Skip to content

sec(iac): Lambda Function URL auth hardening -- CORS wildcard rejection + AWS_IAM OAC scaffolding (#390, #424)#574

Open
cristim wants to merge 3 commits into
feat/multicloud-web-frontendfrom
sec/lambda-cors-auth-hardening
Open

sec(iac): Lambda Function URL auth hardening -- CORS wildcard rejection + AWS_IAM OAC scaffolding (#390, #424)#574
cristim wants to merge 3 commits into
feat/multicloud-web-frontendfrom
sec/lambda-cors-auth-hardening

Conversation

@cristim
Copy link
Copy Markdown
Member

@cristim cristim commented May 20, 2026

Summary

  • sec(auth): CORS wildcard origin + credentials=true on staging Lambda URL #390 (CORS): Reject wildcard allowed_origins via module validation; replace ["*"] in all envs with explicit origins. The Function URL uses allow_credentials = true -- a wildcard origin reflects any inbound Origin header with credentials allowed, enabling any-origin CSRF.
  • sec(iac): production Lambda Function URL uses authorization_type=NONE — no IAM gate #424 (IAM auth): Switch function_url_auth_type module default from "NONE" to "AWS_IAM". Add aws_cloudfront_origin_access_control (type=lambda, sigv4, always-sign) to the frontend module. Add a standalone aws_lambda_permission at the environment layer (breaks the cycle: Lambda URL is CloudFront's origin; distribution ARN feeds the permission -- they can't both live inside a single module). All three env tfvars keep "NONE" with a TODO(#424) cut-over comment until enable_cdn = true is set and CloudFront is deployed.

The frontend client.ts was already OAC-ready: it uses X-Authorization (preserves the Authorization header for CloudFront SigV4 signing) and addContentHashHeader (x-amz-content-sha256 required for POST/PUT/PATCH/DELETE through OAC).

Files changed

File Change
terraform/modules/compute/aws/lambda/variables.tf Default function_url_auth_type to "AWS_IAM"; add validation; expand description
terraform/modules/frontend/aws/main.tf Add aws_cloudfront_origin_access_control.lambda; wire origin_access_control_id
terraform/modules/frontend/aws/variables.tf Add enable_oac variable
terraform/environments/aws/compute.tf Add standalone aws_lambda_permission.function_url_cloudfront (cycle-free)
terraform/environments/aws/frontend.tf Set enable_oac when platform=lambda + auth_type=AWS_IAM
terraform/environments/aws/github-{dev,staging,prod}.tfvars Add TODO(#424) cut-over comments; no functional change to values

Test plan

  • terraform fmt -check passes on all changed modules and environments (verified pre-commit hook)
  • terraform validate passes (pre-commit hook)
  • terraform plan with allowed_origins = ["*"] fails with the validation error (regression test from sec(auth): CORS wildcard origin + credentials=true on staging Lambda URL #390)
  • terraform plan with enable_cdn = true + lambda_function_url_auth_type = "AWS_IAM" shows aws_cloudfront_origin_access_control.lambda + aws_lambda_permission.function_url_cloudfront being created
  • Manual verification of the IAM auth path requires deploying CloudFront -- tracked in the follow-up issue

Manual verification note

The runtime cut-over (flipping deployed envs to AWS_IAM) requires a follow-up deploy. See the TODO(#424) comments in each tfvar for the 3-step procedure. A follow-up issue will be filed to track this.

Closes

Closes #390
Partially addresses #424 (infrastructure scaffolding complete; runtime cut-over tracked separately)

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

Warning

Review limit reached

@cristim, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 2 reviews/hour. Refill in 25 minutes and 37 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e23a2a62-6618-43ac-bbf8-a816a1df2564

📥 Commits

Reviewing files that changed from the base of the PR and between 9ecab71 and 6e8d69d.

📒 Files selected for processing (13)
  • terraform/environments/aws/compute.tf
  • terraform/environments/aws/dev.tfvars.example
  • terraform/environments/aws/frontend.tf
  • terraform/environments/aws/github-dev.tfvars
  • terraform/environments/aws/github-prod.tfvars
  • terraform/environments/aws/github-staging.tfvars
  • terraform/environments/aws/variables.tf
  • terraform/modules/compute/aws/lambda/main.tf
  • terraform/modules/compute/aws/lambda/variables.tf
  • terraform/modules/frontend/aws/main.tf
  • terraform/modules/frontend/aws/variables.tf
  • terraform/profiles/aws/dev.tfvars.example
  • terraform/profiles/aws/prod.tfvars.example
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sec/lambda-cors-auth-hardening

Comment @coderabbitai help to get the list of available commands and usage tips.

@cristim cristim added priority/p1 Next up; this sprint severity/high Significant harm urgency/this-sprint Within the current sprint impact/all-users Affects every user effort/m Days type/security Security finding triaged Item has been triaged labels May 20, 2026
@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

cristim added 2 commits May 21, 2026 01:24
…424)

Switch the lambda module's function_url_auth_type default from "NONE" to
"AWS_IAM" and add the full infrastructure needed to enforce IAM-gated access
via CloudFront OAC:

- lambda module: new aws_lambda_permission.function_url_cloudfront resource
  grants cloudfront.amazonaws.com lambda:InvokeFunctionUrl scoped to a specific
  distribution ARN (source_arn). Created only when auth_type = "AWS_IAM" and
  cloudfront_distribution_arn is provided; no-op otherwise.
- lambda module: new cloudfront_distribution_arn variable (default "") and
  updated function_url_auth_type description + validation block.
- frontend module: new aws_cloudfront_origin_access_control.lambda resource
  (type=lambda, sigv4, always-sign) created when enable_oac = true. OAC id
  wired into the origin block alongside the existing custom_origin_config.
- frontend module: new enable_oac variable (bool, default false).
- environments/aws/compute.tf: pass cloudfront_distribution_arn to lambda module
  (length() guard prevents plan errors when enable_cdn = false).
- environments/aws/frontend.tf: set enable_oac when platform = lambda + AWS_IAM.
- All three env tfvars: keep lambda_function_url_auth_type = "NONE" (unchanged)
  and add a TODO(#424) comment with the 3-step cut-over instructions.

The deployed environments retain their current "NONE" value until CloudFront is
enabled per the follow-up issue. Flipping to AWS_IAM without a CloudFront OAC
in place would make the Function URL return HTTP 403 on all requests.

The frontend client.ts already uses X-Authorization (preserves the Authorization
header for CloudFront OAC SigV4) and addContentHashHeader (x-amz-content-sha256
required for POST/PUT/PATCH/DELETE through OAC) -- no frontend changes needed.

Verified: terraform fmt -check passes on all touched modules and the environment.
Manual verification of the auth path required after CloudFront is deployed.
@cristim cristim force-pushed the sec/lambda-cors-auth-hardening branch from ca51ba6 to fc37407 Compare May 20, 2026 23:24
@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…refs #424)

Remove the standalone lambda_function_url_auth_type variable and derive its
value via a local in compute.tf:

  local.lambda_function_url_auth_type = var.enable_cdn ? "AWS_IAM" : "NONE"

The two valid combinations were the only sensible ones anyway:

  enable_cdn = false -> auth_type = "NONE"
    SPA hits the Function URL directly; browsers cannot SigV4-sign, so the
    URL must be unauthenticated at the AWS-IAM layer; auth is enforced at
    the application layer (login session, CSRF, API keys).

  enable_cdn = true  -> auth_type = "AWS_IAM"
    CloudFront fronts the URL with an OAC that SigV4-signs every request
    (aws_cloudfront_origin_access_control.lambda in the frontend module).
    The Function URL rejects anything not signed by CloudFront's OAC.

Tying the two flags together prevents the two failure modes the previous
"two variables operator must keep in sync" design exposed:

  (a) enable_cdn = true with auth_type = "NONE" -> public Lambda URL behind
      a pointless CloudFront (security goal of #424 defeated).
  (b) enable_cdn = false with auth_type = "AWS_IAM" -> direct browser hits
      to Function URL return 403 (no way to sign), site is unreachable.

Updated three usages (compute.tf Lambda module call, the standalone
aws_lambda_permission.function_url_cloudfront resource count, and
frontend.tf enable_oac derivation) to use the local / enable_cdn directly.

Removed the lambda_function_url_auth_type lines from all three env tfvars
(github-dev/staging/prod) plus the two profile example tfvars and the
top-level dev.tfvars.example; replaced the TODO(#424) cut-over comments
with a short explanation of the enable_cdn -> auth_type mapping.

Verified: terraform validate clean, terraform fmt clean.

Refs #424
@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/m Days impact/all-users Affects every user priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/security Security finding urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant