diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index cdb57a9..e70cfd5 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -41,3 +41,12 @@ jobs: stack-name: "seahaven-security-baseline" secrets: deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN_SECURITY }} + + deploy-dev: + uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@3cde673b9d05c0e68aac4d997d582f2543853d20 # main + with: + node-version: "24" + stacks: "dev-baseline" + stack-name: "seahaven-dev-baseline" + secrets: + deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN_DEV }} diff --git a/README.md b/README.md index 78ce524..0d54e7e 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,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-external-dev-baseline` | 396287094661 | us-east-1 | Member-account baseline: Config, GuardDuty, Security Hub (FSBP + CIS v3.0), Access Analyzer, flow logs, budget | | `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) | ## CDK app @@ -59,6 +60,7 @@ the TypeScript source — no separate compile step needed for `cdk synth` / | `backup` | `seahaven-backup` | 328440206208 | us-east-1 | `lib/backup-stack.ts` | | `external-dev-baseline` | `seahaven-external-dev-baseline` | 396287094661 | us-east-1 | `lib/member-baseline-stack.ts` | | `security-baseline` | `seahaven-security-baseline` | 001520130573 | us-east-1 | `lib/member-baseline-stack.ts` | +| `dev-baseline` | `seahaven-dev-baseline` | 710827005802 | us-east-1 | `lib/member-baseline-stack.ts` (orgManagedDetection) | The member-account stack (`external-dev-baseline`) deploys with credentials for **396287094661** — the CD workflow runs it as a separate job assuming that diff --git a/bin/app.ts b/bin/app.ts index 813ee3f..3e3f7dd 100644 --- a/bin/app.ts +++ b/bin/app.ts @@ -12,6 +12,7 @@ import { OrgGovernanceStack } from "../lib/org-governance-stack"; const ACCOUNT = "328440206208"; const EXTERNAL_DEV_ACCOUNT = "396287094661"; const SECURITY_ACCOUNT = "001520130573"; +const DEV_ACCOUNT = "710827005802"; // All 5 VPCs in 328440206208 / us-east-1 (4 custom + default), audit Agent 7. // Index-derived logical IDs — append only, never reorder. @@ -100,6 +101,26 @@ new MemberBaselineStack(app, "security-baseline", { managedByTag: "seahaven-org-baseline", }); +// ── Member-account baseline: seahaven-dev (Phase 4) ───────────────────────── +// Internal dev/staging workloads (NOT the external-dev engagement account). +// Created 2026-07-14 AFTER org delegation went live, so GuardDuty/SecurityHub/ +// analyzer are org-managed (auto-enrollment verified) — orgManagedDetection +// keeps this stack from creating colliding local duplicates. Same lifecycle +// rule as the other new accounts: root-harden at org ROOT, then move-account +// into the nonprod OU (ou-nbuj-zpt5ka98). +new MemberBaselineStack(app, "dev-baseline", { + stackName: "seahaven-dev-baseline", + env: { account: DEV_ACCOUNT, region: "us-east-1" }, + namePrefix: "seahaven-dev", + monthlyBudgetUsd: 150, + budgetAlertEmail: "aws@seahaven.com", + ownerEmail: "adam@seahaven.com", + // Default VPC kept (dev runs real workloads) — logged from day one. + flowLogVpcIds: ["vpc-08f07dc5edeea621f"], + managedByTag: "seahaven-org-baseline", + orgManagedDetection: true, +}); + // ── Shared DynamoDB CMK (INFRA-95 / M-3) ───────────────────────────────────── // Dedicated, standalone stack so the customer-managed key for sensitive // finance/PII DynamoDB tables is an independent shared dependency for the owning diff --git a/lib/detective-controls.ts b/lib/detective-controls.ts index a3fa3bd..f8d0c6f 100644 --- a/lib/detective-controls.ts +++ b/lib/detective-controls.ts @@ -31,6 +31,16 @@ export interface DetectiveControlsProps { * custom resources; keep it stable per account. */ readonly namePrefix: string; + /** + * Create the account-local GuardDuty detector / Security Hub hub + standards / + * account Access Analyzer. Default TRUE (pre-delegation accounts own these). + * Set FALSE for accounts created AFTER org delegation went live (2026-07-14): + * GuardDuty/Security Hub auto-enroll new members and CREATE these resources + * themselves — a CFN-owned duplicate fails (one detector/hub per account), + * and the org analyzer supersedes the account analyzer. The Config recorder + * is always created: recorders stay per-account, delegation never makes one. + */ + readonly localDetectiveServices?: boolean; } export class DetectiveControls extends Construct { @@ -297,12 +307,14 @@ export class DetectiveControls extends Construct { }); // ────────────────────────────────────────────────────────────────────── - // H-3 GuardDuty + // H-3 GuardDuty, H-4 Security Hub, M-5 Access Analyzer — skipped when the + // org delegated admin owns them (localDetectiveServices: false). // ────────────────────────────────────────────────────────────────────── - new guardduty.CfnDetector(this, "GuardDutyDetector", { - enable: true, - findingPublishingFrequency: "FIFTEEN_MINUTES", - }); + if (props.localDetectiveServices ?? true) { + new guardduty.CfnDetector(this, "GuardDutyDetector", { + enable: true, + findingPublishingFrequency: "FIFTEEN_MINUTES", + }); // ────────────────────────────────────────────────────────────────────── // H-4 Security Hub (FSBP + CIS v3.0) @@ -354,6 +366,7 @@ export class DetectiveControls extends Construct { analyzerName: `${prefix}-account-analyzer`, type: "ACCOUNT", }); + } new cdk.CfnOutput(this, "ConfigBucketName", { value: configBucket.bucketName, diff --git a/lib/member-baseline-stack.ts b/lib/member-baseline-stack.ts index f6677a8..efc61e9 100644 --- a/lib/member-baseline-stack.ts +++ b/lib/member-baseline-stack.ts @@ -21,6 +21,12 @@ export interface MemberBaselineStackProps extends cdk.StackProps { readonly flowLogVpcIds: string[]; /** Value for the ManagedBy tag on every resource in the stack. */ readonly managedByTag: string; + /** + * TRUE for accounts created after org delegation (2026-07-14): GuardDuty / + * Security Hub / analyzer are org-managed (auto-enrolled), so the stack must + * not create local duplicates. Default FALSE (pre-delegation accounts). + */ + readonly orgManagedDetection?: boolean; } /** @@ -47,6 +53,7 @@ export class MemberBaselineStack extends cdk.Stack { new DetectiveControls(this, "DetectiveControls", { namePrefix: props.namePrefix, + localDetectiveServices: !(props.orgManagedDetection ?? false), }); new FlowLogs(this, "FlowLogs", {