diff --git a/lib/deploy-substrate/deploy-substrate.template.yaml b/lib/deploy-substrate/deploy-substrate.template.yaml index 2ac9b77..0f4542a 100644 --- a/lib/deploy-substrate/deploy-substrate.template.yaml +++ b/lib/deploy-substrate/deploy-substrate.template.yaml @@ -148,13 +148,15 @@ Description: >- # seahaven-lambda-execution-boundary-meal-order-manager: 2380 / 10 statements # seahaven-lambda-execution-boundary-seahaven-site: 1159 / 6 statements # seahaven-lambda-execution-boundary-seahaven-door-unlock-api: measure after deploy (PLAT-76) +# seahaven-lambda-execution-boundary-paychex-integrations: floor-only (PLAT-120) # Dev copies are floor-only (691 / 4) via IsProdAccount. # # Guardrail PolicyDocuments also cap at 6,144. Each extra allow-list ARN # is copied into four Sids in EACH of SamCfnIamManagementPolicy and -# HcptfIamManagementPolicy. Measured after this list (7 ARNs, PLAT-76): +# HcptfIamManagementPolicy. Measured after PLAT-76 (7 ARNs): # seahaven-cfn-exec-iam-management: 4571 / 10 statements (1573 headroom) # seahaven-hcptf-iam-management: 4693 / 10 statements (1451 headroom) +# PLAT-120 adds an 8th ARN (paychex-integrations). Re-measure after deploy. # # CRITICAL: a role has exactly ONE permissions boundary, so statements cannot # be spilled into a second attached managed policy. Do not introduce @@ -367,6 +369,16 @@ Resources: # - no IAM permissions for S3 / SQS / SSM / SES / KMS / VPC in this # stack's template as of 2026-07-30 # + # paychex-integrations (functions: paychex-*, PLAT-120) + # - Authority: Sea-Haven-Industries/paychex-integrations terraform/ + # (placeholder Lambda). Floor only in this PR: secrets do not exist + # yet so exact GetSecretValue ARNs cannot be pinned. Do NOT add + # secret:paychex-integrations/* patterns. After first HCP apply, + # widen with the six minted secret ARNs. + # - CloudWatch Logs (floor) + # - no DynamoDB / EventBridge / S3 data plane / SES / KMS / VPC in the + # scaffold Terraform + # # afi-backup-monitor (functions: afi-*) # - secretsmanager:GetSecretValue on TWO exact prod secret ARNs # (PLAT-56, created 2026-08-05 in seahaven-prod; no name patterns): @@ -837,6 +849,23 @@ Resources: - !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts/index/*" - !Ref AWS::NoValue + PaychexIntegrationsBoundary: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: seahaven-lambda-execution-boundary-paychex-integrations + Description: >- + Per-workload permissions boundary for paychex-integrations (PLAT-120). + Floor only until first HCP apply mints secret suffixes. + PolicyDocument: + Version: "2012-10-17" + Statement: + # Floor aliases — edit the &lambdaBoundaryFloor* anchors on + # AfiBackupMonitorBoundary only; do not inline a divergent copy. + - *lambdaBoundaryFloorLogsWrite + - *lambdaBoundaryFloorLogsDescribe + - *lambdaBoundaryFloorXRay + - *lambdaBoundaryFloorEc2Eni + ProcurementIngestBoundary: Type: AWS::IAM::ManagedPolicy Properties: @@ -1691,6 +1720,7 @@ Resources: - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-meal-order-manager" - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-seahaven-site" - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-seahaven-door-unlock-api" + - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-paychex-integrations" # Attach managed policies — MUST have boundary already on role - Sid: IAMAttachPolicyWithBoundary diff --git a/lib/terraform-substrate/terraform-substrate.template.yaml b/lib/terraform-substrate/terraform-substrate.template.yaml index 43d1bd4..2a33bac 100644 --- a/lib/terraform-substrate/terraform-substrate.template.yaml +++ b/lib/terraform-substrate/terraform-substrate.template.yaml @@ -82,7 +82,9 @@ Description: >- # len(json.dumps(doc,separators=(',',':'))) on the synthesized PolicyDocument # — the same wall the role INLINE limit (10,240 bytes) put the first # deploy-substrate deploy into on 2026-07-27. Compact size recorded after -# synth in this PR: 4693 characters / 10 statements (1451 headroom). +# synth with 6 ARNs: 4693 characters / 10 statements (1451 headroom). +# PLAT-76 added a seventh ARN. PLAT-120 adds an eighth (paychex-integrations, +# ~380 characters across four Sids). Re-measure after deploy. # # PER-WORKSPACE ROLE ACCUMULATOR # At each stack's migration, a PR appends to this template: @@ -212,6 +214,7 @@ Resources: - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-meal-order-manager" - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-seahaven-site" - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-seahaven-door-unlock-api" + - !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-paychex-integrations" # Attach managed policies — MUST have boundary already on role - Sid: IAMAttachPolicyWithBoundary @@ -739,6 +742,195 @@ Resources: Resource: - !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts" + # --------------------------------------------------------------------------- + # paychex-integrations (PLAT-120) — plan + apply roles for workspace + # paychex-integrations-prod. Copy shape from front-integrations; scaffold + # first apply is Lambda + artifact bucket + alarms + secret shells only + # (no EventBridge, no DynamoDB). Secret Get/Put value stays off the apply + # role. Lambda execution boundary is floor-only until first apply mints + # secret suffixes. + # --------------------------------------------------------------------------- + HcptfPaychexIntegrationsPlanRole: + Type: AWS::IAM::Role + Condition: IsProdAccount + Properties: + RoleName: hcptf-paychex-integrations-plan + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Federated: !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/app.terraform.io" + Action: sts:AssumeRoleWithWebIdentity + Condition: + StringEquals: + "app.terraform.io:aud": aws.workload.identity + "app.terraform.io:sub": organization:seahaven:project:seahaven-prod:workspace:paychex-integrations-prod:run_phase:plan + ManagedPolicyArns: + - arn:aws:iam::aws:policy/job-function/ViewOnlyAccess + Policies: + - PolicyName: paychex-integrations-plan-refresh + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: RefreshIamRoles + Effect: Allow + Action: + - iam:GetRole + - iam:GetRolePolicy + - iam:ListRolePolicies + - iam:ListAttachedRolePolicies + Resource: + - !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/paychex-*" + - Sid: RefreshManagedPolicies + Effect: Allow + Action: + - iam:GetPolicy + - iam:GetPolicyVersion + Resource: "*" + - Sid: RefreshLambda + Effect: Allow + Action: + - lambda:Get* + Resource: + - !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:paychex-*" + # Collection/list APIs authorize only against Resource "*". + - Sid: RefreshLambdaList + Effect: Allow + Action: + - lambda:ListFunctions + - lambda:GetAccountSettings + Resource: "*" + - Sid: RefreshArtifactsBucket + Effect: Allow + Action: + - s3:Get* + - s3:ListBucket + Resource: + - !Sub "arn:aws:s3:::paychex-integrations-artifacts-${AWS::AccountId}" + - !Sub "arn:aws:s3:::paychex-integrations-artifacts-${AWS::AccountId}/*" + - Sid: RefreshCloudWatchAlarms + Effect: Allow + Action: + - cloudwatch:DescribeAlarms + - cloudwatch:ListTagsForResource + Resource: + - !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:paychex-*" + - Sid: RefreshLogs + Effect: Allow + Action: + - logs:DescribeLogGroups + - logs:ListTagsForResource + Resource: "*" + - Sid: RefreshSecrets + Effect: Allow + Action: + - secretsmanager:DescribeSecret + - secretsmanager:GetResourcePolicy + - secretsmanager:ListSecretVersionIds + Resource: + - !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:paychex-integrations/*" + + HcptfPaychexIntegrationsApplyRole: + Type: AWS::IAM::Role + Condition: IsProdAccount + Properties: + RoleName: hcptf-paychex-integrations + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Federated: !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/app.terraform.io" + Action: sts:AssumeRoleWithWebIdentity + Condition: + StringEquals: + "app.terraform.io:aud": aws.workload.identity + "app.terraform.io:sub": organization:seahaven:project:seahaven-prod:workspace:paychex-integrations-prod:run_phase:apply + ManagedPolicyArns: + - !Ref HcptfIamManagementPolicy + Policies: + - PolicyName: paychex-integrations-services + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: LambdaAll + Effect: Allow + Action: + - lambda:* + Resource: + - !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:paychex-*" + - Sid: LambdaList + Effect: Allow + Action: + - lambda:ListFunctions + - lambda:ListLayers + - lambda:GetAccountSettings + Resource: "*" + - Sid: CloudWatchLogs + Effect: Allow + Action: + - logs:CreateLogGroup + - logs:DeleteLogGroup + - logs:PutRetentionPolicy + - logs:DeleteRetentionPolicy + - logs:TagResource + - logs:UntagResource + - logs:ListTagsForResource + Resource: + - !Sub "arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/lambda/paychex-*" + - Sid: CloudWatchLogsDescribe + Effect: Allow + Action: + - logs:DescribeLogGroups + Resource: "*" + - Sid: LambdaArtifactsBucket + Effect: Allow + Action: + - s3:* + Resource: + - !Sub "arn:aws:s3:::paychex-integrations-artifacts-${AWS::AccountId}" + - !Sub "arn:aws:s3:::paychex-integrations-artifacts-${AWS::AccountId}/*" + - Sid: CloudWatchAlarms + Effect: Allow + Action: + - cloudwatch:* + Resource: + - !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:paychex-*" + - Sid: SiteAlertsSns + Effect: Allow + Action: + - sns:Publish + - sns:GetTopicAttributes + Resource: + - !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts" + - Sid: PaychexSecretShell + Effect: Allow + Action: + - secretsmanager:DeleteSecret + - secretsmanager:DescribeSecret + - secretsmanager:GetResourcePolicy + - secretsmanager:PutResourcePolicy + - secretsmanager:DeleteResourcePolicy + - secretsmanager:TagResource + - secretsmanager:UntagResource + Resource: + - !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:paychex-integrations/*" + - Sid: PaychexSecretCreate + Effect: Allow + Action: + - secretsmanager:CreateSecret + Resource: "*" + Condition: + StringEquals: + "secretsmanager:Name": + - paychex-integrations/oauth-client + - paychex-integrations/webhook-api-key + - paychex-integrations/google-service-account + - paychex-integrations/slack-bot-token + - paychex-integrations/front-inboxes-write + - paychex-integrations/3cx-system-admin + # --------------------------------------------------------------------------- # Per-workspace hcptf-* roles for sh-openswe-traces-prod (PLAT-73). #