From 03ea8cbdd8a1a7fed7a254845c2af72a63affe99 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Wed, 17 Jun 2026 11:33:27 -0400 Subject: [PATCH] Scope shift-manager Lambda IAM to least privilege MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nightly sweep flagged four over-broad permissions. Scope each to only what the function actually reads (verified against source): - WeeklyPost: secrets to slack-bot-token-* only (was the whole afterhours-shift-manager/* namespace); SES SendEmail to the single noreply@seahaven.com identity (was identity/*). - RosterSync and RingScheduler: secrets to 3cx-* only (was the whole namespace); both read only the 3cx domain/client-id/client-secret. SlackBotFunction and HolidayRouter wildcards are left unchanged — out of scope for this sweep. Refs: INFRA --- template.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/template.yaml b/template.yaml index 68f325a..639c65e 100644 --- a/template.yaml +++ b/template.yaml @@ -156,16 +156,19 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref ShiftTable - Statement: + # Least privilege: only the Slack bot token, not the whole namespace. - Effect: Allow Action: - secretsmanager:GetSecretValue Resource: - - !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*" + - !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/slack-bot-token-*" - Effect: Allow Action: - ses:SendEmail Resource: - - !Sub "arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/*" + # Only the single sending identity (noreply@seahaven.com), not + # every identity in the account. + - !Sub "arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/noreply@seahaven.com" # The sending identity has a default configuration set # (seahaven-email-events); SES authorizes SendEmail against the # config-set resource too, so it must be granted alongside the @@ -207,11 +210,12 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref ShiftTable - Statement: + # Least privilege: only the 3cx-* secrets this function reads. - Effect: Allow Action: - secretsmanager:GetSecretValue Resource: - - !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*" + - !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/3cx-*" Events: # Daily at 6am ET (before the 7am schedule post and 8am 3CX scheduler) # EST: 6am ET = 11:00 UTC (Nov-Mar) @@ -249,11 +253,12 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref ShiftTable - Statement: + # Least privilege: only the 3cx-* secrets this function reads. - Effect: Allow Action: - secretsmanager:GetSecretValue Resource: - - !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*" + - !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/3cx-*" Events: # Daily at 8am ET — update after-hours routing DailyScheduleEST: