mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-09-30 08:03:19 +00:00
seahaven-app-waf (CLOUDFRONT scope, us-east-1): AWS managed Common + Known Bad Inputs rule groups + per-IP rate limit (2000/5min). ARN published to SSM /seahaven/waf/app-web-acl-arn for app stacks (meal-order/orders) to consume. seahaven.com already has its own WAF; ledgerflow is being decommissioned (INFRA-26); proposal-system-web skipped (not live).
76 lines
2.4 KiB
TypeScript
76 lines
2.4 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 class AppWebAcl extends Construct {
|
|
constructor(scope: Construct, id: string) {
|
|
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"),
|
|
},
|
|
],
|
|
});
|
|
|
|
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 });
|
|
}
|
|
}
|