From 69f31842cbf233daebcef550acb38180a18dffbf Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Wed, 5 Aug 2026 18:46:32 -0400 Subject: [PATCH] feat(iam): add hcptf roles for sh-openswe-traces-prod (PLAT-73) (#80) * 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. --- .../terraform-substrate.template.yaml | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/lib/terraform-substrate/terraform-substrate.template.yaml b/lib/terraform-substrate/terraform-substrate.template.yaml index 2494263..acbbf81 100644 --- a/lib/terraform-substrate/terraform-substrate.template.yaml +++ b/lib/terraform-substrate/terraform-substrate.template.yaml @@ -727,3 +727,217 @@ Resources: - 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