feat(iam): add door-unlock-api hcptf roles and boundary (PLAT-76) (#123)
Some checks are pending
Deploy / deploy-management (push) Waiting to run
Deploy / deploy-external-dev (push) Waiting to run
Deploy / deploy-security (push) Waiting to run
Deploy / deploy-dev (push) Waiting to run
Deploy / deploy-prod (push) Waiting to run

* feat(iam): add door-unlock-api hcptf roles and boundary

Give HCP Terraform a prod plan/apply pair, a per-workload Lambda boundary with exact SSM and 3CX ARNs, and API access-log delivery so PLAT-76 can leave the mgmt CDK stack.

* fix(iam): pin door-unlock apigw domain and ssm reads

Stop the apply role from managing every HTTP API custom domain, and keep SecureString door-unlock parameters off HCP plan and apply GetParameter.
This commit is contained in:
Adam Moussa 2026-08-27 21:26:27 +00:00 • committed by GitHub
parent 608c11ed0a
commit 689ec147a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 377 additions and 1 deletions

View file

@ -147,11 +147,12 @@ Description: >-
# seahaven-lambda-execution-boundary-procurement-ingest: 3977 / 11 statements
# 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)
# 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 (6 ARNs):
# HcptfIamManagementPolicy. Measured after this list (7 ARNs, PLAT-76):
# seahaven-cfn-exec-iam-management: 4571 / 10 statements (1573 headroom)
# seahaven-hcptf-iam-management: 4693 / 10 statements (1451 headroom)
#
@ -1104,6 +1105,47 @@ Resources:
- !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/*"
- !Ref AWS::NoValue
DoorUnlockApiBoundary:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: seahaven-lambda-execution-boundary-seahaven-door-unlock-api
Description: >-
Per-workload permissions boundary for seahaven-door-unlock-api
(PLAT-76 / PLAT-52). Floor plus exact prod SSM parameter ARNs and
3CX secret ARNs. Shared policy remains the live-role ceiling
until app retarget.
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
- !If
- IsProdAccount
- Sid: DoorUnlockApiSsm
Effect: Allow
Action:
- ssm:GetParameter
Resource:
- arn:aws:ssm:us-east-1:011934824531:parameter/seahaven/door-unlock/elements-api-key
- arn:aws:ssm:us-east-1:011934824531:parameter/seahaven/door-unlock/auth-token
- arn:aws:ssm:us-east-1:011934824531:parameter/seahaven/door-unlock/door-id
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: DoorUnlockApiSecrets
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-domain-TPwqWP
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-client-id-jzyQXb
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-client-secret-jpO476
- !Ref AWS::NoValue
# ---------------------------------------------------------------------------
# Shared CloudFormation execution role (SAM stacks) — INFRA-97 scoped
#
@ -1648,6 +1690,7 @@ Resources:
- !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-procurement-ingest"
- !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"
# Attach managed policies — MUST have boundary already on role
- Sid: IAMAttachPolicyWithBoundary

View file

@ -211,6 +211,7 @@ Resources:
- !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary-procurement-ingest"
- !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"
# Attach managed policies — MUST have boundary already on role
- Sid: IAMAttachPolicyWithBoundary
@ -1980,3 +1981,335 @@ Resources:
}
]
}
# ---------------------------------------------------------------------------
# seahaven-door-unlock-api-prod (PLAT-76) — HttpApi + 5 Lambdas + EventBridge
# + ACM custom domain + alarms. Plan role: ViewOnly + plan-refresh sidecar.
# Apply role: HcptfIamManagement + prefix-scoped service wildcards.
# ---------------------------------------------------------------------------
HcptfDoorUnlockApiPlanRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-seahaven-door-unlock-api-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:seahaven-door-unlock-api-prod:run_phase:plan
ManagedPolicyArns:
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
Policies:
- PolicyName: seahaven-door-unlock-api-plan-refresh
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: RefreshIamRoles
Effect: Allow
Action:
- iam:GetRole
- iam:GetRolePolicy
- iam:ListRolePolicies
- iam:ListAttachedRolePolicies
- iam:ListRoleTags
Resource:
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/door-unlock-api-*"
- Sid: RefreshManagedPolicies
Effect: Allow
Action:
- iam:GetPolicy
- iam:GetPolicyVersion
Resource: "*"
- Sid: RefreshEventBridge
Effect: Allow
Action:
- events:DescribeRule
- events:ListTargetsByRule
- events:ListTagsForResource
Resource:
- !Sub "arn:aws:events:us-east-1:${AWS::AccountId}:rule/door-unlock-api-*"
- Sid: RefreshLambda
Effect: Allow
Action:
- lambda:Get*
- lambda:List*
Resource:
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:door-unlock-api-*"
- Sid: RefreshBuckets
Effect: Allow
Action:
- s3:Get*
- s3:ListBucket
Resource:
- !Sub "arn:aws:s3:::door-unlock-api-artifacts-${AWS::AccountId}"
- !Sub "arn:aws:s3:::door-unlock-api-artifacts-${AWS::AccountId}/*"
- Sid: RefreshLogs
Effect: Allow
Action:
- logs:DescribeLogGroups
- logs:ListTagsForResource
Resource: "*"
- Sid: RefreshAcm
Effect: Allow
Action:
- acm:DescribeCertificate
- acm:ListCertificates
- acm:ListTagsForCertificate
- acm:GetCertificate
Resource: "*"
# String door-id only. SecureString auth-token / elements-api-key
# stay off the plan role so speculative runs cannot render them.
- Sid: RefreshDoorUnlockSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/door-unlock/door-id"
- Sid: RefreshDoorUnlockSsmTags
Effect: Allow
Action:
- ssm:ListTagsForResource
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/door-unlock/*"
- Sid: RefreshSsmDescribeParameters
Effect: Allow
Action:
- ssm:DescribeParameters
Resource: "*"
- Sid: RefreshHttpApi
Effect: Allow
Action:
- apigateway:GET
Resource:
- !Sub "arn:aws:apigateway:us-east-1::/apis/*"
- arn:aws:apigateway:us-east-1::/domainnames/doorunlock.seahaven.com
- arn:aws:apigateway:us-east-1::/domainnames/doorunlock.seahaven.com/*
- !Sub "arn:aws:apigateway:us-east-1::/tags/*"
- Sid: RefreshAlarms
Effect: Allow
Action:
- cloudwatch:DescribeAlarms
- cloudwatch:ListTagsForResource
Resource: "*"
- Sid: RefreshThreeCxSecrets
Effect: Allow
Action:
- secretsmanager:DescribeSecret
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-domain-TPwqWP
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-client-id-jzyQXb
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-client-secret-jpO476
HcptfDoorUnlockApiApplyRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-seahaven-door-unlock-api
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:seahaven-door-unlock-api-prod:run_phase:apply
ManagedPolicyArns:
- !Ref HcptfIamManagementPolicy
Policies:
- PolicyName: seahaven-door-unlock-api-services
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: LambdaAll
Effect: Allow
Action:
- lambda:*
Resource:
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:door-unlock-api-*"
- Sid: LambdaList
Effect: Allow
Action:
- lambda:ListFunctions
- lambda:GetAccountSettings
Resource: "*"
- Sid: EventBridgeRules
Effect: Allow
Action:
- events:*
Resource:
- !Sub "arn:aws:events:us-east-1:${AWS::AccountId}:rule/door-unlock-api-*"
- 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/door-unlock-api-*"
- !Sub "arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/apigateway/door-unlock-api*"
- Sid: CloudWatchLogsDescribe
Effect: Allow
Action:
- logs:DescribeLogGroups
Resource: "*"
- Sid: DoorUnlockApiGwAccessLogDelivery
Effect: Allow
Action:
- logs:CreateLogDelivery
- logs:GetLogDelivery
- logs:UpdateLogDelivery
- logs:DeleteLogDelivery
- logs:ListLogDeliveries
- logs:DescribeResourcePolicies
Resource: "*"
- Sid: StackBuckets
Effect: Allow
Action:
- s3:*
Resource:
- !Sub "arn:aws:s3:::door-unlock-api-artifacts-${AWS::AccountId}"
- !Sub "arn:aws:s3:::door-unlock-api-artifacts-${AWS::AccountId}/*"
# HTTP API ids are allocated at create (same as meal-order).
# Custom domain is hostname-pinned like procurement-api.
- Sid: HttpApiManage
Effect: Allow
Action:
- apigateway:*
Resource:
- !Sub "arn:aws:apigateway:us-east-1::/apis"
- !Sub "arn:aws:apigateway:us-east-1::/apis/*"
- !Sub "arn:aws:apigateway:us-east-1::/tags/*"
- Sid: HttpApiDomain
Effect: Allow
Action:
- apigateway:*
Resource:
- arn:aws:apigateway:us-east-1::/domainnames/doorunlock.seahaven.com
- arn:aws:apigateway:us-east-1::/domainnames/doorunlock.seahaven.com/*
- Sid: AcmCreate
Effect: Allow
Action:
- acm:RequestCertificate
Resource: "*"
Condition:
StringEquals:
"aws:RequestTag/Project": seahaven-door-unlock-api
- Sid: AcmList
Effect: Allow
Action:
- acm:ListCertificates
- acm:ListTagsForCertificate
Resource: "*"
- Sid: AcmManageTagged
Effect: Allow
Action:
- acm:DescribeCertificate
- acm:GetCertificate
- acm:DeleteCertificate
- acm:AddTagsToCertificate
- acm:RemoveTagsFromCertificate
- acm:RenewCertificate
Resource: "*"
Condition:
StringEquals:
"aws:ResourceTag/Project": seahaven-door-unlock-api
# HCP reads the String door-id data source only. Lambda execution
# roles (not this apply role) GetParameter the SecureStrings.
- Sid: DoorUnlockSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/door-unlock/door-id"
- Sid: DoorUnlockSsmTags
Effect: Allow
Action:
- ssm:ListTagsForResource
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/door-unlock/*"
- Sid: DoorUnlockSsmDescribeParameters
Effect: Allow
Action:
- ssm:DescribeParameters
Resource: "*"
- Sid: DescribeThreeCxSecrets
Effect: Allow
Action:
- secretsmanager:DescribeSecret
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-domain-TPwqWP
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-client-id-jzyQXb
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afterhours-shift-manager/3cx-client-secret-jpO476
- Sid: DoorUnlockPassRoleApiGateway
Effect: Allow
Action:
- iam:PassRole
Resource:
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/door-unlock-api-*"
Condition:
StringEquals:
"iam:PassedToService": "apigateway.amazonaws.com"
- Sid: CloudWatchAlarms
Effect: Allow
Action:
- cloudwatch:PutMetricAlarm
- cloudwatch:DeleteAlarms
- cloudwatch:DescribeAlarms
- cloudwatch:TagResource
- cloudwatch:UntagResource
- cloudwatch:ListTagsForResource
Resource:
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:door-unlock-api-*"
- Sid: SnsPublishSiteAlerts
Effect: Allow
Action:
- sns:Publish
- sns:GetTopicAttributes
- sns:ListTagsForResource
Resource:
- !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts"
DoorUnlockApiAccessLogResourcePolicy:
Type: AWS::Logs::ResourcePolicy
Condition: IsProdAccount
Properties:
PolicyName: DoorUnlockApiAccessLogDelivery
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSLogDeliveryWrite",
"Effect": "Allow",
"Principal": { "Service": "delivery.logs.amazonaws.com" },
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/apigateway/door-unlock-api",
"arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/apigateway/door-unlock-api:*"
],
"Condition": {
"StringEquals": {
"aws:SourceAccount": "${AWS::AccountId}"
}
}
}
]
}