mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-10-03 15:03:13 +00:00
Nightly backup jobs fail on resources that have left the management account. The CloudFront WebACL is already gone while CloudFormation still owns it, so the deletion policy has to be Retain before a later change can remove it.
89 lines
3 KiB
TypeScript
89 lines
3 KiB
TypeScript
import * as cdk from "aws-cdk-lib";
|
|
import * as wafv2 from "aws-cdk-lib/aws-wafv2";
|
|
import * as ssm from "aws-cdk-lib/aws-ssm";
|
|
import { Construct } from "constructs";
|
|
|
|
/**
|
|
* Shared CloudFront WAF WebACL for Sea Haven app distributions (audit M-17).
|
|
*
|
|
* AWS managed rule groups (Common + Known Bad Inputs) plus an IP rate limit.
|
|
* CLOUDFRONT-scope WebACLs must live in us-east-1 — which is where this stack
|
|
* is — so it can be referenced by any app CloudFront distribution by ARN.
|
|
*
|
|
* The ARN is published to SSM (`/seahaven/waf/app-web-acl-arn`) so app stacks in
|
|
* other repos can consume it via `{{resolve:ssm:...}}` without a hard CFN export.
|
|
*/
|
|
export interface AppWebAclProps {
|
|
/**
|
|
* Deletion policy for the WebACL only. The SSM parameter keeps the default
|
|
* Delete policy so a later stack update can remove the parameter. Prod does
|
|
* not set this. Management sets RETAIN first because the live WebACL is
|
|
* already gone and a Delete call would fail and roll back into a recreate.
|
|
*/
|
|
readonly webAclRemovalPolicy?: cdk.RemovalPolicy;
|
|
}
|
|
|
|
export class AppWebAcl extends Construct {
|
|
constructor(scope: Construct, id: string, props?: AppWebAclProps) {
|
|
super(scope, id);
|
|
|
|
const vis = (metric: string): wafv2.CfnWebACL.VisibilityConfigProperty => ({
|
|
cloudWatchMetricsEnabled: true,
|
|
sampledRequestsEnabled: true,
|
|
metricName: metric,
|
|
});
|
|
|
|
const webAcl = new wafv2.CfnWebACL(this, "AppWebAcl", {
|
|
name: "seahaven-app-waf",
|
|
scope: "CLOUDFRONT",
|
|
defaultAction: { allow: {} },
|
|
visibilityConfig: vis("seahaven-app-waf"),
|
|
rules: [
|
|
{
|
|
name: "AWSCommonRuleSet",
|
|
priority: 1,
|
|
overrideAction: { none: {} },
|
|
statement: {
|
|
managedRuleGroupStatement: {
|
|
vendorName: "AWS",
|
|
name: "AWSManagedRulesCommonRuleSet",
|
|
},
|
|
},
|
|
visibilityConfig: vis("AWSCommonRuleSet"),
|
|
},
|
|
{
|
|
name: "AWSKnownBadInputs",
|
|
priority: 2,
|
|
overrideAction: { none: {} },
|
|
statement: {
|
|
managedRuleGroupStatement: {
|
|
vendorName: "AWS",
|
|
name: "AWSManagedRulesKnownBadInputsRuleSet",
|
|
},
|
|
},
|
|
visibilityConfig: vis("AWSKnownBadInputs"),
|
|
},
|
|
{
|
|
name: "RateLimitPerIp",
|
|
priority: 3,
|
|
action: { block: {} },
|
|
statement: {
|
|
rateBasedStatement: { limit: 2000, aggregateKeyType: "IP" },
|
|
},
|
|
visibilityConfig: vis("RateLimitPerIp"),
|
|
},
|
|
],
|
|
});
|
|
if (props?.webAclRemovalPolicy) {
|
|
webAcl.applyRemovalPolicy(props.webAclRemovalPolicy);
|
|
}
|
|
|
|
new ssm.StringParameter(this, "AppWebAclArnParam", {
|
|
parameterName: "/seahaven/waf/app-web-acl-arn",
|
|
stringValue: webAcl.attrArn,
|
|
description: "ARN of the shared CloudFront WAF WebACL (audit M-17)",
|
|
});
|
|
|
|
new cdk.CfnOutput(this, "AppWebAclArn", { value: webAcl.attrArn });
|
|
}
|
|
}
|