diff --git a/oidc-deploy-roles.yaml b/oidc-deploy-roles.yaml index fbb3279..2754733 100644 --- a/oidc-deploy-roles.yaml +++ b/oidc-deploy-roles.yaml @@ -31,6 +31,192 @@ 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) ──────── + # ec2:Describe* are required even for non-VPC Lambdas as the ENI + # lifecycle actions need them. + - Sid: Ec2Eni + Effect: Allow + Action: + - ec2:CreateNetworkInterface + - ec2:DescribeNetworkInterfaces + - ec2:DeleteNetworkInterface + - ec2:AssignPrivateIpAddresses + - ec2:UnassignPrivateIpAddresses + Resource: "*" + + # ── DynamoDB (afterhours, payments, meal-order, front-integrations) ─ + - 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/*" + + # ── 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/*" + # --------------------------------------------------------------------------- # Shared CloudFormation execution role (SAM stacks) # --------------------------------------------------------------------------- @@ -540,6 +726,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: