refactor(iam): consolidate boundary statements under PolicySize cap (#98)
Some checks are pending
Deploy / deploy-management (push) Waiting to run
Deploy / deploy-external-dev (push) Waiting to run
Deploy / deploy-security (push) Waiting to run
Deploy / deploy-dev (push) Waiting to run
Deploy / deploy-prod (push) Waiting to run

Merge workload secret/DDB/S3 SIDs and trim meal-order extras so the
shared lambda execution boundary fits under the 6144-character limit.
This commit is contained in:
Adam Moussa 2026-08-07 19:54:28 -04:00 • committed by GitHub
parent 44b672ae9a
commit 3605215a28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -121,26 +121,23 @@ Description: >-
# correctly left untouched.
#
# SIZE BUDGET: an attached managed policy document is capped at 6,144 characters
# (whitespace excluded). LambdaExecutionBoundary measures 691 characters across
# 4 statements as of 2026-07-31 — the fleet-wide floor only. Measure before
# widening — len(json.dumps(doc,separators=(',',':'))) on the synthesized
# PolicyDocument with ${AWS::AccountId} resolved, and UPDATE THESE TWO NUMBERS in
# the same edit (they went stale twice inside this branch alone).
# (whitespace excluded). LambdaExecutionBoundary measures 5903 characters across
# 16 statements as of 2026-08-07 (PLAT-93 consolidation after the meal-order
# PolicySize rollback). Measure before widening — len(json.dumps(doc,
# separators=(',',':'))) on the synthesized PolicyDocument with
# ${AWS::AccountId} resolved, and UPDATE THESE TWO NUMBERS in the same edit.
#
# Headroom is 5,453 characters, roughly TWELVE workloads at ~450 each. That is a
# deliberate outcome, not luck: an earlier revision of this branch pre-loaded
# per-workload prefixes for all five mgmt SAM stacks and reached 5,457 characters
# with 687 left — about one workload of room — before any stack had actually
# migrated. Deferring per-workload scope to each migration PR (see the note on
# the boundary itself) removed that pressure entirely. If the budget tightens
# again as workloads land, the end-state fix is per-workload boundaries
# Headroom is 241 characters. Statement consolidation (shared WorkloadSecrets /
# WorkloadDynamoDB / extended WorkloadS3 Resource lists; no new action wildcards)
# is the only remaining lever short of per-workload boundaries. If the budget
# tightens again, the end-state fix is per-workload boundaries
# (seahaven-lambda-execution-boundary-<workload>), which also resolves the
# shared-ceiling residual — tracked as INFRA-187, do not improvise it.
# shared-ceiling residual — tracked as PLAT-52 / INFRA-187, do not improvise it.
# CRITICAL: unlike the 2026-07-27 inline-limit incident,
# there is NO restructure available when this cap is reached — a role has exactly
# ONE permissions boundary, so statements cannot be spilled into a second attached
# managed policy. At the cap the only levers are prefix consolidation and dropping
# unused actions.
# unused actions. Do not introduce dynamodb:* / s3:* / ses:Send* to reclaim space.
#
# This template is deployed via lib/deploy-substrate-stack.ts
# (cloudformation-include) as stack seahaven-deploy-substrate, once per member
@ -492,71 +489,38 @@ Resources:
- ec2:DescribeVpcs
Resource: "*"
# ── afi-backup-monitor (PLAT-56) — prod-only exact secret ARNs ───────
# Derived from live stack parameters + secrets created in seahaven-prod
# 2026-08-05. No secret:afi-* patterns. Omitted in seahaven-dev via
# IsProdAccount (same template deploys to both accounts).
# ── Shared workload data-plane (PLAT-93 PolicySize consolidation) ───
# Per-workload secret/DDB/S3 SIDs were merged so meal-order-manager
# (PLAT-70) can fit under the 6,144-character managed-policy cap
# without introducing new action wildcards. Exact secret ARNs only.
# End-state isolation remains PLAT-52 / INFRA-187.
#
# WorkloadSecrets covers: afi-backup-monitor (PLAT-56),
# front-integrations (PLAT-72), procurement-ingest (PLAT-86),
# meal-order-manager (PLAT-70).
- !If
- IsProdAccount
- Sid: AfiBackupMonitorSecrets
- Sid: WorkloadSecrets
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afi-api-key-w0E02a
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afi-slack-webhook-T4oR3G
- !Ref AWS::NoValue
# ── front-integrations (PLAT-72) — prod-only exact secret ARNs + DDB ─
# Derived from live SAM template + secrets created in seahaven-prod
# 2026-08-05. No secret:front-integrations/* patterns.
- !If
- IsProdAccount
- Sid: FrontIntegrationsSecrets
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/front-api-token-UXKv0U
- arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/slack-bot-token-giGfA7
- arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/google-service-account-dqv3Bo
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: FrontIntegrationsDynamoDB
Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:Query
- dynamodb:Scan
- dynamodb:BatchGetItem
- dynamodb:BatchWriteItem
- dynamodb:DescribeTable
- dynamodb:ConditionCheckItem
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts/index/*"
- !Ref AWS::NoValue
# ── procurement-ingest (PLAT-86) — prod-only exact secrets + data plane ─
# Derived from live CDK stacks in seahaven-prod 2026-08-06. Exact secret
# ARNs only (no secret:procurement-* / workorder-ingest/* wildcards).
- !If
- IsProdAccount
- Sid: ProcurementIngestSecrets
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:procurement-ingest/web-ui-auth-token-ApAMmr
- arn:aws:secretsmanager:us-east-1:011934824531:secret:workorder-ingest/shoc-webhook-hmac-puYTcB
- arn:aws:secretsmanager:us-east-1:011934824531:secret:meal-order-manager/slack-bot-token-ZGmSGw
- !Ref AWS::NoValue
# WorkloadDynamoDB: enumerated union of front-integrations CRUD +
# procurement-ingest CRUD/stream actions. Meal-order table ARNs appended.
# Stream actions on non-stream tables are inert at the ceiling.
- !If
- IsProdAccount
- Sid: ProcurementIngestDynamoDB
- Sid: WorkloadDynamoDB
Effect: Allow
Action:
- dynamodb:GetItem
@ -576,6 +540,8 @@ Resources:
# it is a silent no-op. Runtime stream consumers use the
# stream ARN via DescribeStream/GetRecords above.
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts/index/*"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/purchase-orders"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/purchase-orders/*"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/verified-sites"
@ -590,10 +556,16 @@ Resources:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-orders/*"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-order-comments"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-order-comments/*"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders/index/*"
- !Ref AWS::NoValue
# ── procurement-ingest remaining data plane + meal-order S3 (PLAT-86/70) ─
# WorkloadS3 keeps the prior ProcurementIngestS3 action list and appends
# meal-order form/reports bucket ARNs (same object CRUD shape).
- !If
- IsProdAccount
- Sid: ProcurementIngestS3
- Sid: WorkloadS3
Effect: Allow
Action:
- s3:GetObject*
@ -607,6 +579,10 @@ Resources:
- !Sub "arn:aws:s3:::po-ingest-emails-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::workorder-ingest-emails-${AWS::AccountId}"
- !Sub "arn:aws:s3:::workorder-ingest-emails-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}/*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
@ -651,6 +627,8 @@ Resources:
- arn:aws:bedrock:us-east-2::foundation-model/anthropic.claude-haiku-4-5-20251001-v1:0
- arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-haiku-4-5-20251001-v1:0
- !Ref AWS::NoValue
# site-alerts publish shared by procurement-ingest alarms and
# meal-order-manager (PLAT-70); no separate MealOrder SNS statement.
- !If
- IsProdAccount
- Sid: ProcurementIngestSns
@ -692,61 +670,15 @@ Resources:
- !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/*"
- !Ref AWS::NoValue
# ── meal-order-manager (PLAT-70) — prod-only exact secret ARN + data plane ─
# Derived from meal-order-manager template.yaml + secrets created in
# seahaven-prod 2026-08-07. Exact ARN only (no secret:meal-order-* patterns).
- !If
- IsProdAccount
- Sid: MealOrderManagerSecrets
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:meal-order-manager/slack-bot-token-ZGmSGw
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerDynamoDB
Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:Query
- dynamodb:Scan
- dynamodb:BatchGetItem
- dynamodb:BatchWriteItem
- dynamodb:DescribeTable
- dynamodb:ConditionCheckItem
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders/index/*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerS3
Effect: Allow
Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
- s3:PutObject*
- s3:DeleteObject*
- s3:AbortMultipartUpload
Resource:
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}/*"
- !Ref AWS::NoValue
# ── meal-order-manager (PLAT-70) — statements not covered above ─────
# Secrets → WorkloadSecrets; DynamoDB → WorkloadDynamoDB; S3 → WorkloadS3;
# SNS → ProcurementIngestSns. Trimmed to identity-policy needs.
- !If
- IsProdAccount
- Sid: MealOrderManagerSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/meal-order-manager/*"
- !Ref AWS::NoValue
@ -765,26 +697,17 @@ Resources:
Effect: Allow
Action:
- ses:SendRawEmail
- ses:SendEmail
Resource: "*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerSns
Effect: Allow
Action:
- sns:Publish
Resource:
- !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts"
- !Ref AWS::NoValue
# ── FURTHER PER-WORKLOAD DATA-PLANE STATEMENTS ──────────────────────
# Floor above + afi-backup-monitor (PLAT-56) + front-integrations
# (PLAT-72) + procurement-ingest (PLAT-86) + seahaven-site (PLAT-91)
# + meal-order-manager (PLAT-70).
# Additional stacks add their own statements here, derived from THEIR
# OWN template, in their own PR, deployed to UPDATE_COMPLETE before
# first workload deploy. WIDENING PATH in the header still governs.
# Floor above + consolidated WorkloadSecrets/DynamoDB/S3 (PLAT-93) +
# procurement remainder + seahaven-site (PLAT-91) + meal-order extras
# (PLAT-70). Prefer extending existing Resource lists over new SIDs.
# Additional stacks add statements here, derived from THEIR OWN
# template, in their own PR, deployed to UPDATE_COMPLETE before first
# workload deploy. Measure SIZE BUDGET before merging. WIDENING PATH
# in the header still governs.
#
# WHY the floor stayed empty of data-plane (decided 2026-07-30, Adam):
# The security win of INFRA-186 comes from DELETION, not enumeration.
@ -795,10 +718,10 @@ Resources:
#
# The end-state fix for the shared-ceiling residual (one boundary =
# every SAM/Terraform workload reaches every other's data plane once
# they land) is per-workload boundaries — tracked as INFRA-187. Do not
# improvise it: both guardrail policies pin ONE literal boundary ARN
# inside StringEquals conditions, and loosening that to a wildcard
# weakens the gate.
# they land) is per-workload boundaries — tracked as PLAT-52 /
# INFRA-187. Do not improvise it: both guardrail policies pin ONE
# literal boundary ARN inside StringEquals conditions, and loosening
# that to a wildcard weakens the gate.
# ---------------------------------------------------------------------------
# Shared CloudFormation execution role (SAM stacks) — INFRA-97 scoped
#