mirror of
https://github.com/Sea-Haven-Industries/.github.git
synced 2026-09-30 04:43:12 +00:00
INFRA-103: Add seahaven-lambda-execution-boundary managed policy (#45)
* Add seahaven-lambda-execution-boundary managed policy Lambda execution roles auto-generated by SAM have no ceiling today — a misconfigured Policies block could grant excessive permissions that persist at runtime. This boundary caps every SAM function execution role at the union of what the five stacks actually need, so the effective permissions are always the intersection of the role's own policies and this document. The policy is a deliberate superset rather than exact-minimum: being slightly broad is safer than a boundary that breaks functions at runtime. Per-service scoping will tighten in follow-up work. SAM template agents: add PermissionsBoundary: arn:aws:iam::328440206208:policy/seahaven-lambda-execution-boundary to Globals.Function in all five stacks after this stack deploys. Refs: INFRA-103 * Fix boundary gaps found in GPT-4.1 cross-review Three issues from the mandatory IAM cross-review (BLOCK/FIX): 1. Add KMS statement — PaymentsDashboard DynamoDB table and payments-dashboard CloudWatch log groups use CMKs. Without kms:Decrypt + kms:GenerateDataKey in the boundary, those Lambda calls fail at the KMS layer at runtime. Scoped to account keys only. 2. Add table/*/index/* to DynamoDB resource — dynamodb:Query on a GSI requires the index ARN; covering only table/* silently denied GSI queries at the boundary. 3. Fix EC2 ENI statement — remove AssignPrivateIpAddresses / UnassignPrivateIpAddresses (EFA-only, not part of Lambda ENI lifecycle); add DescribeSubnets + DescribeSecurityGroups + DescribeVpcs which are required by the Lambda service during VPC attachment and are present in AWSLambdaVPCAccessExecutionRole. 4. Add SES configuration-set/* resource — ses:SendRawEmail requires permission on the configuration set if one is passed at send time. Refs: INFRA-103
This commit is contained in:
parent
7f84f9cfde
commit
291a62b00d
1 changed files with 217 additions and 0 deletions
|
|
@ -31,6 +31,216 @@ Resources:
|
|||
ThumbprintList:
|
||||
- 6938fd4d98bab03faadb97b34396831e3780aea1
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Lambda execution permissions boundary (INFRA-103)
|
||||
#
|
||||
# This managed policy is the CEILING for every Lambda execution role that the
|
||||
# five SAM stacks auto-generate via AWS::Serverless::Function. Applying it as
|
||||
# PermissionsBoundary on those roles means the effective permissions are the
|
||||
# intersection of the role's own policies and this boundary, so a misconfigured
|
||||
# SAM role can never exceed what is listed here.
|
||||
#
|
||||
# The boundary is intentionally a SUPERSET of the union of all runtime
|
||||
# permissions currently granted across the five stacks. Being slightly broad
|
||||
# is the correct trade-off at this stage — a boundary that is too tight will
|
||||
# break Lambda functions at runtime after deploy, which is worse than a slightly
|
||||
# loose boundary that is tightened in a follow-up.
|
||||
#
|
||||
# Permission sources per stack:
|
||||
#
|
||||
# afterhours-shift-manager
|
||||
# - DynamoDB CRUD (afterhours-shifts table)
|
||||
# - secretsmanager:GetSecretValue (afterhours-shift-manager/*)
|
||||
# - ses:SendEmail (SES identity)
|
||||
# - CloudWatch Logs (all functions)
|
||||
#
|
||||
# payments-dashboard
|
||||
# - DynamoDB CRUD / Read (PaymentsDashboard table)
|
||||
# - S3 GetObject (payroll-emails, payments-csv buckets)
|
||||
# - secretsmanager:GetSecretValue (payments-dashboard/*)
|
||||
# - sqs:SendMessage + sqs:ReceiveMessage + sqs:DeleteMessage etc.
|
||||
# (PayrollBatchQueue + DLQs)
|
||||
# - lambda:InvokeFunction (ExpenseReceiver → ExpenseProcessor)
|
||||
# - ec2:CreateNetworkInterface / DescribeNetworkInterfaces /
|
||||
# DeleteNetworkInterface (VPC-attached functions)
|
||||
# - CloudWatch Logs
|
||||
#
|
||||
# meal-order-manager
|
||||
# - DynamoDB CRUD / Read (meal-order-manager-orders table)
|
||||
# - S3 CRUD (ReportsBucket) + s3:GetObject (ReportsBucket presigned URLs)
|
||||
# - secretsmanager:GetSecretValue (meal-order-manager/*)
|
||||
# - ssm:GetParameter (/meal-order-manager/*)
|
||||
# - lambda:InvokeFunction (submit-order → slack-notifier,
|
||||
# close-form → aggregate-orders)
|
||||
# - ses:SendRawEmail
|
||||
# - CloudWatch Logs
|
||||
#
|
||||
# front-integrations
|
||||
# - DynamoDB CRUD (front-sla-alerts table)
|
||||
# - secretsmanager:GetSecretValue (by ARN, various)
|
||||
# - CloudWatch Logs
|
||||
#
|
||||
# afi-backup-monitor
|
||||
# - secretsmanager:GetSecretValue (by ARN)
|
||||
# - CloudWatch Logs
|
||||
#
|
||||
# ---------------------------------------------------------------------------
|
||||
LambdaExecutionBoundary:
|
||||
Type: AWS::IAM::ManagedPolicy
|
||||
Properties:
|
||||
ManagedPolicyName: seahaven-lambda-execution-boundary
|
||||
Description: >-
|
||||
Permissions boundary ceiling for all SAM-managed Lambda execution roles.
|
||||
Applied via PermissionsBoundary on every Globals.Function in the five
|
||||
SAM stacks (INFRA-103). Effective permissions are the intersection of
|
||||
this policy and the role's own inline policies.
|
||||
PolicyDocument:
|
||||
Version: "2012-10-17"
|
||||
Statement:
|
||||
|
||||
# ── CloudWatch Logs (every Lambda) ──────────────────────────────────
|
||||
- Sid: CloudWatchLogs
|
||||
Effect: Allow
|
||||
Action:
|
||||
- logs:CreateLogGroup
|
||||
- logs:CreateLogStream
|
||||
- logs:PutLogEvents
|
||||
- logs:DescribeLogGroups
|
||||
- logs:DescribeLogStreams
|
||||
Resource: "*"
|
||||
|
||||
# ── X-Ray tracing (standard Lambda execution) ────────────────────
|
||||
- Sid: XRay
|
||||
Effect: Allow
|
||||
Action:
|
||||
- xray:PutTraceSegments
|
||||
- xray:PutTelemetryRecords
|
||||
Resource: "*"
|
||||
|
||||
# ── VPC / ENI management (payments-dashboard VPC functions) ────────
|
||||
# Matches AWSLambdaVPCAccessExecutionRole exactly.
|
||||
# AssignPrivateIpAddresses / UnassignPrivateIpAddresses are for EFA
|
||||
# and secondary IPs — not part of the Lambda ENI lifecycle — omitted.
|
||||
- Sid: Ec2Eni
|
||||
Effect: Allow
|
||||
Action:
|
||||
- ec2:CreateNetworkInterface
|
||||
- ec2:DescribeNetworkInterfaces
|
||||
- ec2:DeleteNetworkInterface
|
||||
- ec2:DescribeSubnets
|
||||
- ec2:DescribeSecurityGroups
|
||||
- ec2:DescribeVpcs
|
||||
Resource: "*"
|
||||
|
||||
# ── DynamoDB (afterhours, payments, meal-order, front-integrations) ─
|
||||
# Table/* covers base-table operations; table/*/index/* is required for
|
||||
# Query/Scan on Global Secondary Indexes.
|
||||
- Sid: DynamoDB
|
||||
Effect: Allow
|
||||
Action:
|
||||
- dynamodb:GetItem
|
||||
- dynamodb:PutItem
|
||||
- dynamodb:UpdateItem
|
||||
- dynamodb:DeleteItem
|
||||
- dynamodb:Query
|
||||
- dynamodb:Scan
|
||||
- dynamodb:BatchGetItem
|
||||
- dynamodb:BatchWriteItem
|
||||
- dynamodb:DescribeTable
|
||||
- dynamodb:ConditionCheckItem
|
||||
Resource:
|
||||
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/*"
|
||||
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/*/index/*"
|
||||
|
||||
# ── S3 (payments-dashboard read, meal-order-manager CRUD) ──────────
|
||||
- Sid: S3
|
||||
Effect: Allow
|
||||
Action:
|
||||
- s3:GetObject
|
||||
- s3:PutObject
|
||||
- s3:DeleteObject
|
||||
- s3:ListBucket
|
||||
- s3:GetBucketLocation
|
||||
- s3:GetObjectVersion
|
||||
- s3:GetObjectTagging
|
||||
- s3:PutObjectTagging
|
||||
Resource:
|
||||
- !Sub "arn:aws:s3:::*-${AWS::AccountId}"
|
||||
- !Sub "arn:aws:s3:::*-${AWS::AccountId}/*"
|
||||
# meal-order-manager ReportsBucket (non-AccountId suffix pattern)
|
||||
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}"
|
||||
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}/*"
|
||||
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}"
|
||||
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/*"
|
||||
|
||||
# ── Secrets Manager (all stacks) ──────────────────────────────────
|
||||
- Sid: SecretsManager
|
||||
Effect: Allow
|
||||
Action:
|
||||
- secretsmanager:GetSecretValue
|
||||
- secretsmanager:DescribeSecret
|
||||
Resource:
|
||||
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:*"
|
||||
|
||||
# ── SSM Parameter Store (meal-order-manager, afterhours) ──────────
|
||||
- Sid: SSMParameterRead
|
||||
Effect: Allow
|
||||
Action:
|
||||
- ssm:GetParameter
|
||||
- ssm:GetParameters
|
||||
- ssm:GetParametersByPath
|
||||
Resource:
|
||||
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/*"
|
||||
|
||||
# ── SQS (payments-dashboard batch queues) ─────────────────────────
|
||||
- Sid: SQS
|
||||
Effect: Allow
|
||||
Action:
|
||||
- sqs:SendMessage
|
||||
- sqs:ReceiveMessage
|
||||
- sqs:DeleteMessage
|
||||
- sqs:GetQueueAttributes
|
||||
- sqs:GetQueueUrl
|
||||
- sqs:ChangeMessageVisibility
|
||||
Resource:
|
||||
- !Sub "arn:aws:sqs:us-east-1:${AWS::AccountId}:*"
|
||||
|
||||
# ── Lambda invocation (payments, meal-order inter-function calls) ──
|
||||
- Sid: LambdaInvoke
|
||||
Effect: Allow
|
||||
Action:
|
||||
- lambda:InvokeFunction
|
||||
Resource:
|
||||
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:*"
|
||||
|
||||
# ── SES (afterhours weekly-post, meal-order email-report) ──────────
|
||||
- Sid: SES
|
||||
Effect: Allow
|
||||
Action:
|
||||
- ses:SendEmail
|
||||
- ses:SendRawEmail
|
||||
Resource:
|
||||
- !Sub "arn:aws:ses:us-east-1:${AWS::AccountId}:identity/*"
|
||||
- !Sub "arn:aws:ses:us-east-1:${AWS::AccountId}:configuration-set/*"
|
||||
|
||||
# ── KMS (CMK-encrypted resources) ─────────────────────────────────
|
||||
# Required for Lambda functions that read/write CMK-encrypted AWS
|
||||
# resources. Verified live state:
|
||||
# - PaymentsDashboard DynamoDB table: CMK key/0b660af3 (KMS:ENABLED)
|
||||
# - payments-dashboard CloudWatch log groups: CMK key/b748750c
|
||||
# Secrets Manager + SQS queues in these stacks use AWS-managed keys
|
||||
# (aws/secretsmanager, aws/sqs) which do not require explicit kms:*
|
||||
# actions in the execution role policy. The CMK keys are scoped to
|
||||
# this account to prevent cross-account KMS calls.
|
||||
- Sid: KMS
|
||||
Effect: Allow
|
||||
Action:
|
||||
- kms:Decrypt
|
||||
- kms:GenerateDataKey
|
||||
- kms:DescribeKey
|
||||
Resource:
|
||||
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:key/*"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Shared CloudFormation execution role (SAM stacks)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -540,6 +750,13 @@ Resources:
|
|||
- !Sub arn:aws:iam::${AWS::AccountId}:role/cdk-hnb659fds-*
|
||||
|
||||
Outputs:
|
||||
LambdaExecutionBoundaryArn:
|
||||
Value: !Ref LambdaExecutionBoundary
|
||||
Description: >-
|
||||
ARN of the Lambda execution permissions boundary. Set this as
|
||||
PermissionsBoundary on Globals.Function in all five SAM stacks.
|
||||
Export:
|
||||
Name: seahaven-lambda-execution-boundary-arn
|
||||
SamCfnExecutionRoleArn:
|
||||
Value: !GetAtt SamCfnExecutionRole.Arn
|
||||
Export:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue