feat(waf): add seahaven-prod shared CloudFront WebACL (PLAT-92) (#96)
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(waf): add seahaven-prod shared CloudFront WebACL stack

Stand up AppWebAcl in a thin prod stack and widen seahaven-site HCP
roles to read the SSM ARN so CloudFront can associate the ACL in-account.

* fix(deploy): add app-web-acl-prod to deploy.yaml
This commit is contained in:
Adam Moussa 2026-08-07 17:07:04 -04:00 • committed by GitHub
parent 2789f3cbcf
commit a6f22880db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 68 additions and 1 deletions

View file

@ -56,7 +56,7 @@ jobs:
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@7ac3528750b346f181347bb09f6af927a1c0aa14 # v1.0.6
with:
node-version: "24"
stacks: "prod-baseline dynamodb-cmk-prod alarm-topic-prod deploy-substrate-prod terraform-substrate-prod"
stacks: "prod-baseline dynamodb-cmk-prod alarm-topic-prod deploy-substrate-prod terraform-substrate-prod app-web-acl-prod"
stack-name: "seahaven-prod-baseline"
secrets:
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN_PROD }}

View file

@ -69,6 +69,7 @@ the TypeScript source — no separate compile step needed for `cdk synth` /
| `deploy-substrate-dev` | `seahaven-deploy-substrate` | 710827005802 | us-east-1 | `lib/deploy-substrate-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` |
| `app-web-acl-prod` | `seahaven-app-web-acl` | 011934824531 | us-east-1 | `lib/app-web-acl-stack.ts` |
Member-account stacks deploy with per-account credentials — the CD workflow
runs one job per account, each assuming that account's OIDC deploy role. Local

View file

@ -9,6 +9,7 @@ import { RegionalBaselineStack } from "../lib/regional-baseline-stack";
import { DeploySubstrateStack } from "../lib/deploy-substrate-stack";
import { TerraformSubstrateStack } from "../lib/terraform-substrate-stack";
import { DynamoDbCmkStack } from "../lib/dynamodb-cmk-stack";
import { AppWebAclStack } from "../lib/app-web-acl-stack";
import { MemberBaselineStack } from "../lib/member-baseline-stack";
import { OrgGovernanceStack } from "../lib/org-governance-stack";
@ -207,6 +208,15 @@ const terraformSubstrateProd = new TerraformSubstrateStack(
);
terraformSubstrateProd.addStackDependency(deploySubstrateProd);
// Shared CloudFront WAF for seahaven-prod (PLAT-92). Same AppWebAcl construct
// as mgmt account-baseline; thin stack so prod does not inherit the full
// mgmt baseline. Publishes /seahaven/waf/app-web-acl-arn for in-account
// CloudFront associations (same-account only).
new AppWebAclStack(app, "app-web-acl-prod", {
stackName: "seahaven-app-web-acl",
env: { account: PROD_ACCOUNT, region: "us-east-1" },
});
const terraformSubstrateDev = new TerraformSubstrateStack(
app,
"terraform-substrate-dev",

25
lib/app-web-acl-stack.ts Normal file
View file

@ -0,0 +1,25 @@
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { AppWebAcl } from "./web-acl";
/**
* Thin per-account stack that owns the shared CloudFront WAFv2 WebACL (M-17)
* and publishes its ARN to SSM `/seahaven/waf/app-web-acl-arn`.
*
* Mgmt already has this ACL inside `AccountBaselineStack`. Workload accounts
* (starting with seahaven-prod / PLAT-92) get a dedicated stack so we do not
* pull the full mgmt baseline (trail, budgets, flow logs, …) into prod just
* to share a CloudFront WAF. App stacks associate by reading the SSM param
* in-account — WAFv2 CloudFront associations are same-account only.
*/
export class AppWebAclStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new AppWebAcl(this, "AppWebAcl");
cdk.Tags.of(this).add("Project", "account-baseline");
cdk.Tags.of(this).add("Owner", "adam@seahavenind.com");
cdk.Tags.of(this).add("ManagedBy", "cdk");
}
}

View file

@ -1482,6 +1482,19 @@ Resources:
- acm:ListTagsForCertificate
- acm:GetCertificate
Resource: "*"
- Sid: RefreshAppWebAclSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/waf/app-web-acl-arn"
- Sid: RefreshWafWebAcl
Effect: Allow
Action:
- wafv2:GetWebACL
- wafv2:ListWebACLs
Resource: "*"
HcptfSeahavenSiteApplyRole:
Type: AWS::IAM::Role
@ -1554,3 +1567,21 @@ Resources:
Condition:
StringEquals:
"aws:ResourceTag/Project": seahaven-site
# CloudFront web_acl_id is set via UpdateDistribution (cloudfront:*
# above). Read the shared ACL ARN from SSM (PLAT-92) and allow
# WAFv2 describe so plans/applies can validate the association.
- Sid: ReadAppWebAclSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/waf/app-web-acl-arn"
- Sid: ReadWafWebAcl
Effect: Allow
Action:
- wafv2:GetWebACL
- wafv2:GetWebACLForResource
- wafv2:ListWebACLs
- wafv2:ListResourcesForWebACL
Resource: "*"