Skip to content

feat(auth): implement opt-in SigV4 cryptographic verification#390

Merged
vieiralucas merged 1 commit intomainfrom
worktree-sigv4-iam-batch3-verify
Apr 14, 2026
Merged

feat(auth): implement opt-in SigV4 cryptographic verification#390
vieiralucas merged 1 commit intomainfrom
worktree-sigv4-iam-batch3-verify

Conversation

@vieiralucas
Copy link
Copy Markdown
Member

@vieiralucas vieiralucas commented Apr 14, 2026

Summary

Batch 3 of 9 for the opt-in SigV4 + IAM enforcement work. Ships the real SigV4 verifier behind the --verify-sigv4 / FAKECLOUD_VERIFY_SIGV4 flag plumbed in batch 1. Off by default — existing workflows see zero behavior change.

What's verified

When enabled, every incoming request is checked cryptographically before reaching the service handler:

  • Canonical request reconstruction following the AWS SigV4 spec:
    • Double-encoded path for non-S3, single-encoded for S3
    • Sorted query string, dropping X-Amz-Signature for presigned URLs
    • Lowercased + sorted headers, whitespace-collapsed values
    • Payload hash from X-Amz-Content-Sha256 when present (supports S3's UNSIGNED-PAYLOAD + STREAMING-* pass-through), otherwise sha256(body)
  • Signing key derivation via the four-step AWS4 -> date -> region -> service -> aws4_request HMAC chain
  • Constant-time comparison via subtle::ConstantTimeEq
  • ±15-minute clock-skew window matching AWS

Error mapping

Verification failures are returned as protocol-correct AWS errors before business logic runs:

Failure AWS error
Wrong signature SignatureDoesNotMatch
Unknown access key InvalidClientTokenId
Clock skew > 15 min RequestTimeTooSkewed
Malformed auth header / missing signed header IncompleteSignature

Root bypass

The reserved test* AKID prefix (shipped in batch 1) short-circuits verification so test / test credentials always work — community convention matching LocalStack and Floci. A startup tracing::warn! already fires when the flag is on to prevent false-positive "my policies work" results from unsigned test clients.

Architecture

  • New CredentialResolver trait in fakecloud_core::auth and concrete IamCredentialResolver in fakecloud_iam — the dependency edge stays core → iam.
  • DispatchConfig gains credential_resolver: Option<Arc<dyn CredentialResolver>>.
  • ResolvedCredential.account_id is sourced from the credential itself, not from global config, priming the shape Feature proposal: multi-account isolation within a single fakecloud instance #381 (multi-account isolation) will need.

Decisions

  • Hand-rolled SigV4 rather than using the aws-sigv4 crate. The AWS crate pulls in aws-smithy-runtime-api + aws-credential-types transitively, which is a heavy dependency for a ~400-line well-specified algorithm. The round-trip E2E test below signs requests via the real aws-sdk-rust and verifies them through our implementation — that's the ground-truth correctness check.
  • X-Amz-Content-Sha256 is trusted pass-through. S3's UNSIGNED-PAYLOAD / STREAMING-* markers are part of the signature, so echoing them into the canonical request matches what the client signed.
  • Hardcoded secret constants in tests use obvious sentinels (testtesttest..., fakefakefake...) to avoid GitHub's push-protection secret scanning.

Test plan

  • cargo test -p fakecloud-aws sigv4:: — 12 unit tests (parse header, parse presigned, canonical URI S3 vs non-S3, canonical query, signing key determinism, clock skew, round-trip verification, tampered body rejection)
  • cargo test -p fakecloud-iam credential_resolver — 2 unit tests for the IAM state adapter
  • cargo test -p fakecloud-core — 43 tests, no regressions
  • cargo test -p fakecloud-e2e --test sigv4_verification6 end-to-end tests that spawn a real fakecloud with FAKECLOUD_VERIFY_SIGV4=true, drive signed requests through aws-sdk-rust, and cover:
    • valid signed request from a newly-created IAM user
    • unknown AKID → InvalidClientTokenId
    • wrong secret → SignatureDoesNotMatch
    • root bypass (test/test) always accepted
    • off-by-default regression guard
    • STS temp credentials from AssumeRole (batch 2) verify successfully
  • cargo test -p fakecloud-e2e --test iam — 57 tests, no regressions
  • cargo test -p fakecloud-conformance — no regressions
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo fmt --check clean

Summary by cubic

Adds opt-in SigV4 cryptographic verification behind --verify-sigv4 / FAKECLOUD_VERIFY_SIGV4. Off by default; when enabled, bad signatures are rejected with AWS-accurate errors before any handler runs.

  • New Features

    • Real SigV4 verifier in fakecloud-aws (verify, parse_sigv4_header, parse_sigv4_presigned).
    • Canonical request: double-encode path for non-S3, single for S3; sorted query (drops X-Amz-Signature for presigned); lowercased/sorted headers; payload hash trusts X-Amz-Content-Sha256 (supports S3 UNSIGNED-PAYLOAD/STREAMING-*).
    • ±15-minute clock skew check and constant-time signature compare.
    • Error mapping: SignatureDoesNotMatch, InvalidClientTokenId, RequestTimeTooSkewed, IncompleteSignature.
    • CredentialResolver trait in fakecloud-core; IamCredentialResolver in fakecloud-iam. Dispatch resolves the secret and verifies per request. test* AKID root bypass remains.
    • E2E and unit tests cover header/presigned parsing, canonicalization, clock skew, round-trip, tampered body, IAM + STS flows.
  • Migration

    • No changes needed unless you opt in.
    • To enable: run with --verify-sigv4 or set FAKECLOUD_VERIFY_SIGV4=true.
    • Ensure callers use real IAM access keys; test/test still works via the bypass (for local bootstrapping).

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

Adds the real SigV4 verifier behind the --verify-sigv4 flag plumbed in
batch 1. Off by default; when enabled, requests with invalid signatures
are rejected before reaching service handlers with protocol-correct
AWS errors (SignatureDoesNotMatch, InvalidClientTokenId,
RequestTimeTooSkewed, IncompleteSignature).

- New ParsedSigV4 + parse_sigv4_header/parse_sigv4_presigned that
  extract the full credential scope, signed headers, signature, and
  X-Amz-Date from Authorization headers and presigned URL query params.
- New verify() that:
  1. checks clock skew (±15 min, matches AWS)
  2. reconstructs the canonical request (double-encoded path for
     non-S3, single for S3, sorted query string dropping
     X-Amz-Signature for presigned, lowercased+sorted headers)
  3. derives the signing key via the four-step HMAC chain
  4. compares in constant time via subtle::ConstantTimeEq
- Respects X-Amz-Content-Sha256 when present (supports S3's
  UNSIGNED-PAYLOAD + STREAMING-* values pass-through).
- New CredentialResolver trait in fakecloud-core::auth and
  IamCredentialResolver impl in fakecloud-iam, keeping the
  dependency edge pointing core -> iam.
- Dispatch integration: when config.verify_sigv4 is on and the caller
  isn't the test/test root bypass, resolve secret, verify, map errors.
- Bootstrapping via test/test root bypass still works end-to-end so
  `FAKECLOUD_VERIFY_SIGV4=true` is usable from a fresh server.
- Added aws-sigv4-supporting deps: hmac, hex, subtle (chrono +
  percent-encoding + sha2 already workspace-wide).

Tests:
- 12 unit tests in fakecloud-aws::sigv4 covering parse, canonical URI
  (S3 vs non-S3), canonical query, signing-key determinism, clock
  skew, round-trip verification, and tampered-body rejection.
- 2 unit tests in fakecloud-iam::credential_resolver covering the IAM
  state adapter.
- 6 end-to-end tests in sigv4_verification.rs that spawn a real
  fakecloud process with FAKECLOUD_VERIFY_SIGV4=true, drive real
  signed requests through aws-sdk-rust clients, and cover:
    * valid signed request from a newly-created IAM user
    * unknown AKID -> InvalidClientTokenId
    * wrong secret -> SignatureDoesNotMatch
    * root bypass (test/test) always accepted
    * off-by-default regression guard
    * STS temp credentials from AssumeRole verify successfully
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 10 files

@vieiralucas vieiralucas merged commit 2e8137a into main Apr 14, 2026
35 checks passed
@vieiralucas vieiralucas deleted the worktree-sigv4-iam-batch3-verify branch April 14, 2026 20:53
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.

1 participant