Account-level AWS security baseline (CloudTrail C-1, AWS Backup C-7) — CDK TypeScript
Find a file
Adam Moussa 64ef25dc5b
Add AWS Backup with offsite vault (audit C-7) (#3)
* Add AWS Backup with offsite vault (audit C-7)

The account had zero AWS Backup vaults/plans, so 22 of 23 data stores
had no immutable, cross-region recovery path (audit finding C-7). One
ransomware event or rogue delete would erase primary plus same-region
snapshots/PITR.

Phase 1 ("critical data first") protects the seven highest-risk stores
with no offsite leg today (2 RDS, 2 DynamoDB, 3 S3) via a daily plan in
a new us-east-1 vault, copied cross-region into a governance-locked
us-west-2 vault. Governance (not compliance) mode first so the plan can
be validated before committing to irreversible immutability.

The backup service role is backup-only (no restore policies) to stay
least-privilege; restores get a separate audited path later. Resources
are selected by explicit ARN to avoid drifting the stacks that own them.

Deploys via the shared cdk deploy --all alongside the C-1 CloudTrail
stack. See the README pre-deploy gates (S3 versioning, database-1
unencrypted copy smoke-test, DynamoDB PITR) before the first run.

* Grant AWS Backup service use of vault CMKs

The L2 BackupVault does not grant the backup service principal use of a
customer-managed key; the synthesized key policy only delegated to
account IAM. Cross-region copy of encrypted RDS/EBS recovery points uses
KMS grants on the destination key, so without an explicit grant those
copy jobs fail — and silently, since the account has no CloudTrail yet.

Add backup.amazonaws.com crypto + CreateGrant statements to both vault
keys, scoped by aws:SourceAccount (cross-review BLOCK 2; mirrors the
discipline used on the C-1 CloudTrail key). Same class of bug the C-1
cross-review caught on the CloudTrail CMK.
2026-05-29 18:06:17 -04:00
.github Initial account-baseline stack with CloudTrail (audit C-1) 2026-05-29 17:44:55 -04:00
bin Add AWS Backup with offsite vault (audit C-7) (#3) 2026-05-29 18:06:17 -04:00
lib Add AWS Backup with offsite vault (audit C-7) (#3) 2026-05-29 18:06:17 -04:00
.gitignore Initial account-baseline stack with CloudTrail (audit C-1) 2026-05-29 17:44:55 -04:00
cdk.json Initial account-baseline stack with CloudTrail (audit C-1) 2026-05-29 17:44:55 -04:00
package-lock.json Initial account-baseline stack with CloudTrail (audit C-1) 2026-05-29 17:44:55 -04:00
package.json Initial account-baseline stack with CloudTrail (audit C-1) 2026-05-29 17:44:55 -04:00
README.md Add AWS Backup with offsite vault (audit C-7) (#3) 2026-05-29 18:06:17 -04:00
tsconfig.json Initial account-baseline stack with CloudTrail (audit C-1) 2026-05-29 17:44:55 -04:00

seahaven-account-baseline

Account-level security and governance baseline for Sea Haven Industries (AWS account 328440206208), managed as a single CDK TypeScript app. Most resources are in us-east-1; the offsite backup vault is in us-west-2. This is where account-wide detective and recovery controls live, so they are versioned, reviewed, and drift-checked like any other stack.

Stacks (all deployed by cdk deploy --all / the CD workflow):

Stack Region Purpose
seahaven-account-baseline us-east-1 CloudTrail + future detective controls (C-1)
seahaven-backup us-east-1 Primary AWS Backup vault + plan + role (C-7)
seahaven-backup-offsite us-west-2 Governance-locked offsite copy vault (C-7)

What it deploys

CloudTrail (audit finding C-1)

Resource Logical ID Notes
Multi-region trail Trail (seahaven-org-trail) Management events read+write, global service events, log-file validation on
Log bucket TrailLogBucket (seahaven-cloudtrail-logs-328440206208) Private (Block Public Access all), SSE-KMS, versioned, TLS-only, Object Lock GOVERNANCE 365d, lifecycle (Glacier @90d, expire @365d), server access logging → seahaven-s3-access-logs
KMS CMK TrailKey (alias/cloudtrail-logs) Encrypts log files; automatic rotation enabled
CloudWatch Logs group created by the L2 Trail 365-day retention; this is the group the CIS Section 4 metric filters (H-1) attach to

Data flow: API activity across all regions → CloudTrail → (a) KMS-encrypted, Object-Locked S3 bucket for durable/tamper-resistant storage and (b) CloudWatch Logs for real-time querying and metric-filter alarms.

Compliance impact: closes CIS 3.1 (multi-region trail), 3.2 (log-file validation), 3.4 (CloudWatch Logs integration), 3.6 (bucket access logging), 3.7 (KMS CMK encryption), and 3.8 (CMK rotation). Unblocks CIS Section 4 / finding H-1 (metric filters + alarms now have a log group to target).

Design decisions

  • Management events only. Object-level S3/Lambda data events (CIS 3.10/3.11) are deferred to control cost; revisit with targeted S3 write data events on sensitive buckets (payments / accounting / kb) if needed.
  • Object Lock GOVERNANCE, not COMPLIANCE. Tamper-resistant but still deletable by a principal holding s3:BypassGovernanceRetention — avoids the irreversibility of COMPLIANCE mode. Revisit if a stricter posture is required.
  • RETAIN on the bucket and KMS key so a stack teardown never destroys the audit trail.

AWS Backup (audit finding C-7)

Phase 1 ("critical data first") of fixing the account's complete lack of AWS Backup. Protects the data stores with no offsite leg today and copies each recovery point cross-region into a governance-locked vault.

Resource Logical ID Notes
Primary vault seahaven-primary (us-east-1) KMS-CMK encrypted, unlocked (working copy), RETAIN
Offsite vault seahaven-offsite (us-west-2) KMS-CMK encrypted, Vault Lock GOVERNANCE (min-retention 30d, no cooling-off window), RETAIN
Backup plan seahaven-critical-daily Daily 06:00 UTC, delete-after 35d, cross-region CopyAction → offsite (retain 90d)
Service role seahaven-backup-service-role Backup-only (Backup + S3-Backup managed policies); restore perms intentionally deferred

Phase-1 scope (selected by explicit ARN, not tags, to avoid drifting other stacks): RDS database-1, RDS proposal-system-db, DynamoDB PaymentsDashboard, DynamoDB purchase-orders, S3 accounting.seahaven.com, seahaven-payments-csv-328440206208, google-workspace-seahavenind.com.

Coexists with existing EBS DLM snapshots and DynamoDB PITR — it supplements them with the missing offsite + immutable leg; it does not replace them.

Design decisions:

  • Governance lock first, not compliance. Recovery points can't be silently deleted, but a principal with explicit permission can still intervene while we validate. Graduate to COMPLIANCE (irreversible) later by adding changeableFor to the offsite vault lock + redeploy.
  • Backup-only role. Restore policies and allowRestores are not granted; restores get a separate audited path once a restore-test process exists.

Pre-deploy gates (must clear before the first scheduled run):

  1. Enable S3 versioning on seahaven-payments-csv-328440206208 and google-workspace-seahavenind.com (accounting.seahaven.com already has it, audit C-9), or their jobs fail silently (folds in H-21).
  2. database-1 is unencrypted (H-19): smoke-test an on-demand backup + copy of it to us-west-2 first; if the copy fails, encrypt it or drop it from the copy.
  3. Enable DynamoDB PITR (H-7) on the two tables for between-window recovery.

Roadmap (same stack)

Account-level detective controls with no current home, to be added to the account-baseline stack: AWS Config (H-2), GuardDuty (H-3), Security Hub (H-4), IAM Access Analyzer (M-5), Inspector2 (M-6). Backup phase 2: expand past the phase-1 set via tag-based selection and graduate the offsite vault to compliance mode.

Deploy

CI/CD via the org reusable workflows (ci-typescript-cdk.yaml, cd-cdk.yaml); pushes to main deploy through the OIDC role in secrets.AWS_DEPLOY_ROLE_ARN. Local: npm ci && npm run build && npx cdk diff.

npx cdk deploy seahaven-account-baseline

Verify

aws cloudtrail get-trail-status --name seahaven-org-trail        # IsLogging: true
aws cloudtrail describe-trails --trail-name-list seahaven-org-trail
aws cloudtrail validate-logs --trail-arn <arn> --start-time <t>  # digest integrity

AWS Backup (C-7):

aws backup list-backup-vaults                                    # seahaven-primary
aws backup list-backup-vaults --region us-west-2                 # seahaven-offsite
aws backup describe-backup-vault --backup-vault-name seahaven-offsite --region us-west-2  # Locked, MinRetentionDays
aws backup get-backup-plan --backup-plan-id <id>                 # daily rule + CopyAction
# Smoke test: on-demand backup of one resource, then confirm the cross-region copy lands
aws backup start-backup-job --backup-vault-name seahaven-primary \
  --resource-arn arn:aws:rds:us-east-1:328440206208:db:database-1 \
  --iam-role-arn arn:aws:iam::328440206208:role/seahaven-backup-service-role
aws backup list-copy-jobs --region us-west-2                     # copy to offsite present + COMPLETED