From 313df882bacd017b91ba0ab81733beb0779b9dd9 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Thu, 23 Jul 2026 15:10:12 -0400 Subject: [PATCH] refactor(prod): drop cross-account CMK grant (slack-bot decommissioned 2026-07-23) The AllowMgmtSlackBotReadViaDynamoDb key-policy statement targeted the seahaven-slack-bot roles, which were decommissioned 2026-07-23. Its successor sh-mcp is undeployed and uses same-account DynamoDB access, so no cross-account reader of the CMK-encrypted purchase-orders table exists. The prod CMK + SSM param + alarm-topic stacks remain (procurement-ingest still imports them). Add a scoped cross-account grant if/when a real cross-account consumer deploys. --- bin/app.ts | 43 +++++++++---------------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/bin/app.ts b/bin/app.ts index b8d1903..7618767 100644 --- a/bin/app.ts +++ b/bin/app.ts @@ -1,7 +1,6 @@ #!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; -import * as iam from "aws-cdk-lib/aws-iam"; import { AccountBaselineStack } from "../lib/account-baseline-stack"; import { AlarmTopicStack } from "../lib/alarm-topic-stack"; import { BackupOffsiteStack } from "../lib/backup-offsite-stack"; @@ -173,44 +172,20 @@ new DynamoDbCmkStack(app, "dynamodb-cmk", { // `site-alerts` by constructed in-account ARN, so both must exist in prod // BEFORE that app's first prod deploy. Same stack names as mgmt (unique // per-account); distinct CDK ids. -const prodDynamoCmk = new DynamoDbCmkStack(app, "dynamodb-cmk-prod", { - stackName: "seahaven-dynamodb-cmk", - env: { account: PROD_ACCOUNT, region: "us-east-1" }, -}); - -// seahaven-slack-bot stays in mgmt but reads the CMK-encrypted purchase-orders -// table cross-account after the migration. The base statement's -// kms:CallerAccount pin (correctly) excludes foreign callers, so the mgmt bot -// roles need their own statement. Delegation-to-IAM per this stack's header: -// PrincipalArn is wildcarded by stack prefix because CFN role suffixes rotate; -// each bot role must ALSO carry an identity-policy grant on this key ARN -// (added in the slack-bot repo's cutover PR). -// -// ⚠️ CUTOVER TRAP: the slack-bot's existing pattern resolves the CMK via SSM -// /seahaven/dynamodb/cmk-arn — that parameter is ACCOUNT-LOCAL and in mgmt -// resolves the MGMT key forever. The cutover PR's identity grant must use the -// PROD key ARN (this stack's DynamoDbCmkArn output), never the mgmt param; -// reusing the SSM pattern grants the wrong key and fails only at runtime. +// No cross-account key-policy statement: the only cross-account reader of the +// CMK-encrypted purchase-orders table (seahaven-slack-bot) was decommissioned +// 2026-07-23, and its successor sh-mcp is undeployed and uses same-account +// DynamoDB access. When/if a cross-account consumer materializes, add a +// correctly-scoped grant then (target its real roles + account). // // Recovery note (failed FIRST create): the key is RETAIN, its alias/SSM param // are not — a CREATE_FAILED rollback orphans an unaliased rotation-enabled // key. Before re-running the deploy, list unaliased CMKs in prod and schedule // deletion of the orphan. -prodDynamoCmk.key.addToResourcePolicy( - new iam.PolicyStatement({ - sid: "AllowMgmtSlackBotReadViaDynamoDb", - effect: iam.Effect.ALLOW, - principals: [new iam.AccountPrincipal(ACCOUNT)], - actions: ["kms:Decrypt", "kms:DescribeKey"], - resources: ["*"], - conditions: { - StringEquals: { "kms:ViaService": "dynamodb.us-east-1.amazonaws.com" }, - ArnLike: { - "aws:PrincipalArn": `arn:aws:iam::${ACCOUNT}:role/seahaven-slack-bot-*`, - }, - }, - }), -); +new DynamoDbCmkStack(app, "dynamodb-cmk-prod", { + stackName: "seahaven-dynamodb-cmk", + env: { account: PROD_ACCOUNT, region: "us-east-1" }, +}); new AlarmTopicStack(app, "alarm-topic-prod", { stackName: "seahaven-alarm-topic",