Fix boundary gaps found in GPT-4.1 cross-review

Three issues from the mandatory IAM cross-review (BLOCK/FIX):

1. Add KMS statement — PaymentsDashboard DynamoDB table and
   payments-dashboard CloudWatch log groups use CMKs. Without
   kms:Decrypt + kms:GenerateDataKey in the boundary, those Lambda
   calls fail at the KMS layer at runtime. Scoped to account keys only.

2. Add table/*/index/* to DynamoDB resource — dynamodb:Query on a GSI
   requires the index ARN; covering only table/* silently denied GSI
   queries at the boundary.

3. Fix EC2 ENI statement — remove AssignPrivateIpAddresses /
   UnassignPrivateIpAddresses (EFA-only, not part of Lambda ENI
   lifecycle); add DescribeSubnets + DescribeSecurityGroups + DescribeVpcs
   which are required by the Lambda service during VPC attachment and are
   present in AWSLambdaVPCAccessExecutionRole.

4. Add SES configuration-set/* resource — ses:SendRawEmail requires
   permission on the configuration set if one is passed at send time.

Refs: INFRA-103
This commit is contained in:
Adam Moussa 2026-06-10 13:58:13 -04:00
parent daddff57b5
commit 67e25f53d5

View file

@ -118,19 +118,23 @@ Resources:
Resource: "*"
# ── VPC / ENI management (payments-dashboard VPC functions) ────────
# ec2:Describe* are required even for non-VPC Lambdas as the ENI
# lifecycle actions need them.
# Matches AWSLambdaVPCAccessExecutionRole exactly.
# AssignPrivateIpAddresses / UnassignPrivateIpAddresses are for EFA
# and secondary IPs — not part of the Lambda ENI lifecycle — omitted.
- Sid: Ec2Eni
Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
- ec2:AssignPrivateIpAddresses
- ec2:UnassignPrivateIpAddresses
- ec2:DescribeSubnets
- ec2:DescribeSecurityGroups
- ec2:DescribeVpcs
Resource: "*"
# ── DynamoDB (afterhours, payments, meal-order, front-integrations) ─
# Table/* covers base-table operations; table/*/index/* is required for
# Query/Scan on Global Secondary Indexes.
- Sid: DynamoDB
Effect: Allow
Action:
@ -146,6 +150,7 @@ Resources:
- dynamodb:ConditionCheckItem
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/*"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/*/index/*"
# ── S3 (payments-dashboard read, meal-order-manager CRUD) ──────────
- Sid: S3
@ -216,6 +221,25 @@ Resources:
- ses:SendRawEmail
Resource:
- !Sub "arn:aws:ses:us-east-1:${AWS::AccountId}:identity/*"
- !Sub "arn:aws:ses:us-east-1:${AWS::AccountId}:configuration-set/*"
# ── KMS (CMK-encrypted resources) ─────────────────────────────────
# Required for Lambda functions that read/write CMK-encrypted AWS
# resources. Verified live state:
# - PaymentsDashboard DynamoDB table: CMK key/0b660af3 (KMS:ENABLED)
# - payments-dashboard CloudWatch log groups: CMK key/b748750c
# Secrets Manager + SQS queues in these stacks use AWS-managed keys
# (aws/secretsmanager, aws/sqs) which do not require explicit kms:*
# actions in the execution role policy. The CMK keys are scoped to
# this account to prevent cross-account KMS calls.
- Sid: KMS
Effect: Allow
Action:
- kms:Decrypt
- kms:GenerateDataKey
- kms:DescribeKey
Resource:
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:key/*"
# ---------------------------------------------------------------------------
# Shared CloudFormation execution role (SAM stacks)