Skip to content

fix(apigatewayv2,iot,core,opensearch,lambda): unify tags, trailing-slash route, strict-iam fail-closed, VPC endpoints, ARN partitions (bug-hunt)#2317

Merged
vieiralucas merged 3 commits into
mainfrom
bh2-compat-auth
Jul 17, 2026
Merged

fix(apigatewayv2,iot,core,opensearch,lambda): unify tags, trailing-slash route, strict-iam fail-closed, VPC endpoints, ARN partitions (bug-hunt)#2317
vieiralucas merged 3 commits into
mainfrom
bh2-compat-auth

Conversation

@vieiralucas

@vieiralucas vieiralucas commented Jul 16, 2026

Copy link
Copy Markdown
Member

Summary

Fixes five confirmed client-compatibility / auth-enforcement bugs found during a bug hunt.

  1. apigatewayv2 tag split -> Terraform perpetual drift. CreateApi/CreateStage wrote inline tags onto the resource struct (HttpApi.tags / Stage.tags), while TagResource/UntagResource/GetTags operated on a separate ARN-keyed state.tags store — the two never synced, so create-time tags were invisible to GetTags and TagResource tags invisible to GetApi/GetStage. Unified on state.tags as the single source of truth: creates now also write there, all read paths (GetApi, GetApis, UpdateApi, GetStage, GetStages, UpdateStage) overlay from it, and deletes drop it. The tag verbs now percent-decode the non-greedy {ResourceArn} @httpLabel so the store is keyed by the plain ARN that creates compute. Fixed for both Api and Stage (identical split). DomainName/VpcLink keep create-time tags in a separate JSON blob — a distinct pre-existing gap, noted, out of scope here.

  2. IoT match_route didn't strip a trailing slash. GET /things/ split to ["things",""] and matched the 2-segment DescribeThing with an empty thingName (404) instead of 1-segment ListThings. Now strips a single trailing slash, mirroring the EKS/opensearch/scheduler routers. Affects every IoT collection with an item sibling.

  3. --iam strict fail-open on an unresolvable AKID (auth bypass). Without --verify-sigv4, a signed request whose access key id resolved to no principal (principal=None, access_key_id=Some) skipped both the identity-policy branch and the anonymous branch and fell through to the handler — bypassing enforcement (including an explicit Deny). Strict mode now fails closed with InvalidClientTokenId for an unknown key, guarded to: strict mode only, a configured credential resolver, and the unresolved-principal case. The reserved test* root-bypass credentials are skipped upstream, so the local-dev bootstrap is unaffected; soft mode and the --verify-sigv4 path are untouched.

  4. opensearch VPC domain missing Endpoints.vpc. VPC domains always emitted a public-style top-level Endpoint and never the Endpoints: {"vpc": ...} map Terraform reads. VPC domains (detected via VPCOptions in config) now emit Endpoints.vpc and omit the top-level Endpoint, matching AWS; non-VPC domains keep Endpoint.

  5. Lambda ARN partition parsing (aws-cn / aws-us-gov). ARN-in-path parsing hardcoded arn:aws:lambda:, so China/GovCloud ARNs weren't recognized. A new strip_lambda_arn_prefix helper matches arn:<partition>:lambda: for any partition; used by both normalize_function_name and qualifier_from_function_ref.

Docs: website/content/docs/reference/security.md updated to state that strict IAM now denies unknown access keys even without --verify-sigv4. The other four fixes are correctness round-trips with no new surface.

Test plan

