Compare commits

...

2 commits

Author SHA1 Message Date
Adam Moussa
69f31842cb
feat(iam): add hcptf roles for sh-openswe-traces-prod (PLAT-73) (#80)
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 hcptf roles for sh-openswe-traces-prod

Storage/IAM-user apply and plan roles for the HCP workspace. No Lambda
boundary widen; explicit IAM user CRUD because hcptf-iam-management is
role-path-only.

* fix(iam): pin CreateSecret to exact export secret name

Remove CreateSecret and UpdateSecret from the ARN-prefix shell grant so
apply cannot create longer-named secrets or overwrite SecretString.
2026-08-05 22:46:32 +00:00
Adam Moussa
fc64b03e3d
feat(iam): hcptf front-integrations roles and boundary (PLAT-72) (#81)
* feat(iam): add hcptf front-integrations roles and boundary widen

Add plan/apply OIDC roles for front-integrations-prod and widen the
Lambda execution boundary with exact prod secret ARNs plus DynamoDB
CRUD on front-sla-alerts.

* fix(iam): restrict front-integrations plan role to lambda Get/List

Keep mutate APIs on the apply role so a compromised plan-phase
OIDC session cannot update or delete front-* functions.
2026-08-05 18:33:31 -04:00
2 changed files with 435 additions and 4 deletions

View file

@ -507,11 +507,46 @@ Resources:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:afi-slack-webhook-T4oR3G
- !Ref AWS::NoValue
# ── front-integrations (PLAT-72) — prod-only exact secret ARNs + DDB ─
# Derived from live SAM template + secrets created in seahaven-prod
# 2026-08-05. No secret:front-integrations/* patterns.
- !If
- IsProdAccount
- Sid: FrontIntegrationsSecrets
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/front-api-token-UXKv0U
- arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/slack-bot-token-giGfA7
- arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/google-service-account-dqv3Bo
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: FrontIntegrationsDynamoDB
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/front-sla-alerts"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts/index/*"
- !Ref AWS::NoValue
# ── FURTHER PER-WORKLOAD DATA-PLANE STATEMENTS ──────────────────────
# Floor above + afi-backup-monitor widening (PLAT-56). Additional
# stacks add their own statements here, derived from THEIR OWN
# template, in their own PR, deployed to UPDATE_COMPLETE before first
# workload deploy. WIDENING PATH in the header still governs.
# Floor above + afi-backup-monitor (PLAT-56) + front-integrations
# (PLAT-72). Additional stacks add their own statements here, derived
# from THEIR OWN template, in their own PR, deployed to
# UPDATE_COMPLETE before first workload deploy. WIDENING PATH in the
# header still governs.
#
# WHY the floor stayed empty of data-plane (decided 2026-07-30, Adam):
# The security win of INFRA-186 comes from DELETION, not enumeration.

View file

@ -545,3 +545,399 @@ Resources:
Resource:
- !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}"
- !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}/*"
# ---------------------------------------------------------------------------
# front-integrations (PLAT-72) — plan + apply roles for workspace
# front-integrations-prod. Copy shape from afi-backup-monitor above; extend
# for DynamoDB table front-sla-alerts, CloudWatch alarms, and site-alerts SNS.
# ---------------------------------------------------------------------------
HcptfFrontIntegrationsPlanRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-front-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:front-integrations-prod:run_phase:plan
ManagedPolicyArns:
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
Policies:
- PolicyName: front-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/front-*"
- 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/front-*"
- Sid: RefreshLambda
Effect: Allow
Action:
# Read-only refresh for plan; mutate APIs stay on the apply role.
- lambda:Get*
- lambda:List*
Resource:
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:front-*"
- Sid: RefreshArtifactsBucket
Effect: Allow
Action:
- s3:Get*
- s3:ListBucket
Resource:
- !Sub "arn:aws:s3:::front-integrations-artifacts-${AWS::AccountId}"
- !Sub "arn:aws:s3:::front-integrations-artifacts-${AWS::AccountId}/*"
- Sid: RefreshDynamoDB
Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:DescribeTimeToLive
- dynamodb:DescribeContinuousBackups
- dynamodb:ListTagsOfResource
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts"
- Sid: RefreshCloudWatchAlarms
Effect: Allow
Action:
- cloudwatch:DescribeAlarms
- cloudwatch:ListTagsForResource
Resource:
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:front-*"
- Sid: RefreshLogs
Effect: Allow
Action:
- logs:DescribeLogGroups
- logs:ListTagsForResource
Resource: "*"
HcptfFrontIntegrationsApplyRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-front-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:front-integrations-prod:run_phase:apply
ManagedPolicyArns:
- !Ref HcptfIamManagementPolicy
Policies:
- PolicyName: front-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:front-*"
- Sid: LambdaList
Effect: Allow
Action:
- lambda:ListFunctions
- lambda:ListLayers
- lambda:GetAccountSettings
Resource: "*"
- Sid: EventBridgeRules
Effect: Allow
Action:
- events:*
Resource:
- !Sub "arn:aws:events:us-east-1:${AWS::AccountId}:rule/front-*"
- 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/front-*"
- Sid: CloudWatchLogsDescribe
Effect: Allow
Action:
- logs:DescribeLogGroups
Resource: "*"
- Sid: LambdaArtifactsBucket
Effect: Allow
Action:
- s3:*
Resource:
- !Sub "arn:aws:s3:::front-integrations-artifacts-${AWS::AccountId}"
- !Sub "arn:aws:s3:::front-integrations-artifacts-${AWS::AccountId}/*"
- Sid: DynamoDBTable
Effect: Allow
Action:
- dynamodb:*
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts/index/*"
- Sid: DynamoDBList
Effect: Allow
Action:
- dynamodb:ListTables
Resource: "*"
- Sid: CloudWatchAlarms
Effect: Allow
Action:
- cloudwatch:*
Resource:
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:front-*"
- Sid: SiteAlertsSns
Effect: Allow
Action:
- sns:Publish
- sns:GetTopicAttributes
Resource:
- !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts"
# ---------------------------------------------------------------------------
# Per-workspace hcptf-* roles for sh-openswe-traces-prod (PLAT-73).
#
# Storage / IAM-user stack — NOT Lambda/EventBridge. Deviations from the
# Lambda apply-role pattern (documented on PLAT-73):
# - No seahaven-lambda-execution-boundary widen (no Lambda exec roles).
# - No lambda:*/events:*/artifact-bucket statements.
# - Explicit IAM user CRUD (seahaven-hcptf-iam-management is role-path-only).
# - Stack-scoped s3:* on account-suffixed data + log buckets.
# - KMS manage for alias/sh-openswe-traces CMK.
# - Secrets Manager shell lifecycle on exact secret name (no Get/Put value).
# ---------------------------------------------------------------------------
HcptfShOpensweTracesPlanRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-sh-openswe-traces-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:sh-openswe-traces-prod:run_phase:plan
ManagedPolicyArns:
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
Policies:
- PolicyName: sh-openswe-traces-plan-refresh
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: RefreshIamUser
Effect: Allow
Action:
- iam:GetUser
- iam:GetUserPolicy
- iam:ListUserPolicies
- iam:ListAttachedUserPolicies
- iam:ListUserTags
- iam:GetAccessKeyLastUsed
- iam:ListAccessKeys
Resource:
- !Sub "arn:aws:iam::${AWS::AccountId}:user/sh-openswe-langsmith-export"
- Sid: RefreshManagedPolicies
Effect: Allow
Action:
- iam:GetPolicy
- iam:GetPolicyVersion
Resource: "*"
- Sid: RefreshBuckets
Effect: Allow
Action:
- s3:Get*
- s3:ListBucket
Resource:
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}"
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}"
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}/*"
- Sid: RefreshKms
Effect: Allow
Action:
- kms:Describe*
- kms:GetKeyPolicy
- kms:GetKeyRotationStatus
- kms:ListResourceTags
- kms:ListAliases
Resource: "*"
- Sid: RefreshSecret
Effect: Allow
Action:
- secretsmanager:DescribeSecret
- secretsmanager:GetResourcePolicy
- secretsmanager:ListSecretVersionIds
Resource:
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:sh-openswe/langsmith-export-s3-*"
HcptfShOpensweTracesApplyRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-sh-openswe-traces
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:sh-openswe-traces-prod:run_phase:apply
ManagedPolicyArns:
- !Ref HcptfIamManagementPolicy
Policies:
- PolicyName: sh-openswe-traces-services
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: TracesBuckets
Effect: Allow
Action:
- s3:*
Resource:
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}"
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}"
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}/*"
# CreateKey is account-level; pin via RequestTag matching the
# app provider default_tags (Project=sh-openswe-traces). Key
# admin after create requires the same ResourceTag — no
# unconstrained PutKeyPolicy/DisableKey on unrelated CMKs.
- Sid: TracesKmsCreate
Effect: Allow
Action:
- kms:CreateKey
Resource: "*"
Condition:
StringEquals:
"aws:RequestTag/Project": sh-openswe-traces
- Sid: TracesKmsList
Effect: Allow
Action:
- kms:ListAliases
Resource: "*"
- Sid: TracesKmsAlias
Effect: Allow
Action:
- kms:CreateAlias
- kms:UpdateAlias
- kms:DeleteAlias
Resource:
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:alias/sh-openswe-traces"
- Sid: TracesKmsKey
Effect: Allow
Action:
- kms:TagResource
- kms:UntagResource
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
- kms:EnableKeyRotation
- kms:DisableKeyRotation
- kms:PutKeyPolicy
- kms:DescribeKey
- kms:GetKeyPolicy
- kms:GetKeyRotationStatus
- kms:ListResourceTags
- kms:EnableKey
- kms:DisableKey
# Alias attach/detach also authorizes against the key ARN.
- kms:CreateAlias
- kms:UpdateAlias
- kms:DeleteAlias
Resource: "*"
Condition:
StringEquals:
"aws:ResourceTag/Project": sh-openswe-traces
- Sid: ExportIamUser
Effect: Allow
Action:
- iam:CreateUser
- iam:DeleteUser
- iam:GetUser
- iam:TagUser
- iam:UntagUser
- iam:UpdateUser
- iam:PutUserPolicy
- iam:DeleteUserPolicy
- iam:GetUserPolicy
- iam:ListUserPolicies
- iam:ListAttachedUserPolicies
- iam:ListUserTags
- iam:ListAccessKeys
Resource:
- !Sub "arn:aws:iam::${AWS::AccountId}:user/sh-openswe-langsmith-export"
# CreateUser is authorized against the user ARN that will exist;
# ListUsers is a collection action on "*".
- Sid: ExportIamUserList
Effect: Allow
Action:
- iam:ListUsers
- iam:GetAccountSummary
Resource: "*"
# Shell lifecycle only — no GetSecretValue / PutSecretValue /
# UpdateSecret so apply never renders or overwrites key material
# in HCP state or run logs. CreateSecret is only on
# ExportSecretCreate with an exact Name pin (not this ARN
# prefix, which would also match longer secret names).
- Sid: ExportSecretShell
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:sh-openswe/langsmith-export-s3-*"
- Sid: ExportSecretCreate
Effect: Allow
Action:
- secretsmanager:CreateSecret
Resource: "*"
Condition:
StringEquals:
"secretsmanager:Name": sh-openswe/langsmith-export-s3