Merge pull request #64 from Sea-Haven-Industries/docs/substrate-escalation-controls
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

docs(readme): document the deploy substrate's escalation controls
This commit is contained in:
Adam Moussa 2026-07-27 20:13:25 -04:00 • committed by GitHub
commit 5c719710b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -142,6 +142,36 @@ of the substrate — they are provisioned per repo at migration/onboarding time
so an account never carries trust relationships for repos that do not deploy
to it.
**Escalation controls on `github-cfn-execution-role`.** Every `iam:CreateRole`,
`AttachRolePolicy` and `PutRolePolicy` is conditioned on the target carrying
`seahaven-lambda-execution-boundary`. That condition alone is not sufficient,
so the attached `seahaven-cfn-exec-iam-management` managed policy also carries
three explicit Deny statements:
- `DenyBoundaryTampering` — no removing a boundary from any role or user.
Granting the delete under the same `StringEquals` condition self-defeats the
gate, because for a delete the condition key resolves to the boundary already
on the target.
- `DenyBoundaryPolicyEdit` — no rewriting any `seahaven-*` managed policy.
- `DenySelfMutation` — the role cannot modify or delete itself or any
`githubdeploy-*` role. Without it the control is one API call from being
undone: `IAMRoleReadAndDelete` grants `iam:DetachRolePolicy` on `Resource:
"*"` unconditioned, so the role could detach the very policy carrying these
Denies.
Verify a change to these with `aws iam simulate-principal-policy` against the
role's own ARN (expect `explicitDeny`) and against a `<stack>-<Function>Role-`
name (expect `allowed`, no regression for normal SAM deploys). Note that
simulation currently does **not** see this role's *inline* policies in
seahaven-prod or seahaven-dev — read those back with `get-role-policy` instead.
Known consequence of `DenyBoundaryTampering`: a CloudFormation rollback of an
update that *adds* a boundary to an existing role wedges in
`UPDATE_ROLLBACK_FAILED`. Recovery is an administrator action, not a pipeline
retry — `aws cloudformation continue-update-rollback --stack-name <stack>
--resources-to-skip <RoleLogicalId>`. Unreachable while every SAM role is
created with the boundary already attached.
**Onboarding a future account as a deploy target:**
1. CDK-bootstrap the account (`npx cdk bootstrap aws://<account>/us-east-1`