mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-09-30 05:43:17 +00:00
feat(iam): add external-dev backend Terraform substrate (#131)
* feat(iam): add external-dev backend terraform substrate * fix(iam): require boundaries for SHOC policy writes
This commit is contained in:
parent
ee233379dd
commit
dba0871587
7 changed files with 1232 additions and 23 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
cdk.out/
|
cdk.out*/
|
||||||
*.js
|
*.js
|
||||||
*.d.ts
|
*.d.ts
|
||||||
*.js.map
|
*.js.map
|
||||||
|
|
|
||||||
131
README.md
131
README.md
|
|
@ -18,7 +18,8 @@ like any other stack.
|
||||||
> only) until 2026-07-14, when the external-dev member baseline was merged in
|
> only) until 2026-07-14, when the external-dev member baseline was merged in
|
||||||
> and the repo renamed. Deployed CloudFormation stack names are unchanged.
|
> and the repo renamed. Deployed CloudFormation stack names are unchanged.
|
||||||
|
|
||||||
Stacks (deployed by the CD workflow — one job per target account):
|
Stacks (normally deployed by one CD job per target account; staged exceptions
|
||||||
|
are noted):
|
||||||
|
|
||||||
| Stack | Account | Region | Purpose |
|
| Stack | Account | Region | Purpose |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|
@ -30,6 +31,7 @@ Stacks (deployed by the CD workflow — one job per target account):
|
||||||
| `seahaven-backup-offsite` | 328440206208 | us-west-2 | Governance-locked offsite copy vault (C-7) |
|
| `seahaven-backup-offsite` | 328440206208 | us-west-2 | Governance-locked offsite copy vault (C-7) |
|
||||||
| `seahaven-org-governance` | 328440206208 | us-east-1 | AWS Organizations OU tree + SCPs (incl. the cdk-imported external-dev guardrails) |
|
| `seahaven-org-governance` | 328440206208 | us-east-1 | AWS Organizations OU tree + SCPs (incl. the cdk-imported external-dev guardrails) |
|
||||||
| `seahaven-external-dev-baseline` | 396287094661 | us-east-1 | Member-account baseline: Config, GuardDuty, Security Hub (FSBP + CIS v3.0), Access Analyzer, flow logs, budget |
|
| `seahaven-external-dev-baseline` | 396287094661 | us-east-1 | Member-account baseline: Config, GuardDuty, Security Hub (FSBP + CIS v3.0), Access Analyzer, flow logs, budget |
|
||||||
|
| `seahaven-terraform-substrate` | 396287094661 | us-east-1 | Staged manually until SHOC role imports complete; HCP roles/deploy boundaries for the backend rehearsal, referencing the existing OIDC provider |
|
||||||
| `seahaven-security-baseline` | 001520130573 | us-east-1 | Member-account baseline for the delegated security-admin account (same construct set) |
|
| `seahaven-security-baseline` | 001520130573 | us-east-1 | Member-account baseline for the delegated security-admin account (same construct set) |
|
||||||
| `seahaven-dev-baseline` | 710827005802 | us-east-1 | Member-account baseline for internal dev/staging (org-managed detection — no local GuardDuty/SecurityHub) |
|
| `seahaven-dev-baseline` | 710827005802 | us-east-1 | Member-account baseline for internal dev/staging (org-managed detection — no local GuardDuty/SecurityHub) |
|
||||||
| `seahaven-prod-baseline` | 011934824531 | us-east-1 | Member-account baseline for production workloads (org-managed detection; mgmt account frozen for new workloads) |
|
| `seahaven-prod-baseline` | 011934824531 | us-east-1 | Member-account baseline for production workloads (org-managed detection; mgmt account frozen for new workloads) |
|
||||||
|
|
@ -67,6 +69,7 @@ the TypeScript source — no separate compile step needed for `cdk synth` /
|
||||||
| `prod-baseline` | `seahaven-prod-baseline` | 011934824531 | us-east-1 | `lib/member-baseline-stack.ts` (orgManagedDetection) |
|
| `prod-baseline` | `seahaven-prod-baseline` | 011934824531 | us-east-1 | `lib/member-baseline-stack.ts` (orgManagedDetection) |
|
||||||
| `deploy-substrate-prod` | `seahaven-deploy-substrate` | 011934824531 | us-east-1 | `lib/deploy-substrate-stack.ts` |
|
| `deploy-substrate-prod` | `seahaven-deploy-substrate` | 011934824531 | us-east-1 | `lib/deploy-substrate-stack.ts` |
|
||||||
| `deploy-substrate-dev` | `seahaven-deploy-substrate` | 710827005802 | us-east-1 | `lib/deploy-substrate-stack.ts` |
|
| `deploy-substrate-dev` | `seahaven-deploy-substrate` | 710827005802 | us-east-1 | `lib/deploy-substrate-stack.ts` |
|
||||||
|
| `terraform-substrate-external-dev` | `seahaven-terraform-substrate` | 396287094661 | us-east-1 | `lib/terraform-substrate-stack.ts` |
|
||||||
| `dynamodb-cmk-prod` | `seahaven-dynamodb-cmk` | 011934824531 | us-east-1 | `lib/dynamodb-cmk-stack.ts` |
|
| `dynamodb-cmk-prod` | `seahaven-dynamodb-cmk` | 011934824531 | us-east-1 | `lib/dynamodb-cmk-stack.ts` |
|
||||||
| `alarm-topic-prod` | `seahaven-alarm-topic` | 011934824531 | us-east-1 | `lib/alarm-topic-stack.ts` |
|
| `alarm-topic-prod` | `seahaven-alarm-topic` | 011934824531 | us-east-1 | `lib/alarm-topic-stack.ts` |
|
||||||
| `app-web-acl-prod` | `seahaven-app-web-acl` | 011934824531 | us-east-1 | `lib/app-web-acl-stack.ts` |
|
| `app-web-acl-prod` | `seahaven-app-web-acl` | 011934824531 | us-east-1 | `lib/app-web-acl-stack.ts` |
|
||||||
|
|
@ -214,9 +217,11 @@ created with the boundary already attached.
|
||||||
|
|
||||||
`lib/terraform-substrate-stack.ts` + `lib/terraform-substrate/terraform-substrate.template.yaml`
|
`lib/terraform-substrate-stack.ts` + `lib/terraform-substrate/terraform-substrate.template.yaml`
|
||||||
deploy `seahaven-terraform-substrate` into each member account that hosts
|
deploy `seahaven-terraform-substrate` into each member account that hosts
|
||||||
Terraform-managed workloads (currently seahaven-prod and seahaven-dev; never
|
Terraform-managed workloads (currently seahaven-prod, seahaven-dev, and
|
||||||
mgmt — mgmt stays SAM until its stacks migrate out). It contains only the
|
external-dev; never mgmt — mgmt stays SAM until its stacks migrate out).
|
||||||
shared account-level plumbing:
|
Prod/dev use the shared IAM-management policy. External-dev references its
|
||||||
|
existing `app.terraform.io` provider and carries only exact SHOC
|
||||||
|
import/adoption roles:
|
||||||
|
|
||||||
- the `app.terraform.io` OIDC identity provider (audience
|
- the `app.terraform.io` OIDC identity provider (audience
|
||||||
`aws.workload.identity`; Retain — it is the federation anchor for every
|
`aws.workload.identity`; Retain — it is the federation anchor for every
|
||||||
|
|
@ -226,7 +231,19 @@ shared account-level plumbing:
|
||||||
`seahaven-lambda-execution-boundary` allow-list owned by the
|
`seahaven-lambda-execution-boundary` allow-list owned by the
|
||||||
deploy-substrate stack — hence the explicit stack dependency in
|
deploy-substrate stack — hence the explicit stack dependency in
|
||||||
`bin/app.ts`) plus the `DenyBoundaryTampering` / `DenyBoundaryPolicyEdit`
|
`bin/app.ts`) plus the `DenyBoundaryTampering` / `DenyBoundaryPolicyEdit`
|
||||||
/ `DenySelfMutation` backstops.
|
/ `DenySelfMutation` backstops,
|
||||||
|
- external-dev-only deploy boundaries
|
||||||
|
`shoc-backend-{tf-poc,dev,staging}-deploy-boundary`. Each is the maximum
|
||||||
|
current policy for one exact `githubdeploy-shoc-backend-*` role. Dev
|
||||||
|
temporarily retains its live broad `elasticbeanstalk-*` S3 grants so
|
||||||
|
boundary attachment cannot regress deployment before the separately
|
||||||
|
reviewed policy narrowing,
|
||||||
|
- external-dev-only runtime boundaries
|
||||||
|
`shoc-backend-{tf-poc,dev,staging}-runtime-boundary`. These retain only the
|
||||||
|
account-scoped S3, environment health/log, and X-Ray portions of
|
||||||
|
`AWSElasticBeanstalkWebTier`, plus each environment's exact secrets/KMS/STS
|
||||||
|
data plane. They deliberately exclude the managed policy's 2026
|
||||||
|
Bedrock/Marketplace additions.
|
||||||
|
|
||||||
**This policy derives from `seahaven-cfn-exec-iam-management` but is
|
**This policy derives from `seahaven-cfn-exec-iam-management` but is
|
||||||
deliberately stricter — it is not a mirror.** The 2026-07-30 security review
|
deliberately stricter — it is not a mirror.** The 2026-07-30 security review
|
||||||
|
|
@ -255,6 +272,110 @@ deliberately NOT pre-provisioned — they are appended to the template at each
|
||||||
stack's migration time so an account never carries trust for workspaces that
|
stack's migration time so an account never carries trust for workspaces that
|
||||||
do not deploy to it.
|
do not deploy to it.
|
||||||
|
|
||||||
|
**External-dev SHOC role adoption is a staged CloudFormation import, not a
|
||||||
|
normal first deploy.** Exactly four roles exist today:
|
||||||
|
`hcptf-shoc-backend-{dev,staging}` and their `-plan` partners. The tf-poc pair
|
||||||
|
does not exist. Two independent CDK contexts make each transition explicit:
|
||||||
|
`enableShocBackendPocRoles` and `enableShocBackendLiveRoles`. Both are
|
||||||
|
version-controlled as `false` in `cdk.json` for the initial rollout.
|
||||||
|
`terraform-substrate-external-dev` is deliberately absent from the automatic
|
||||||
|
external-dev deploy job during this sequence; `external-dev-baseline` remains
|
||||||
|
automatic and unchanged.
|
||||||
|
|
||||||
|
1. Create the role-free base stack:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx cdk deploy terraform-substrate-external-dev \
|
||||||
|
-c enableShocBackendPocRoles=false \
|
||||||
|
-c enableShocBackendLiveRoles=false
|
||||||
|
```
|
||||||
|
|
||||||
|
`CreateOIDCProvider=false` is fixed in `bin/app.ts`; the external-dev
|
||||||
|
account therefore creates neither the existing provider, SHOC roles, nor
|
||||||
|
the prod/dev-only shared IAM policy. The base stack does create all six
|
||||||
|
retained external-dev deploy/runtime boundary policies.
|
||||||
|
2. Set `enableShocBackendPocRoles` to `true` in `cdk.json`, leave the live gate
|
||||||
|
`false`, review the synthesized two-role addition, then run the normal
|
||||||
|
external-dev stack update. This creates only the new tf-poc HCP plan/apply
|
||||||
|
pair. The retained POC CDK stack references
|
||||||
|
`shoc-backend-tf-poc-deploy-boundary` when it creates
|
||||||
|
`githubdeploy-shoc-backend-tf-poc` and
|
||||||
|
`shoc-backend-tf-poc-runtime-boundary` when it creates the POC runtime
|
||||||
|
role; do not attach the generic account execution boundary to either role.
|
||||||
|
3. Prove both tf-poc HCP assumptions and the retained POC import rehearsal
|
||||||
|
before touching the live-role ownership boundary.
|
||||||
|
4. In a separately approved administrator/CDK migration, tag the existing HCP
|
||||||
|
apply roles first:
|
||||||
|
`hcptf-shoc-backend-dev` gets
|
||||||
|
`HcpTerraformWorkspace=shoc-backend-dev`, and
|
||||||
|
`hcptf-shoc-backend-staging` gets
|
||||||
|
`HcpTerraformWorkspace=shoc-backend-staging`. Next attach
|
||||||
|
`shoc-backend-dev-deploy-boundary` and
|
||||||
|
`shoc-backend-staging-deploy-boundary` to the exact `githubdeploy-*` roles,
|
||||||
|
and attach `shoc-backend-dev-runtime-boundary` /
|
||||||
|
`shoc-backend-staging-runtime-boundary` to the exact runtime roles. Verify
|
||||||
|
the boundary ceilings before adding the matching manager tag to either
|
||||||
|
target `githubdeploy-*` role. The POC CDK
|
||||||
|
creates its deploy role with `HcpTerraformWorkspace=shoc-backend-tf-poc`;
|
||||||
|
the substrate-created POC apply role already carries the same principal
|
||||||
|
tag. Verify each effective deployment action before continuing. HCP remains
|
||||||
|
blocked while a target tag is missing/different or the target lacks its
|
||||||
|
exact dedicated boundary, so a partial migration cannot authorize policy
|
||||||
|
writes. Complete both runtime/deploy boundary attachments before workload
|
||||||
|
imports. HCP apply roles deliberately have no
|
||||||
|
`iam:PutRolePermissionsBoundary` or boundary-policy mutation permissions.
|
||||||
|
5. In the backend bootstrap, add Terraform `removed` blocks with
|
||||||
|
`destroy = false` for only the four dev/staging HCP roles and their inline
|
||||||
|
policies. Apply and verify Terraform state no longer owns them while all
|
||||||
|
four physical roles and ARNs remain unchanged.
|
||||||
|
6. Set both contexts to `true`, synthesize with
|
||||||
|
`npx cdk synth terraform-substrate-external-dev`, and create a
|
||||||
|
CloudFormation **IMPORT** change set for the four existing
|
||||||
|
`AWS::IAM::Role` resources by exact role name. Do not run a normal
|
||||||
|
CREATE/UPDATE change set for this ownership transition. The POC gate must
|
||||||
|
remain true so the already-managed pair stays in the template. Import
|
||||||
|
records ownership; it does not update existing role properties or inline
|
||||||
|
policies.
|
||||||
|
7. Run a separate, reviewed CloudFormation reconcile update after import and
|
||||||
|
before switching workspace credentials. Each current live role has one
|
||||||
|
inline policy: `shoc-backend-dev-import-plan`,
|
||||||
|
`shoc-backend-dev-import-apply`,
|
||||||
|
`shoc-backend-staging-import-plan`, or
|
||||||
|
`shoc-backend-staging-import-apply`. Existing descriptions and tags are
|
||||||
|
inventoried in the backend handoff. Reconcile those explicit differences
|
||||||
|
to the final baseline shape without replacing a role.
|
||||||
|
8. After reconcile and HCP assumption proof, keep both context values committed
|
||||||
|
as `true`. Every subsequent normal deployment must synthesize all six
|
||||||
|
roles. Never return either gate to false as a rollback mechanism; Retain
|
||||||
|
protects the physical role but removing it from the stack abandons
|
||||||
|
CloudFormation ownership.
|
||||||
|
9. Only after all imports/reconciliation complete and both context defaults
|
||||||
|
are permanently `true`, add `terraform-substrate-external-dev` back to the
|
||||||
|
external-dev workflow stack selector. Until then all substrate operations
|
||||||
|
are deliberate manual deploy/import actions.
|
||||||
|
10. Retire the backend bootstrap only after the POC pair and all four imported
|
||||||
|
live roles are proven under this stack. Role deletion/recreation is never a
|
||||||
|
migration step.
|
||||||
|
|
||||||
|
The external-dev apply roles intentionally omit role create/delete,
|
||||||
|
managed-policy attach/detach, trust or boundary mutation, `iam:PassRole`, and
|
||||||
|
secret-value APIs. IAM writes are limited to exact-role inline-policy and
|
||||||
|
ordinary tag updates plus exact-profile tags; role descriptions remain stable
|
||||||
|
and HCP receives no `UpdateRole` or `UpdateRoleDescription`. The SCP permits
|
||||||
|
only the three enumerated HCP apply roles to mutate a `githubdeploy-*` role
|
||||||
|
whose locked `HcpTerraformWorkspace` resource tag equals the caller's immutable
|
||||||
|
principal tag. Adding or changing that manager tag remains administrator/CDK
|
||||||
|
only. POC DNS and certificate access is tag/name constrained because their
|
||||||
|
physical IDs are allocated by the temporary retained CDK stack before
|
||||||
|
Terraform imports them. Dev and staging DNS writes are pinned to their existing
|
||||||
|
hosted-zone IDs and API record names.
|
||||||
|
|
||||||
|
Current compact policy-document sizes are 1,387 / 1,873 / 1,844 characters for
|
||||||
|
the POC/dev/staging deploy boundaries and 1,176 / 1,779 / 1,656 for their
|
||||||
|
runtime boundaries, each below IAM's 6,144-character managed-policy limit. The
|
||||||
|
external-dev IAM guardrail SCP is 4,922 compact characters against its
|
||||||
|
5,120-character Organizations limit; keep size assertions in every change.
|
||||||
|
|
||||||
**HCP Terraform layout (org-level setup, console):** one org `seahaven`
|
**HCP Terraform layout (org-level setup, console):** one org `seahaven`
|
||||||
(free tier: 500 managed resources, 1 concurrent run); one HCP **project per
|
(free tier: 500 managed resources, 1 concurrent run); one HCP **project per
|
||||||
AWS account** (`seahaven-prod`, `seahaven-dev`); one **workspace per stack**
|
AWS account** (`seahaven-prod`, `seahaven-dev`); one **workspace per stack**
|
||||||
|
|
|
||||||
28
bin/app.ts
28
bin/app.ts
|
|
@ -31,6 +31,13 @@ const PROD_VPC_IDS = [
|
||||||
|
|
||||||
const app = new cdk.App();
|
const app = new cdk.App();
|
||||||
|
|
||||||
|
const contextBoolean = (key: string): boolean => {
|
||||||
|
const value = app.node.tryGetContext(key);
|
||||||
|
if (value === true || value === "true") return true;
|
||||||
|
if (value === false || value === "false" || value === undefined) return false;
|
||||||
|
throw new Error(`${key} must be true or false`);
|
||||||
|
};
|
||||||
|
|
||||||
new AccountBaselineStack(app, "account-baseline", {
|
new AccountBaselineStack(app, "account-baseline", {
|
||||||
stackName: "seahaven-account-baseline",
|
stackName: "seahaven-account-baseline",
|
||||||
env: { account: ACCOUNT, region: "us-east-1" },
|
env: { account: ACCOUNT, region: "us-east-1" },
|
||||||
|
|
@ -190,8 +197,10 @@ const deploySubstrateDev = new DeploySubstrateStack(app, "deploy-substrate-dev",
|
||||||
// OIDC provider + the shared boundary-gated guardrail policy
|
// OIDC provider + the shared boundary-gated guardrail policy
|
||||||
// (seahaven-hcptf-iam-management) that per-workspace apply roles attach.
|
// (seahaven-hcptf-iam-management) that per-workspace apply roles attach.
|
||||||
// Per-workspace hcptf-* roles are appended to the template at each stack's
|
// Per-workspace hcptf-* roles are appended to the template at each stack's
|
||||||
// migration time, never here. prod/dev ONLY — mgmt stays SAM (Terraform POC
|
// migration time, never here. prod/dev/external-dev ONLY — mgmt stays SAM
|
||||||
// decision 2026-07-30; the mgmt POC substrate was rolled back the same day).
|
// (Terraform POC decision 2026-07-30; the mgmt POC substrate was rolled back
|
||||||
|
// the same day). External-dev references its existing provider and uses
|
||||||
|
// workload-specific inline policies instead of the shared IAM manager.
|
||||||
// The guardrail policy names the seahaven-lambda-execution-boundary ARN only
|
// The guardrail policy names the seahaven-lambda-execution-boundary ARN only
|
||||||
// inside Condition strings, so CFN infers no creation edge — the explicit
|
// inside Condition strings, so CFN infers no creation edge — the explicit
|
||||||
// dependency below guarantees the deploy-substrate stack (which owns the
|
// dependency below guarantees the deploy-substrate stack (which owns the
|
||||||
|
|
@ -227,6 +236,21 @@ const terraformSubstrateDev = new TerraformSubstrateStack(
|
||||||
);
|
);
|
||||||
terraformSubstrateDev.addStackDependency(deploySubstrateDev);
|
terraformSubstrateDev.addStackDependency(deploySubstrateDev);
|
||||||
|
|
||||||
|
// External-dev already has app.terraform.io federation. Both role gates start
|
||||||
|
// false in cdk.json: POC is enabled by a normal update; dev/staging only by
|
||||||
|
// CloudFormation import after Terraform relinquishes those four live roles.
|
||||||
|
const terraformSubstrateExternalDev = new TerraformSubstrateStack(
|
||||||
|
app,
|
||||||
|
"terraform-substrate-external-dev",
|
||||||
|
{
|
||||||
|
stackName: "seahaven-terraform-substrate",
|
||||||
|
env: { account: EXTERNAL_DEV_ACCOUNT, region: "us-east-1" },
|
||||||
|
createOidcProvider: false,
|
||||||
|
enableShocBackendPocRoles: contextBoolean("enableShocBackendPocRoles"),
|
||||||
|
enableShocBackendLiveRoles: contextBoolean("enableShocBackendLiveRoles"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// ── Shared DynamoDB CMK (INFRA-95 / M-3) ─────────────────────────────────────
|
// ── Shared DynamoDB CMK (INFRA-95 / M-3) ─────────────────────────────────────
|
||||||
// Dedicated, standalone stack so the customer-managed key for sensitive
|
// Dedicated, standalone stack so the customer-managed key for sensitive
|
||||||
// finance/PII DynamoDB tables is an independent shared dependency for the owning
|
// finance/PII DynamoDB tables is an independent shared dependency for the owning
|
||||||
|
|
|
||||||
4
cdk.json
4
cdk.json
|
|
@ -17,6 +17,8 @@
|
||||||
"context": {
|
"context": {
|
||||||
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
||||||
"@aws-cdk/core:checkSecretUsage": true,
|
"@aws-cdk/core:checkSecretUsage": true,
|
||||||
"@aws-cdk/core:target-partitions": ["aws"]
|
"@aws-cdk/core:target-partitions": ["aws"],
|
||||||
|
"enableShocBackendPocRoles": false,
|
||||||
|
"enableShocBackendLiveRoles": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Sid": "ProtectBoundaryPolicyFromEdits",
|
"Sid": "ProtectExecutionBoundary",
|
||||||
"Effect": "Deny",
|
"Effect": "Deny",
|
||||||
"Action": ["iam:CreatePolicyVersion", "iam:SetDefaultPolicyVersion", "iam:DeletePolicy", "iam:DeletePolicyVersion"],
|
"Action": ["iam:CreatePolicyVersion", "iam:SetDefaultPolicyVersion", "iam:DeletePolicy", "iam:DeletePolicyVersion"],
|
||||||
"Resource": "arn:aws:iam::396287094661:policy/external-dev-execution-boundary"
|
"Resource": "arn:aws:iam::396287094661:policy/external-dev-execution-boundary"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Sid": "ProtectShocBoundaries",
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": ["iam:CreatePolicyVersion", "iam:SetDefaultPolicyVersion", "iam:DeletePolicy", "iam:DeletePolicyVersion"],
|
||||||
|
"Resource": "arn:aws:iam::396287094661:policy/shoc-backend-*-boundary",
|
||||||
|
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*"] } }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Sid": "DenyAlteringPermissionsBoundaries",
|
"Sid": "DenyAlteringPermissionsBoundaries",
|
||||||
"Effect": "Deny",
|
"Effect": "Deny",
|
||||||
|
|
@ -32,18 +39,61 @@
|
||||||
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole"] } }
|
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole"] } }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Sid": "ProtectPrivilegedRoles",
|
"Sid": "ProtectNonGithubPrivilegedRoles",
|
||||||
"Effect": "Deny",
|
"Effect": "Deny",
|
||||||
"Action": ["iam:UpdateAssumeRolePolicy", "iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:PutRolePolicy", "iam:DeleteRolePolicy", "iam:DeleteRole", "iam:UpdateRole", "iam:TagRole", "iam:UntagRole"],
|
"Action": ["iam:UpdateAssumeRolePolicy", "iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:PutRolePolicy", "iam:DeleteRolePolicy", "iam:DeleteRole", "iam:UpdateRole", "iam:TagRole", "iam:UntagRole"],
|
||||||
"Resource": [
|
"Resource": [
|
||||||
"arn:aws:iam::396287094661:role/OrganizationAccountAccessRole",
|
"arn:aws:iam::396287094661:role/OrganizationAccountAccessRole",
|
||||||
"arn:aws:iam::396287094661:role/cdk-hnb659fds-*",
|
"arn:aws:iam::396287094661:role/cdk-hnb659fds-*",
|
||||||
"arn:aws:iam::396287094661:role/githubdeploy-*",
|
|
||||||
"arn:aws:iam::396287094661:role/seahaven-extdev-config-recorder-role",
|
"arn:aws:iam::396287094661:role/seahaven-extdev-config-recorder-role",
|
||||||
"arn:aws:iam::396287094661:role/seahaven-extdev-config-custom-resource-role",
|
"arn:aws:iam::396287094661:role/seahaven-extdev-config-custom-resource-role",
|
||||||
"arn:aws:iam::396287094661:role/aws-service-role/*"
|
"arn:aws:iam::396287094661:role/aws-service-role/*"
|
||||||
],
|
],
|
||||||
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*"] } }
|
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*"] } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sid": "ProtectDeploymentPrincipalLifecycle",
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": ["iam:UpdateAssumeRolePolicy", "iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:DeleteRolePolicy", "iam:DeleteRole", "iam:UpdateRole", "iam:UpdateRoleDescription", "iam:PutRolePermissionsBoundary", "iam:DeleteRolePermissionsBoundary"],
|
||||||
|
"Resource": ["arn:aws:iam::396287094661:role/githubdeploy-*", "arn:aws:iam::396287094661:role/hcptf-*"],
|
||||||
|
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*"] } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sid": "ProtectGithubRoleMetadata",
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": ["iam:PutRolePolicy", "iam:TagRole", "iam:UntagRole"],
|
||||||
|
"Resource": "arn:aws:iam::396287094661:role/githubdeploy-*",
|
||||||
|
"Condition": { "ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*", "arn:aws:iam::396287094661:role/hcptf-shoc-backend-tf-poc", "arn:aws:iam::396287094661:role/hcptf-shoc-backend-dev", "arn:aws:iam::396287094661:role/hcptf-shoc-backend-staging"] } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sid": "DenyUnmanagedGithubRole",
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": ["iam:PutRolePolicy", "iam:TagRole", "iam:UntagRole"],
|
||||||
|
"Resource": "arn:aws:iam::396287094661:role/githubdeploy-*",
|
||||||
|
"Condition": {
|
||||||
|
"ArnLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/hcptf-shoc-backend-tf-poc", "arn:aws:iam::396287094661:role/hcptf-shoc-backend-dev", "arn:aws:iam::396287094661:role/hcptf-shoc-backend-staging"] },
|
||||||
|
"Null": { "aws:ResourceTag/HcpTerraformWorkspace": "true" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sid": "EnforceGithubRoleManagerTag",
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": ["iam:PutRolePolicy", "iam:TagRole", "iam:UntagRole"],
|
||||||
|
"Resource": "arn:aws:iam::396287094661:role/githubdeploy-*",
|
||||||
|
"Condition": {
|
||||||
|
"ArnLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/hcptf-shoc-backend-tf-poc", "arn:aws:iam::396287094661:role/hcptf-shoc-backend-dev", "arn:aws:iam::396287094661:role/hcptf-shoc-backend-staging"] },
|
||||||
|
"StringNotEquals": { "aws:ResourceTag/HcpTerraformWorkspace": "${aws:PrincipalTag/HcpTerraformWorkspace}" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sid": "LockHcpTerraformWorkspaceTag",
|
||||||
|
"Effect": "Deny",
|
||||||
|
"Action": ["iam:TagRole", "iam:UntagRole"],
|
||||||
|
"Resource": "arn:aws:iam::396287094661:role/*",
|
||||||
|
"Condition": {
|
||||||
|
"ArnNotLike": { "aws:PrincipalArn": ["arn:aws:iam::396287094661:role/OrganizationAccountAccessRole", "arn:aws:iam::396287094661:role/cdk-hnb659fds-*"] },
|
||||||
|
"ForAnyValue:StringEquals": { "aws:TagKeys": "HcpTerraformWorkspace" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,26 @@ export interface TerraformSubstrateStackProps extends cdk.StackProps {
|
||||||
* remove the orphaned provider or redeploy with this false.
|
* remove the orphaned provider or redeploy with this false.
|
||||||
*/
|
*/
|
||||||
createOidcProvider?: boolean;
|
createOidcProvider?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the two SHOC backend tf-poc HCP roles. Defaults false so the
|
||||||
|
* external-dev base-stack create is role-free.
|
||||||
|
*/
|
||||||
|
enableShocBackendPocRoles?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the four existing SHOC backend dev/staging HCP roles. Defaults
|
||||||
|
* false because these names must enter the stack through CloudFormation
|
||||||
|
* resource import, never a normal create/update.
|
||||||
|
*/
|
||||||
|
enableShocBackendLiveRoles?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-account HCP Terraform deploy substrate: the shared account-level
|
* Per-account HCP Terraform deploy substrate: the conditional
|
||||||
* resources every Terraform workspace pipeline needs -
|
* app.terraform.io OIDC provider, the prod/dev shared boundary-gated IAM
|
||||||
* - app.terraform.io OIDC identity provider (conditional, see props), and
|
* manager, and reviewed per-workspace role pairs. External-dev conditions out
|
||||||
* - `seahaven-hcptf-iam-management`, the shared boundary-gated IAM
|
* the shared manager and uses exact inline policies for its SHOC import roles.
|
||||||
* guardrail policy every per-workspace APPLY role attaches.
|
|
||||||
*
|
*
|
||||||
* Deliberately NOT here: per-workspace hcptf-<stack> / hcptf-<stack>-plan
|
* Deliberately NOT here: per-workspace hcptf-<stack> / hcptf-<stack>-plan
|
||||||
* roles. Those are appended to the template at each stack's migration time
|
* roles. Those are appended to the template at each stack's migration time
|
||||||
|
|
@ -58,6 +70,10 @@ export class TerraformSubstrateStack extends cdk.Stack {
|
||||||
),
|
),
|
||||||
parameters: {
|
parameters: {
|
||||||
CreateOIDCProvider: props?.createOidcProvider === false ? "false" : "true",
|
CreateOIDCProvider: props?.createOidcProvider === false ? "false" : "true",
|
||||||
|
EnableShocBackendPocRoles:
|
||||||
|
props?.enableShocBackendPocRoles === true ? "true" : "false",
|
||||||
|
EnableShocBackendLiveRoles:
|
||||||
|
props?.enableShocBackendLiveRoles === true ? "true" : "false",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue