mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-10-04 19:31:58 +00:00
Merge pull request #63 from Sea-Haven-Industries/fix/deploy-substrate-deny-self-mutation
fix(iam): port DenySelfMutation into the prod/dev deploy substrate
This commit is contained in:
commit
9c1d083f3f
1 changed files with 70 additions and 12 deletions
|
|
@ -30,18 +30,21 @@ Description: >-
|
||||||
# first deploy failed with ServiceLimitExceeded (2026-07-27). Effective
|
# first deploy failed with ServiceLimitExceeded (2026-07-27). Effective
|
||||||
# permissions are unchanged — verified by comparing the full 27-statement
|
# permissions are unchanged — verified by comparing the full 27-statement
|
||||||
# set before and after the move (identical), since identity policies are
|
# set before and after the move (identical), since identity policies are
|
||||||
# unioned and an explicit Deny still wins. NOTE for the mgmt remediation:
|
# unioned and an explicit Deny still wins. mgmt received this same
|
||||||
# mgmt needs this same restructure before its Deny statements can be added,
|
# restructure in Phase B (.github PR #98), so this is no longer a
|
||||||
# - SECURITY FIX, deliberate divergence: iam:DeleteRolePermissionsBoundary
|
# divergence,
|
||||||
|
# - SECURITY FIX (now in BOTH copies): iam:DeleteRolePermissionsBoundary
|
||||||
# removed from Sid IAMPutPermissionsBoundary and explicit Deny statements
|
# removed from Sid IAMPutPermissionsBoundary and explicit Deny statements
|
||||||
# (DenyBoundaryTampering / DenyBoundaryPolicyEdit) added. The mgmt copy
|
# (DenyBoundaryTampering / DenyBoundaryPolicyEdit / DenySelfMutation)
|
||||||
# still carries the hole — verified live 2026-07-27 via
|
# added. The mgmt copy was remediated 2026-07-27 (.github PRs #95 Phase A
|
||||||
# simulate-principal-policy on the deployed mgmt role
|
# + #98 Phase B); DenySelfMutation and the widened policy/seahaven-*
|
||||||
# (iam:DeleteRolePermissionsBoundary = ALLOWED). New accounts must not be
|
# DenyBoundaryPolicyEdit scope were then ported back here, so the two
|
||||||
# born with it. Remediating mgmt means changing a role that is actively
|
# copies' statement sets are reconciled as of that date — every IAM
|
||||||
# executing production deploys, so it is tracked as a separate change
|
# statement in LambdaExecutionBoundary, SamCfnIamManagementPolicy and
|
||||||
# with its own review gates rather than folded in here. Reconcile the two
|
# SamCfnExecutionRole is byte-identical across the two files; the only
|
||||||
# copies when that lands.
|
# remaining delta is the DependsOn line above, which is ordering, not
|
||||||
|
# permission. If a substrate statement changes again, change BOTH files
|
||||||
|
# in the same piece of work.
|
||||||
#
|
#
|
||||||
# This template is deployed via lib/deploy-substrate-stack.ts
|
# This template is deployed via lib/deploy-substrate-stack.ts
|
||||||
# (cloudformation-include) as stack seahaven-deploy-substrate, once per
|
# (cloudformation-include) as stack seahaven-deploy-substrate, once per
|
||||||
|
|
@ -527,6 +530,12 @@ Resources:
|
||||||
- logs:DescribeDestinations
|
- logs:DescribeDestinations
|
||||||
- logs:AssociateKmsKey
|
- logs:AssociateKmsKey
|
||||||
- logs:DisassociateKmsKey
|
- logs:DisassociateKmsKey
|
||||||
|
# Ported from the mgmt copy (Phase A): afterhours-shift-manager
|
||||||
|
# creates an AWS::Logs::MetricFilter through this role, so a
|
||||||
|
# SAM stack migrating here fails mid-deploy without these.
|
||||||
|
- logs:PutMetricFilter
|
||||||
|
- logs:DeleteMetricFilter
|
||||||
|
- logs:DescribeMetricFilters
|
||||||
Resource: "*"
|
Resource: "*"
|
||||||
|
|
||||||
# ── EventBridge / CloudWatch Events (scheduled Lambdas) ───────────
|
# ── EventBridge / CloudWatch Events (scheduled Lambdas) ───────────
|
||||||
|
|
@ -880,6 +889,12 @@ Resources:
|
||||||
- !Sub "arn:aws:iam::${AWS::AccountId}:role/*"
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/*"
|
||||||
- !Sub "arn:aws:iam::${AWS::AccountId}:user/*"
|
- !Sub "arn:aws:iam::${AWS::AccountId}:user/*"
|
||||||
|
|
||||||
|
# Scoped to the whole seahaven-* policy family, not just the boundary:
|
||||||
|
# this policy carries the Deny statements, so it is now a
|
||||||
|
# higher-value target than the boundary it protects. Safe to scope
|
||||||
|
# broadly — the role holds no iam:CreatePolicy anywhere and no SAM
|
||||||
|
# stack manages a managed policy through it (both verified
|
||||||
|
# 2026-07-27), so nothing legitimate writes policy versions here.
|
||||||
- Sid: DenyBoundaryPolicyEdit
|
- Sid: DenyBoundaryPolicyEdit
|
||||||
Effect: Deny
|
Effect: Deny
|
||||||
Action:
|
Action:
|
||||||
|
|
@ -888,7 +903,50 @@ Resources:
|
||||||
- iam:DeletePolicyVersion
|
- iam:DeletePolicyVersion
|
||||||
- iam:DeletePolicy
|
- iam:DeletePolicy
|
||||||
Resource:
|
Resource:
|
||||||
- !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary"
|
- !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-*"
|
||||||
|
|
||||||
|
# Self-protection. Without this the whole control is one API call
|
||||||
|
# from being undone: IAMRoleReadAndDelete below grants
|
||||||
|
# iam:DetachRolePolicy on Resource "*" with no condition, so this
|
||||||
|
# role could detach the very policy carrying these Denies from
|
||||||
|
# itself and reinstate the escalation. Verified live 2026-07-27:
|
||||||
|
# simulate-principal-policy returned "allowed" for DetachRolePolicy,
|
||||||
|
# DeleteRolePolicy and DeleteRole against this role's own ARN and
|
||||||
|
# against githubdeploy-* roles.
|
||||||
|
#
|
||||||
|
# Also closes a denial-of-service and a self-elevation precondition:
|
||||||
|
# iam:PutRolePermissionsBoundary is condition-pinned to the Lambda
|
||||||
|
# boundary ARN but NOT scoped by target, so this role could apply
|
||||||
|
# that runtime boundary to itself or to a githubdeploy-* role —
|
||||||
|
# bricking the pipelines, unrecoverable without an admin because
|
||||||
|
# removing a boundary is denied above, and making the otherwise-inert
|
||||||
|
# AttachRolePolicy/PutRolePolicy self-elevation conditions start
|
||||||
|
# matching.
|
||||||
|
#
|
||||||
|
# Costs nothing operationally: this role is only ever passed to
|
||||||
|
# CloudFormation for SAM application stacks. The substrate's own
|
||||||
|
# roles are managed by THIS stack (deployed through the CDK
|
||||||
|
# bootstrap execution role), and per-repo githubdeploy-* roles are
|
||||||
|
# provisioned at onboarding time outside any stack this role
|
||||||
|
# executes — so CloudFormation never exercises these actions
|
||||||
|
# against them as this role. SAM-generated roles are named
|
||||||
|
# <stack>-<Function>Role-<hash> and are unaffected.
|
||||||
|
- Sid: DenySelfMutation
|
||||||
|
Effect: Deny
|
||||||
|
Action:
|
||||||
|
- iam:AttachRolePolicy
|
||||||
|
- iam:DeleteRole
|
||||||
|
- iam:DeleteRolePolicy
|
||||||
|
- iam:DeleteRolePermissionsBoundary
|
||||||
|
- iam:DetachRolePolicy
|
||||||
|
- iam:PutRolePolicy
|
||||||
|
- iam:PutRolePermissionsBoundary
|
||||||
|
- iam:UpdateAssumeRolePolicy
|
||||||
|
- iam:UpdateRole
|
||||||
|
- iam:UpdateRoleDescription
|
||||||
|
Resource:
|
||||||
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/github-cfn-execution-role"
|
||||||
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/githubdeploy-*"
|
||||||
|
|
||||||
# Read / tag / delete role and policy — no boundary condition needed
|
# Read / tag / delete role and policy — no boundary condition needed
|
||||||
- Sid: IAMRoleReadAndDelete
|
- Sid: IAMRoleReadAndDelete
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue