From 496e56887296fd367ba6c7b1f69a5a3416d0613a Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Mon, 28 Sep 2026 15:38:18 -0400 Subject: [PATCH] fix(iam): trust only this account's deploy environment (PLAT-79) --- terraform/iam_github_deploy.tf | 11 +++++------ terraform/locals.tf | 16 +++++++++++----- tests/infra/hcpContract.test.js | 11 ++++++++--- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/terraform/iam_github_deploy.tf b/terraform/iam_github_deploy.tf index e37695f..b60cafa 100644 --- a/terraform/iam_github_deploy.tf +++ b/terraform/iam_github_deploy.tf @@ -1,8 +1,9 @@ # GitHub Actions OIDC role for the thin deploy.yaml caller of # org reusable cd-hcp-lambda.yaml. # -# One role: GitHub Environments have a single DEPLOY_ROLE_ARN. Trust is pinned -# to Environments dev and prod. job_workflow_ref matches the reusable at any ref. +# One role per account: GitHub Environments have a single DEPLOY_ROLE_ARN. +# The prod role trusts environment:prod only. The dev role trusts environment:dev only. +# job_workflow_ref is StringEquals on the reusable SHA pinned by deploy.yaml. # AWS STS GitHub condition keys include job_workflow_ref, not workflow_ref. # # Not a Lambda execution role: no permissions_boundary. Path /tf-managed/ so @@ -33,11 +34,9 @@ data "aws_iam_policy_document" "github_deploy_assume" { } condition { - test = "StringLike" + test = "StringEquals" variable = "token.actions.githubusercontent.com:job_workflow_ref" - values = [ - "Sea-Haven-Industries/.github/.github/workflows/cd-hcp-lambda.yaml@*", - ] + values = [local.github_deploy_workflow_ref] } } } diff --git a/terraform/locals.tf b/terraform/locals.tf index a57be8f..c97d247 100644 --- a/terraform/locals.tf +++ b/terraform/locals.tf @@ -21,13 +21,19 @@ locals { dynamodb_cmk_ssm = "/seahaven/dynamodb/cmk-arn" github_oidc_provider_arn = "arn:aws:iam::${local.account_id}:oidc-provider/token.actions.githubusercontent.com" - # Org has Actions OIDC use_immutable_subject=true. Both forms, both environments. - github_oidc_subs = [ - "repo:${var.github_repo}:environment:dev", + # Repo OIDC subject customization is the default (use_default=true), so tokens + # use the classic repo:owner/name:environment: form. Each account's role + # trusts only its own Environment. The prod role must not trust dev. + github_oidc_subs_prod = [ "repo:${var.github_repo}:environment:prod", - "repo:Sea-Haven-Industries@183236204/payments-dashboard@1206210946:environment:dev", - "repo:Sea-Haven-Industries@183236204/payments-dashboard@1206210946:environment:prod", ] + github_oidc_subs_dev = [ + "repo:${var.github_repo}:environment:dev", + ] + github_oidc_subs = local.is_prod ? local.github_oidc_subs_prod : local.github_oidc_subs_dev + # Matches the SHA pin in .github/workflows/deploy.yaml. A reusable bump + # updates both together. StringEquals, not @*. + github_deploy_workflow_ref = "Sea-Haven-Industries/.github/.github/workflows/cd-hcp-lambda.yaml@ee5b843ca105422b679c3fdeb9eaa68c6e500a85" dynamodb_cmk_arns = { prod = "arn:aws:kms:us-east-1:011934824531:key/be5fa4cb-c546-40fe-a13d-c7bec79f5d12" diff --git a/tests/infra/hcpContract.test.js b/tests/infra/hcpContract.test.js index 81891b8..242f178 100644 --- a/tests/infra/hcpContract.test.js +++ b/tests/infra/hcpContract.test.js @@ -88,10 +88,15 @@ describe("HCP Terraform seam (PLAT-79)", () => { }); it("pins GitHub deploy trust to the Lambda reusable", () => { - assert.match(locals, /environment:dev/); - assert.match(locals, /environment:prod/); + const prodSubs = locals.split("github_oidc_subs_prod")[1].split("github_oidc_subs_dev")[0]; + assert.match(prodSubs, /environment:prod/); + assert.doesNotMatch(prodSubs, /environment:dev/); assert.match(githubDeploy, /github_oidc_subs/); - assert.match(githubDeploy, /cd-hcp-lambda\.yaml@\*/); + assert.match(githubDeploy, /job_workflow_ref/); + assert.match(locals, /cd-hcp-lambda\.yaml@ee5b843ca105422b679c3fdeb9eaa68c6e500a85/); + assert.match(deploy, /cd-hcp-lambda\.yaml@ee5b843ca105422b679c3fdeb9eaa68c6e500a85/); + assert.doesNotMatch(githubDeploy, /cd-hcp-lambda\.yaml@\*/); + assert.doesNotMatch(locals, /cd-hcp-lambda\.yaml@\*/); assert.doesNotMatch(githubDeploy, /deploy\.yaml@refs\/heads/); assert.doesNotMatch(variables, /github_deploy_branch/); });