Repository navigation
fix(iac/aws): list every attached policy in the ci-cd-permissions policy_arns output - #1211
Conversation
…icy_arns output The policy_arns output says it holds the ARNs of all managed policies attached to the deploy role, but it was a hand-maintained map that only listed networking, compute, data and monitoring. compute_b and iam, which role.tf also attaches, were missing, so an audit or teardown driven by `terraform output -json policy_arns` skipped the two policies that carry the boundary-delegation half of the role. Add compute_b and iam, and add outputs_guard_test.go, which re-derives the aws_iam_role_policy_attachment set from this root's *.tf files and requires policy_arns to match it exactly (same keys, same policy ARN expressions, nothing extra), so the two can't drift again. Document the output in the README. Closes LeanerCloud#309 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 3 billable files and costs up to $0.75. Or wait 59 minutes for your next included review. View limit details
Comment |
|
Independent review 2 (claude-fleet, cold): APPROVE 2e5acc8 Findings (none blocking):
Against #309:
Blast radius:
Reuse and scope:
Drift:
Attacked:
Verified locally, with GOTOOLCHAIN=go1.26.10 and no cloud credentials, under the heavy lock:
Reviewed by an independent agent with no author context (second reviewer). Review only: nothing was pushed, and the GitHub review state is unchanged. |
|
VERDICT: APPROVE 2e5acc8 Independent adversarial review of #1211 at 2e5acc8 (single commit on origin/main). The fix closes #309: on this head, role.tf:151-179 declares six aws_iam_role_policy_attachment resources (networking, compute, compute_b, data, iam, monitoring) and outputs.tf:17-24 now lists exactly those six, each pointing at the same aws_iam_policy..arn the attachment uses. Leaving workload_boundary out is correct. It is referenced only as the iam:PermissionsBoundary condition value (policy_iam.tf:108), and no attachment puts it on the deploy role. Findings (none blocking):
Blast radius: Attacked:
Not verified here: no |
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: APPROVE Blocking:
Non-blocking:
Attacked:
Body-stale:
Reviewer: rA2. Bar while the maintainers are away (set by IAmJSD as acting owner): one approving independent review on the head, two for money, auth and security paths. |
|
Local verification at |
|
Merged by IAmJSD's agent at IAmJSD's request (IAmJSD is acting owner while the maintainers are away), under the bar IAmJSD set: 2 approving independent review(s) on |
Root cause
terraform/environments/aws/ci-cd-permissions/outputs.tfbuildspolicy_arnsby hand. Its description says it holds the ARNs of all attached managed policies, but nothing tied it to the attachments inrole.tf. #1094 later addedmonitoringto both, butcompute_bandiamwere never listed. On current main,role.tfattaches six policies (networking,compute,compute_b,data,iam,monitoring) and the output lists four. The issue says "three of five" because it was filed beforemonitoringwas added.Anyone who reviews or detaches the role's policies from
terraform output -json policy_arnsmissescompute_bandiam, which are the policies that carry the #1705 boundary-delegation half of the role.Fix
outputs.tf: addcompute_bandiam, so the map has one entry peraws_iam_role_policy_attachment, keyed by attachment name. The description now says which role and how the map is keyed. A comment explains whyworkload_boundaryis left out: it is the permissions boundary for roles the deploy role creates, not a policy attached to the deploy role.outputs_guard_test.go(new, samecicdpermissions_testpackage and helpers aspolicy_guard_test.go): reads everyaws_iam_role_policy_attachmentfrom this root's*.tffiles, comment lines stripped, and requirespolicy_arnsto match exactly in both directions: every attachment has an entry with the same key and the samepolicy_arnexpression, and every entry matches an attachment. It fails closed if the scan finds fewer than 6 attachments, or if an attachment or map entry has a shape it can't read.README.md: addterraform output -json policy_arnsto the outputs section. I found no workflow, script or doc that readspolicy_arns(git grep policy_arnsonly matches the audit report andoutputs.tf), so nothing downstream changes.Why not derive the map from the attachments? Terraform can't list every resource of one type. The only real derivation would merge the six attachments into one
for_eachresource, which changes their state addresses (aws_iam_role_policy_attachment.networkingwould become...deploy["networking"]) in a root that a human applies by hand. That needsmovedblocks and a carefully reviewed apply, which is too much for a p3/xs output fix. The list stays explicit, and the Go guard does the drift check instead.The new check is a Go test rather than a
terraform test. Adding atests/dir would turn on the "Test CI/CD trust" CI step for AWS (init+testwith mocked providers) and widen this change. The Go test runs in the existing unit-test job with no provider download, and.terraform.lock.hclis untouched.Closes #309
Verification
All local on macOS (darwin_arm64). This is static source evidence plus
terraform validate. Nothing ranplan/applyand no cloud credentials were used (AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/nullthroughout). Gap: I did not compare the output against a real applied state (terraform output -json policy_arnsagainst the live root).Fail before (test added,
outputs.tfas on main):Pass after:
go test -count=1 ./terraform/environments/aws/ci-cd-permissions/passes (the whole package, existing guards included).Mutation probes, applied one at a time to the fixed tree and then reverted. Each one fails the test with a specific message:
iamentry"iam"entryworkload_boundary = aws_iam_policy.workload_boundary.arncompute_b = aws_iam_policy.compute.arn(wrong value)aws_iam_role_policy_attachment.extrainrole.tfwith no output entry"extra"entrycomputeentry"compute"entrydatatodbOther checks:
terraform fmt -check -recursivein the module dir: cleanTF_DATA_DIR=$(mktemp -d)/tfdata terraform init -backend=false -lockfile=readonly -input=falsethenterraform validate: "Success! The configuration is valid." Lock file unchanged.go vet ./terraform/environments/aws/ci-cd-permissions/: cleangolangci-lint run --allow-parallel-runners ./terraform/environments/aws/ci-cd-permissions/...(v2.10.1): 0 issuesgocyclo -over 10 -ignore "_test\.go"on the package: silent (the new test's helpers are under 10 too)go test -count=1 ./internal/mocks/: okbash scripts/gosec-hook.sh <new test file>: clean--register-awsplusscripts/setup-git-secrets.shand.gitallowed), run in a freshgit initrepo with an isolated HOME, over the three changed files: clean.scripts/test-git-secrets-allowlist.sh: 42 PASS, 0 FAILmarkdownlintv0.47.0 on the README: cleanOut of scope
for_eachresource withmovedblocks (see above). This could be a follow-up if the maintainer wants the output to be derived structurally.workload_boundary(the issue mentions it as optional). It isn't attached to the deploy role, so it doesn't belong in a map described as attached policies. If an inventory output for it is wanted, it should be its own output.Labels
Could a maintainer mirror #309's triage labels onto this PR:
impact/internal,triaged,priority/p3,severity/low,urgency/eventually,effort/xs,type/bug.🤖 Generated with Claude Code