From edf681c4ff6f71818176a712941b1692cce07c91 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Wed, 10 Jun 2026 19:31:59 -0400 Subject: [PATCH] fix: grant KMS on seahaven-dynamodb CMK to PaymentsDashboard consumers (INFRA-104) (#44) The PaymentsDashboard table was migrated to SSE-KMS (alias/seahaven-dynamodb, INFRA-95/M-3) out-of-band, but no function role had kms perms. fetchBoaTransactions failed 6/6 daily runs with kms:Decrypt AccessDeniedException, taking the BoA transaction feed 100% down; the other 3 table consumers were latently broken too. - Add kms:Decrypt/GenerateDataKey/DescribeKey to processPayrollEmail, processPaymentCsv, fetchBoaTransactions (read-write) and kms:Decrypt/DescribeKey to slackAppHome (read-only). Actions match the lambda permissions boundary. - Declare SSESpecification on the table to reconcile out-of-band drift (idempotent). - Resolve the CMK arn from SSM /seahaven/dynamodb/cmk-arn. GPT-4.1 cross-review: no blockers. --- template.yaml | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/template.yaml b/template.yaml index bbad6f0..5496e93 100644 --- a/template.yaml +++ b/template.yaml @@ -2,6 +2,16 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Payments Dashboard - S3 CSV ingestion to Slack App Home +Parameters: + DynamoDbCmkArn: + Type: AWS::SSM::Parameter::Value + Default: /seahaven/dynamodb/cmk-arn + Description: >- + ARN of the shared customer-managed CMK (alias/seahaven-dynamodb) that + encrypts the PaymentsDashboard table. Functions that read/write the table + need kms:Decrypt/GenerateDataKey/DescribeKey on this key (the boundary + permits exactly these), or DynamoDB calls fail with AccessDeniedException. + Globals: Function: Runtime: nodejs22.x @@ -167,6 +177,14 @@ Resources: TimeToLiveSpecification: AttributeName: ttl Enabled: true + # SSE-KMS with the shared CMK (alias/seahaven-dynamodb, INFRA-95 / M-3). + # The table was migrated to this key out-of-band, so declaring it here + # reconciles the template drift (no-op against the live table). Consumer + # roles still need explicit kms perms below (SAM policies do not auto-add). + SSESpecification: + SSEEnabled: true + SSEType: KMS + KMSMasterKeyId: !Ref DynamoDbCmkArn PayrollEmailBucket: Type: AWS::S3::Bucket @@ -385,6 +403,14 @@ Resources: BucketName: !Sub seahaven-payroll-emails-${AWS::AccountId} - DynamoDBCrudPolicy: TableName: !Ref DashboardTable + - Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - kms:Decrypt + - kms:GenerateDataKey + - kms:DescribeKey + Resource: !Ref DynamoDbCmkArn - Version: "2012-10-17" Statement: - Effect: Allow @@ -433,6 +459,14 @@ Resources: BucketName: !Sub seahaven-payments-csv-${AWS::AccountId} - DynamoDBCrudPolicy: TableName: !Ref DashboardTable + - Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - kms:Decrypt + - kms:GenerateDataKey + - kms:DescribeKey + Resource: !Ref DynamoDbCmkArn - Version: "2012-10-17" Statement: - Effect: Allow @@ -469,6 +503,13 @@ Resources: Policies: - DynamoDBReadPolicy: TableName: !Ref DashboardTable + - Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - kms:Decrypt + - kms:DescribeKey + Resource: !Ref DynamoDbCmkArn - Version: "2012-10-17" Statement: - Effect: Allow @@ -508,6 +549,14 @@ Resources: Policies: - DynamoDBCrudPolicy: TableName: !Ref DashboardTable + - Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - kms:Decrypt + - kms:GenerateDataKey + - kms:DescribeKey + Resource: !Ref DynamoDbCmkArn - Version: "2012-10-17" Statement: - Effect: Allow