QA plan: Secrets and least-privilege IAM (#25)
Story: Secrets and least-privilege IAM, per env (#25)
Capability: AWS infrastructure (#19)
ADRs: RAG009 — authorizer role scoping; RAG001 — per-env isolation
Test scope
Section titled “Test scope”| Type | Coverage |
|---|---|
| IaC lint | tfsec or checkov rule in CI: no wildcard * on Resource in any aws_iam_policy_document except cloudwatch:PutMetricData and xray:PutTraceSegments |
| IaC lint | tfsec rule: no plaintext secret value in Terraform variables, outputs, or *.tfvars |
| Static (plan review) | API Lambda role: scoped to specific Bedrock model ARN, specific cluster ARN, specific secret ARN |
| Static (plan review) | Authorizer Lambda role: scoped to rds-data:ExecuteStatement on cluster ARN + secretsmanager:GetSecretValue on credential secret ARN only |
| Static (plan review) | Secrets Manager secret ARN is referenced in Terraform; secret value never appears in .tf or *.tfvars |
| Static (plan review) | Per-env naming convention followed: secret path includes env prefix (qa/ or prod/); no cross-env secret references |
| Integration (manual, QA) | After apply: aws iam simulate-principal-policy confirms the API Lambda role cannot call s3:GetObject on arbitrary buckets or bedrock:InvokeModel on model IDs not in the allow list |
| Security | OIDC trust policy for GitHub Actions names repo and environment explicitly; no * in the sub claim |
Entry criteria
Section titled “Entry criteria”- #21, #22, #23, #24 deployed to QA (modules exist for roles to reference)
tfsecandcheckovinstalled in CI
Exit criteria
Section titled “Exit criteria”tfsecandcheckovpass with no high-severity findings onmodules/andenvs/- Plan review checklist: no wildcard resources, no plaintext secrets, per-env naming confirmed
iam-policy-simulator(or equivalentterraform planassertion) confirms least-privilege for both Lambda roles- OIDC trust policy check passes in CI
Environments
Section titled “Environments”IaC lint and static: CI. Integration check: manual against QA post-deploy using AWS CLI.
Tooling
Section titled “Tooling”tfsec, checkov, terraform validate, terraform plan, AWS CLI (aws iam simulate-principal-policy).
How tests are written
Section titled “How tests are written”# Given the QA Terraform plan has been applied# When the IAM policy simulator runs against the API Lambda roleaws iam simulate-principal-policy \ --policy-source-arn "$API_LAMBDA_ROLE_ARN" \ --action-names "s3:GetObject" \ --resource-arns "arn:aws:s3:::*" \ | python3 -c "import sys,json; r=json.load(sys.stdin); assert all(e['EvalDecision']=='implicitDeny' for e in r['EvaluationResults']), 'S3 access not denied'"# Then all evaluated decisions are implicitDenytfsec and checkov run as CI steps without Given/When/Then (they are linters, not tests); the IAM simulation above uses the pattern for verification steps.
- A wildcard on
secretsmanager:*that is scoped to a specific secret ARN pattern (e.g.,arn:aws:secretsmanager:*:*:secret:qa/*) may passtfsecbut violate least-privilege intent. PR review must check that the action list, not just the resource, is scoped. - Cross-env secret reference is easiest to introduce in shared module outputs; enforce via
checkovrule and reviewer checklist.
Capability acceptance criteria link
Section titled “Capability acceptance criteria link”RAG009 Consequences: authorizer Lambda role scoped to cluster ARN and credential secret ARN only. Security per-feature requirements (#19–#25): no wildcards, no plaintext secrets, per-env naming. Exit criteria map to each row.
© 2026 Benjamin Arunski