New tests, all passing:

  • apigatewayv2 (e2e, real SDK): test_api_tags_single_source_of_truth_round_trip (create-time tags visible to GetTags; TagResource visible to GetApi; Untag round-trips both read paths) + test_stage_tags_single_source_of_truth_round_trip.
  • IoT (unit): routes_by_method_and_path asserts GET /things/ -> ListThings; thing_create_get_list_delete asserts the trailing-slash GET returns the list.
  • auth (e2e, iam_enforcement.rs): unknown_access_key_denied_in_strict_without_sigv4 (unknown AKID under strict/no-sigv4 -> InvalidClientTokenId) + resolved_principal_still_allowed_in_strict_without_sigv4 (no regression).
  • opensearch (integration): vpc_domain_reports_endpoints_vpc_not_public_endpoint + non_vpc_domain_reports_public_endpoint.
  • lambda (unit): normalize_function_name_strips_non_aws_partition_arns, qualifier_from_non_aws_partition_arn, get_function_accepts_china_partition_arn.

Runs (worktree):

  • cargo test -p fakecloud-iot -p fakecloud-lambda -p fakecloud-opensearch -p fakecloud-apigatewayv2 -p fakecloud-core — green.
  • cargo test -p fakecloud-e2e --test apigatewayv2 single_source — 2 passed.
  • cargo test -p fakecloud-e2e --test iam_enforcement — 52 passed; --test sigv4_verification — 6 passed; boundary/abac/scheduler/session_policy suites — green (auth change touches dispatch).
  • cargo clippy (touched crates + fakecloud-core --all-targets -D warnings, e2e tests) — clean.
  • cargo fmt — applied.

Summary by cubic

Unifies API Gateway v2 tags into a single ARN-keyed source of truth. Also fixes OpenSearch VPC endpoint shape, expands Lambda ARN parsing to all partitions, and reverts the IoT trailing-slash tweak to keep AWS‑conformant validation.

  • Bug Fixes
    • fakecloud-apigatewayv2: Use an ARN-keyed tag store for APIs and stages; percent-decode {ResourceArn} in TagResource/UntagResource/GetTags; overlay tags in Get*/Update*; clear tags on delete.
    • fakecloud-opensearch: VPC domains expose Endpoints.vpc and omit the top-level Endpoint; non-VPC domains keep Endpoint.
    • fakecloud-lambda: Parse Lambda ARNs for any partition (e.g., aws-cn, aws-us-gov) for function refs and qualifiers.
    • fakecloud-iot: Revert the trailing-slash routing change to preserve AWS-conformant validation on empty path params.

Written for commit 1235201. Summary will update on new commits.

Review in cubic

…ash route, strict-iam fail-closed, VPC endpoints, ARN partitions (bug-hunt)
@vieiralucas
vieiralucas requested a review from Copilot July 16, 2026 22:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…mance

The `/things/`->ListThings rewrite made an empty trailing path segment
(e.g. thingName="") stop matching the mutating {thingName} ops, so the
model's negative_too_short_<pathParam> variants got a 404 instead of the
expected validation error — regressing IoT conformance by 112 variants.
The trailing-slash behaviour was an unproven LOW-severity nicety; AWS
conformance (empty path param -> route to the {param} op -> ValidationException)
is the source of truth. The other four batch fixes (apigatewayv2 tags,
strict-IAM fail-closed, opensearch VPC endpoints, Lambda ARN partitions)
are unaffected.
@vieiralucas
vieiralucas requested a review from Copilot July 17, 2026 01:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…v2/anonymous/deny-message paths

The unresolvable-AKID fail-closed branch broke established enforcement
behaviour (sigv2 presigned attribution, anonymous explicit-deny handling,
and the encoded iam deny message) because the guard fired on paths beyond
the intended unknown-signed-key case. The fail-open gap it targeted is a
real but MEDIUM concern; revisit in a dedicated, fully-traced PR. The other
compat fixes in this PR (apigatewayv2 tag unification, opensearch VPC
endpoints, Lambda ARN partitions) are unaffected.
@vieiralucas
vieiralucas requested a review from Copilot July 17, 2026 04:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vieiralucas
vieiralucas merged commit 80188ab into main Jul 17, 2026
156 of 157 checks passed
@vieiralucas
vieiralucas deleted the bh2-compat-auth branch July 17, 2026 06:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants