@composurecdk/wafv2 0.0.0-stage → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -2
- package/dist/commonjs/alarm-config.d.ts +60 -0
- package/dist/commonjs/alarm-config.d.ts.map +1 -0
- package/dist/commonjs/alarm-config.js +3 -0
- package/dist/commonjs/alarm-config.js.map +1 -0
- package/dist/commonjs/alarm-defaults.d.ts +34 -0
- package/dist/commonjs/alarm-defaults.d.ts.map +1 -0
- package/dist/commonjs/alarm-defaults.js +37 -0
- package/dist/commonjs/alarm-defaults.js.map +1 -0
- package/dist/commonjs/alarms.d.ts +18 -0
- package/dist/commonjs/alarms.d.ts.map +1 -0
- package/dist/commonjs/alarms.js +94 -0
- package/dist/commonjs/alarms.js.map +1 -0
- package/dist/commonjs/association.d.ts +21 -0
- package/dist/commonjs/association.d.ts.map +1 -0
- package/dist/commonjs/association.js +61 -0
- package/dist/commonjs/association.js.map +1 -0
- package/dist/commonjs/custom-response.d.ts +52 -0
- package/dist/commonjs/custom-response.d.ts.map +1 -0
- package/dist/commonjs/custom-response.js +137 -0
- package/dist/commonjs/custom-response.js.map +1 -0
- package/dist/commonjs/defaults.d.ts +89 -0
- package/dist/commonjs/defaults.d.ts.map +1 -0
- package/dist/commonjs/defaults.js +90 -0
- package/dist/commonjs/defaults.js.map +1 -0
- package/dist/commonjs/index.d.ts +30 -0
- package/dist/commonjs/index.d.ts.map +1 -0
- package/dist/commonjs/index.js +35 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/logging-filter.d.ts +27 -0
- package/dist/commonjs/logging-filter.d.ts.map +1 -0
- package/dist/commonjs/logging-filter.js +17 -0
- package/dist/commonjs/logging-filter.js.map +1 -0
- package/dist/commonjs/logging.d.ts +59 -0
- package/dist/commonjs/logging.d.ts.map +1 -0
- package/dist/commonjs/logging.js +91 -0
- package/dist/commonjs/logging.js.map +1 -0
- package/dist/commonjs/metrics.d.ts +58 -0
- package/dist/commonjs/metrics.d.ts.map +1 -0
- package/dist/commonjs/metrics.js +67 -0
- package/dist/commonjs/metrics.js.map +1 -0
- package/dist/commonjs/names.d.ts +46 -0
- package/dist/commonjs/names.d.ts.map +1 -0
- package/dist/commonjs/names.js +140 -0
- package/dist/commonjs/names.js.map +1 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/rule-builder.d.ts +123 -0
- package/dist/commonjs/rule-builder.d.ts.map +1 -0
- package/dist/commonjs/rule-builder.js +163 -0
- package/dist/commonjs/rule-builder.js.map +1 -0
- package/dist/commonjs/scope.d.ts +15 -0
- package/dist/commonjs/scope.d.ts.map +1 -0
- package/dist/commonjs/scope.js +35 -0
- package/dist/commonjs/scope.js.map +1 -0
- package/dist/commonjs/web-acl-builder.d.ts +152 -0
- package/dist/commonjs/web-acl-builder.d.ts.map +1 -0
- package/dist/commonjs/web-acl-builder.js +205 -0
- package/dist/commonjs/web-acl-builder.js.map +1 -0
- package/dist/esm/alarm-config.d.ts +60 -0
- package/dist/esm/alarm-config.d.ts.map +1 -0
- package/dist/esm/alarm-config.js +2 -0
- package/dist/esm/alarm-config.js.map +1 -0
- package/dist/esm/alarm-defaults.d.ts +34 -0
- package/dist/esm/alarm-defaults.d.ts.map +1 -0
- package/dist/esm/alarm-defaults.js +34 -0
- package/dist/esm/alarm-defaults.js.map +1 -0
- package/dist/esm/alarms.d.ts +18 -0
- package/dist/esm/alarms.d.ts.map +1 -0
- package/dist/esm/alarms.js +90 -0
- package/dist/esm/alarms.js.map +1 -0
- package/dist/esm/association.d.ts +21 -0
- package/dist/esm/association.d.ts.map +1 -0
- package/dist/esm/association.js +58 -0
- package/dist/esm/association.js.map +1 -0
- package/dist/esm/custom-response.d.ts +52 -0
- package/dist/esm/custom-response.d.ts.map +1 -0
- package/dist/esm/custom-response.js +133 -0
- package/dist/esm/custom-response.js.map +1 -0
- package/dist/esm/defaults.d.ts +89 -0
- package/dist/esm/defaults.d.ts.map +1 -0
- package/dist/esm/defaults.js +87 -0
- package/dist/esm/defaults.js.map +1 -0
- package/dist/esm/index.d.ts +30 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logging-filter.d.ts +27 -0
- package/dist/esm/logging-filter.d.ts.map +1 -0
- package/dist/esm/logging-filter.js +14 -0
- package/dist/esm/logging-filter.js.map +1 -0
- package/dist/esm/logging.d.ts +59 -0
- package/dist/esm/logging.d.ts.map +1 -0
- package/dist/esm/logging.js +88 -0
- package/dist/esm/logging.js.map +1 -0
- package/dist/esm/metrics.d.ts +58 -0
- package/dist/esm/metrics.d.ts.map +1 -0
- package/dist/esm/metrics.js +63 -0
- package/dist/esm/metrics.js.map +1 -0
- package/dist/esm/names.d.ts +46 -0
- package/dist/esm/names.d.ts.map +1 -0
- package/dist/esm/names.js +132 -0
- package/dist/esm/names.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/rule-builder.d.ts +123 -0
- package/dist/esm/rule-builder.d.ts.map +1 -0
- package/dist/esm/rule-builder.js +160 -0
- package/dist/esm/rule-builder.js.map +1 -0
- package/dist/esm/scope.d.ts +15 -0
- package/dist/esm/scope.d.ts.map +1 -0
- package/dist/esm/scope.js +32 -0
- package/dist/esm/scope.js.map +1 -0
- package/dist/esm/web-acl-builder.d.ts +152 -0
- package/dist/esm/web-acl-builder.d.ts.map +1 -0
- package/dist/esm/web-acl-builder.js +202 -0
- package/dist/esm/web-acl-builder.js.map +1 -0
- package/package.json +78 -4
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createWebAclRuleBuilder = createWebAclRuleBuilder;
|
|
4
|
+
const core_1 = require("@composurecdk/core");
|
|
5
|
+
const defaults_js_1 = require("./defaults.js");
|
|
6
|
+
const names_js_1 = require("./names.js");
|
|
7
|
+
const RULE_GROUP_STATEMENTS = ["managedRuleGroupStatement", "ruleGroupReferenceStatement"];
|
|
8
|
+
/** Whether `statement` evaluates a rule group, so its rule takes `overrideAction`. */
|
|
9
|
+
function isRuleGroupStatement(statement) {
|
|
10
|
+
return RULE_GROUP_STATEMENTS.some((key) => statement[key] !== undefined);
|
|
11
|
+
}
|
|
12
|
+
class WebAclRuleBuilder {
|
|
13
|
+
props = {};
|
|
14
|
+
#statement;
|
|
15
|
+
#action;
|
|
16
|
+
#response;
|
|
17
|
+
#labels = [];
|
|
18
|
+
/**
|
|
19
|
+
* Sets the rule's statement as raw CloudFormation, for a statement this
|
|
20
|
+
* builder has no helper for. Fields CDK types as `any` — `singleHeader`,
|
|
21
|
+
* `queryString`, `uriPath`, `method` and the like — pass through verbatim,
|
|
22
|
+
* so they must use CloudFormation's PascalCase (`{ Name: "x-api-key" }`).
|
|
23
|
+
*
|
|
24
|
+
* A rule has exactly one statement; setting a second throws.
|
|
25
|
+
*/
|
|
26
|
+
statement(statement) {
|
|
27
|
+
this.#setStatement("statement", statement);
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
/** Blocks matching requests. This is the default action. */
|
|
31
|
+
block() {
|
|
32
|
+
return this.#setAction("block");
|
|
33
|
+
}
|
|
34
|
+
/** Allows matching requests, ending evaluation of the web ACL. */
|
|
35
|
+
allow() {
|
|
36
|
+
return this.#setAction("allow");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Counts matching requests and carries on evaluating. On a rule-group rule
|
|
40
|
+
* this counts every rule in the group instead of applying its actions.
|
|
41
|
+
*/
|
|
42
|
+
count() {
|
|
43
|
+
return this.#setAction("count");
|
|
44
|
+
}
|
|
45
|
+
/** Answers matching requests with a CAPTCHA puzzle. */
|
|
46
|
+
captcha() {
|
|
47
|
+
return this.#setAction("captcha");
|
|
48
|
+
}
|
|
49
|
+
/** Answers matching requests with a silent browser challenge. */
|
|
50
|
+
challenge() {
|
|
51
|
+
return this.#setAction("challenge");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Blocks matching requests with a custom response instead of WAF's `403`.
|
|
55
|
+
* The body is collected into the web ACL's `customResponseBodies`, so
|
|
56
|
+
* nothing has to be declared there by hand.
|
|
57
|
+
*
|
|
58
|
+
* @param status - One of the status codes WAF supports; `410` is not.
|
|
59
|
+
* @param body - `{ json }`, `{ text }` or `{ html }`; at most 4 KB.
|
|
60
|
+
* @param headers - At most 10, and not `content-type`.
|
|
61
|
+
*/
|
|
62
|
+
respond(status, body, headers) {
|
|
63
|
+
if (this.#action !== undefined && this.#action !== "block") {
|
|
64
|
+
throw new Error(`respond() blocks the request, but the rule's action is already "${this.#action}". A custom response is only available on Block.`);
|
|
65
|
+
}
|
|
66
|
+
this.#action = "block";
|
|
67
|
+
this.#response = { status, body, headers };
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Adds labels to matching requests, for later rules to match on with a
|
|
72
|
+
* label match statement. Give the namespaces and name only, such as
|
|
73
|
+
* `"auth:missing-token"`; WAF adds the web ACL prefix.
|
|
74
|
+
*/
|
|
75
|
+
labels(...labels) {
|
|
76
|
+
for (const label of labels)
|
|
77
|
+
(0, names_js_1.validateRuleLabel)(label);
|
|
78
|
+
this.#labels.push(...labels);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
/** @internal — see ADR-0005. */
|
|
82
|
+
[core_1.COPY_STATE](target) {
|
|
83
|
+
target.#statement = this.#statement;
|
|
84
|
+
target.#action = this.#action;
|
|
85
|
+
target.#response = this.#response;
|
|
86
|
+
target.#labels.push(...this.#labels);
|
|
87
|
+
}
|
|
88
|
+
#setStatement(method, statement) {
|
|
89
|
+
if (this.#statement !== undefined) {
|
|
90
|
+
throw new Error(`${method}(): the rule already has a statement. A rule has exactly one; combine conditions with an AndStatement or OrStatement.`);
|
|
91
|
+
}
|
|
92
|
+
this.#statement = statement;
|
|
93
|
+
}
|
|
94
|
+
#setAction(action) {
|
|
95
|
+
if (this.#response !== undefined && action !== "block") {
|
|
96
|
+
throw new Error(`${action}(): the rule already has a custom response from respond(), which only applies to Block.`);
|
|
97
|
+
}
|
|
98
|
+
this.#action = action;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
/** @internal — the web ACL builder turns each rule into CloudFormation. */
|
|
102
|
+
toRule(ctx) {
|
|
103
|
+
const who = `rule "${ctx.name}"`;
|
|
104
|
+
const statement = this.#statement;
|
|
105
|
+
if (statement === undefined) {
|
|
106
|
+
throw new Error(`${ctx.owner} ${who} has no statement. Give it one, such as .statement({ ... }).`);
|
|
107
|
+
}
|
|
108
|
+
const metricName = this.props.metricName ?? ctx.name;
|
|
109
|
+
(0, names_js_1.validateMetricName)(metricName);
|
|
110
|
+
const sampled = this.props.sampledRequests ?? ctx.sampledRequests;
|
|
111
|
+
const rateBased = statement.rateBasedStatement !== undefined;
|
|
112
|
+
const ruleGroup = isRuleGroupStatement(statement);
|
|
113
|
+
const { blocks, ...action } = this.#actionProperty(ctx, who, ruleGroup, rateBased);
|
|
114
|
+
const property = {
|
|
115
|
+
name: ctx.name,
|
|
116
|
+
priority: this.props.priority ?? ctx.defaultPriority,
|
|
117
|
+
statement,
|
|
118
|
+
...action,
|
|
119
|
+
visibilityConfig: {
|
|
120
|
+
...defaults_js_1.WEB_ACL_VISIBILITY_DEFAULTS,
|
|
121
|
+
metricName,
|
|
122
|
+
sampledRequestsEnabled: sampled,
|
|
123
|
+
},
|
|
124
|
+
...(this.#labels.length > 0 && { ruleLabels: this.#labels.map((name) => ({ name })) }),
|
|
125
|
+
...(this.props.captchaConfig && { captchaConfig: this.props.captchaConfig }),
|
|
126
|
+
...(this.props.challengeConfig && { challengeConfig: this.props.challengeConfig }),
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
property,
|
|
130
|
+
metricName,
|
|
131
|
+
sampled,
|
|
132
|
+
rateBased,
|
|
133
|
+
ruleGroup,
|
|
134
|
+
blocks,
|
|
135
|
+
alarms: this.props.alarms,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
#actionProperty(ctx, who, ruleGroup, rateBased) {
|
|
139
|
+
if (ruleGroup) {
|
|
140
|
+
if (this.#action !== undefined && this.#action !== "count") {
|
|
141
|
+
throw new Error(`${ctx.owner} ${who} evaluates a rule group, whose rules keep their own actions. ` +
|
|
142
|
+
`Use .count() to count them all instead, or leave the action unset.`);
|
|
143
|
+
}
|
|
144
|
+
// The group's own rules block unless the whole group is counted.
|
|
145
|
+
return this.#action === "count"
|
|
146
|
+
? { overrideAction: { count: {} }, blocks: false }
|
|
147
|
+
: { overrideAction: { none: {} }, blocks: true };
|
|
148
|
+
}
|
|
149
|
+
const action = this.#action ?? "block";
|
|
150
|
+
if (action === "allow" && rateBased) {
|
|
151
|
+
throw new Error(`${ctx.owner} ${who} is rate-based, and a rate-based rule cannot Allow. Use block, count, captcha or challenge.`);
|
|
152
|
+
}
|
|
153
|
+
if (action !== "block")
|
|
154
|
+
return { action: { [action]: {} }, blocks: false };
|
|
155
|
+
return { action: { block: ctx.bodies.block(who, ctx.name, this.#response) }, blocks: true };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** @internal — the web ACL builder makes one of these per rule. */
|
|
159
|
+
function createWebAclRuleBuilder() {
|
|
160
|
+
// eslint-disable-next-line composurecdk/builder-must-be-tagged -- a rule is a property of AWS::WAFv2::WebACL, not a taggable resource
|
|
161
|
+
return (0, core_1.Builder)(WebAclRuleBuilder);
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=rule-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-builder.js","sourceRoot":"","sources":["../../src/rule-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IConstruct } from "constructs";
|
|
2
|
+
/** Where a web ACL can be used: CloudFront distributions, or regional resources. */
|
|
3
|
+
export type WebAclScope = "REGIONAL" | "CLOUDFRONT";
|
|
4
|
+
/**
|
|
5
|
+
* Throws when a CLOUDFRONT web ACL's stack is in a known region other than
|
|
6
|
+
* `us-east-1`, and warns when the region cannot be known at synth.
|
|
7
|
+
*
|
|
8
|
+
* Env-agnostic stacks fall back to `CDK_DEFAULT_REGION`, which the CDK toolkit
|
|
9
|
+
* uses to fill the deploy region, as `@composurecdk/route53`'s query logging
|
|
10
|
+
* does.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function checkCloudFrontRegion(scope: IConstruct, id: string): void;
|
|
15
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/scope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAOpD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAqBzE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkCloudFrontRegion = checkCloudFrontRegion;
|
|
4
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
5
|
+
const CLOUDFRONT_REGION = "us-east-1";
|
|
6
|
+
const REGION_ANNOTATION = "@composurecdk/wafv2:cloudfront-region";
|
|
7
|
+
const SOURCE = "https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works-resources.html";
|
|
8
|
+
/**
|
|
9
|
+
* Throws when a CLOUDFRONT web ACL's stack is in a known region other than
|
|
10
|
+
* `us-east-1`, and warns when the region cannot be known at synth.
|
|
11
|
+
*
|
|
12
|
+
* Env-agnostic stacks fall back to `CDK_DEFAULT_REGION`, which the CDK toolkit
|
|
13
|
+
* uses to fill the deploy region, as `@composurecdk/route53`'s query logging
|
|
14
|
+
* does.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
function checkCloudFrontRegion(scope, id) {
|
|
19
|
+
const stackRegion = aws_cdk_lib_1.Stack.of(scope).region;
|
|
20
|
+
const region = aws_cdk_lib_1.Token.isUnresolved(stackRegion) ? process.env.CDK_DEFAULT_REGION : stackRegion;
|
|
21
|
+
if (region === undefined || region === "") {
|
|
22
|
+
aws_cdk_lib_1.Annotations.of(scope).addWarningV2(REGION_ANNOTATION, `WebAclBuilder "${id}" has scope CLOUDFRONT, which AWS WAF only accepts in ` +
|
|
23
|
+
`${CLOUDFRONT_REGION}. This stack is env-agnostic and CDK_DEFAULT_REGION is not ` +
|
|
24
|
+
`set, so the region cannot be checked at synth. Pin env: { region: ` +
|
|
25
|
+
`"${CLOUDFRONT_REGION}" } on the stack to silence this warning.`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (region !== CLOUDFRONT_REGION) {
|
|
29
|
+
throw new Error(`WebAclBuilder "${id}" has scope CLOUDFRONT, but its stack is in "${region}". ` +
|
|
30
|
+
`AWS WAF only creates CloudFront web ACLs in ${CLOUDFRONT_REGION}. Deploy this ` +
|
|
31
|
+
`web ACL from a ${CLOUDFRONT_REGION} stack and pass its ARN to the distribution's ` +
|
|
32
|
+
`webAclId. See ${SOURCE}.`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/scope.ts"],"names":[],"mappings":";;AAqBA,sDAqBC;AA1CD,6CAAwD;AAMxD,MAAM,iBAAiB,GAAG,WAAW,CAAC;AACtC,MAAM,iBAAiB,GAAG,uCAAuC,CAAC;AAClE,MAAM,MAAM,GACV,wFAAwF,CAAC;AAE3F;;;;;;;;;GASG;AACH,SAAgB,qBAAqB,CAAC,KAAiB,EAAE,EAAU;IACjE,MAAM,WAAW,GAAG,mBAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,MAAM,GAAG,mBAAK,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,WAAW,CAAC;IAC9F,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAC1C,yBAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,YAAY,CAChC,iBAAiB,EACjB,kBAAkB,EAAE,wDAAwD;YAC1E,GAAG,iBAAiB,6DAA6D;YACjF,oEAAoE;YACpE,IAAI,iBAAiB,2CAA2C,CACnE,CAAC;QACF,OAAO;IACT,CAAC;IACD,IAAI,MAAM,KAAK,iBAAiB,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,kBAAkB,EAAE,gDAAgD,MAAM,KAAK;YAC7E,+CAA+C,iBAAiB,gBAAgB;YAChF,kBAAkB,iBAAiB,gDAAgD;YACnF,iBAAiB,MAAM,GAAG,CAC7B,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { Alarm } from "aws-cdk-lib/aws-cloudwatch";
|
|
2
|
+
import type { LogGroup } from "aws-cdk-lib/aws-logs";
|
|
3
|
+
import { type CfnLoggingConfiguration, CfnWebACL, type CfnWebACLAssociation, type CfnWebACLProps } from "aws-cdk-lib/aws-wafv2";
|
|
4
|
+
import type { IConstruct } from "constructs";
|
|
5
|
+
import { COPY_STATE, type Lifecycle, type Resolvable } from "@composurecdk/core";
|
|
6
|
+
import { type ITaggedBuilder } from "@composurecdk/cloudformation";
|
|
7
|
+
import { type AlarmConfigurer } from "@composurecdk/cloudwatch";
|
|
8
|
+
import type { WebAclAlarmConfig } from "./alarm-config.js";
|
|
9
|
+
import { type WebAclAssociationTarget } from "./association.js";
|
|
10
|
+
import { type CustomResponse } from "./custom-response.js";
|
|
11
|
+
import { type IWebAclRuleBuilder } from "./rule-builder.js";
|
|
12
|
+
import { type WebAclLoggingConfig } from "./logging.js";
|
|
13
|
+
import { type WebAclMetrics } from "./metrics.js";
|
|
14
|
+
import { type WebAclScope } from "./scope.js";
|
|
15
|
+
/**
|
|
16
|
+
* Configuration for {@link createWebAclBuilder}: CDK's `CfnWebACLProps`, less
|
|
17
|
+
* what the builder owns. Rules, the default action and associations have
|
|
18
|
+
* their own methods; custom response bodies and the visibility config are
|
|
19
|
+
* generated.
|
|
20
|
+
*/
|
|
21
|
+
export interface WebAclBuilderProps extends Omit<CfnWebACLProps, "scope" | "defaultAction" | "rules" | "customResponseBodies" | "visibilityConfig" | "tags"> {
|
|
22
|
+
/**
|
|
23
|
+
* `REGIONAL` (the default) for API Gateway, Application Load Balancers,
|
|
24
|
+
* AppSync, Cognito, App Runner and Verified Access; `CLOUDFRONT` for
|
|
25
|
+
* CloudFront distributions, which must be deployed in `us-east-1`.
|
|
26
|
+
*/
|
|
27
|
+
scope?: WebAclScope;
|
|
28
|
+
/**
|
|
29
|
+
* The name of the web ACL's CloudWatch metrics. Defaults to its `name` when
|
|
30
|
+
* that is set, and otherwise to a name derived from its construct path, so
|
|
31
|
+
* two web ACLs never share metrics.
|
|
32
|
+
*/
|
|
33
|
+
metricName?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Whether WAF keeps samples of the requests the web ACL and its rules
|
|
36
|
+
* inspect. Off by default: samples keep every header, and logging's
|
|
37
|
+
* redaction does not apply to them. Turning it on without a
|
|
38
|
+
* `dataProtectionConfig` warns.
|
|
39
|
+
*/
|
|
40
|
+
sampledRequests?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The web ACL's logging. On by default, to a created `aws-waf-logs-`
|
|
43
|
+
* CloudWatch log group, keeping only the requests WAF does not allow and
|
|
44
|
+
* redacting the `authorization` and `cookie` headers. `false` turns it off.
|
|
45
|
+
*
|
|
46
|
+
* @see https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/sec_detect_investigate_events_app_service_logging.html
|
|
47
|
+
*/
|
|
48
|
+
logging?: WebAclLoggingConfig | false;
|
|
49
|
+
/**
|
|
50
|
+
* The master switch for the recommended alarms, which are configured per
|
|
51
|
+
* rule with the rule builder's `.alarms()`. `false` creates none; alarms
|
|
52
|
+
* added with `addAlarm()` are unaffected.
|
|
53
|
+
*
|
|
54
|
+
* No alarm actions are configured; apply them from the result or with
|
|
55
|
+
* `alarmActionsPolicy`.
|
|
56
|
+
*/
|
|
57
|
+
recommendedAlarms?: WebAclAlarmConfig | false;
|
|
58
|
+
}
|
|
59
|
+
/** The build output of an {@link IWebAclBuilder}. */
|
|
60
|
+
export interface WebAclBuilderResult {
|
|
61
|
+
/** The web ACL. */
|
|
62
|
+
webAcl: CfnWebACL;
|
|
63
|
+
/**
|
|
64
|
+
* One association per `associateWith` call, in that order. Empty for a
|
|
65
|
+
* CLOUDFRONT web ACL, which a distribution references by `webAclId`.
|
|
66
|
+
*/
|
|
67
|
+
associations: CfnWebACLAssociation[];
|
|
68
|
+
/** The log group WAF writes to. Absent when logging is off or goes to your own destination. */
|
|
69
|
+
logGroup?: LogGroup;
|
|
70
|
+
/** The logging configuration. Absent when logging is off. */
|
|
71
|
+
logging?: CfnLoggingConfiguration;
|
|
72
|
+
/**
|
|
73
|
+
* The web ACL's metrics, for the ACL, the default action and each rule,
|
|
74
|
+
* with dimensions that match what WAF publishes.
|
|
75
|
+
*/
|
|
76
|
+
metrics: WebAclMetrics;
|
|
77
|
+
/** The CloudWatch alarms created, recommended and custom, keyed by alarm key. */
|
|
78
|
+
alarms: Record<string, Alarm>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A fluent builder for an AWS WAF web ACL.
|
|
82
|
+
*
|
|
83
|
+
* @see {@link createWebAclBuilder}
|
|
84
|
+
*/
|
|
85
|
+
export type IWebAclBuilder = ITaggedBuilder<WebAclBuilderProps, WebAclBuilder>;
|
|
86
|
+
declare class WebAclBuilder implements Lifecycle<WebAclBuilderResult> {
|
|
87
|
+
#private;
|
|
88
|
+
props: Partial<WebAclBuilderProps>;
|
|
89
|
+
/**
|
|
90
|
+
* What happens to a request no rule decides on. `allow` is the default;
|
|
91
|
+
* `block` can answer with a custom response.
|
|
92
|
+
*/
|
|
93
|
+
defaultAction(action: "allow" | "block", response?: CustomResponse): this;
|
|
94
|
+
/**
|
|
95
|
+
* Adds a rule. Rules are evaluated in the order they are added, unless a
|
|
96
|
+
* rule sets its own `.priority(n)`.
|
|
97
|
+
*
|
|
98
|
+
* @param name - Unique within the web ACL; also the rule's default metric name.
|
|
99
|
+
* @param configure - Sets the rule's statement, action and the rest.
|
|
100
|
+
*/
|
|
101
|
+
addRule(name: string, configure: (rule: IWebAclRuleBuilder) => IWebAclRuleBuilder): this;
|
|
102
|
+
/**
|
|
103
|
+
* Protects a regional resource with this web ACL. Call it once per
|
|
104
|
+
* resource. Accepts an ARN, a CDK reference CloudFormation's
|
|
105
|
+
* `AWS::WAFv2::WebACLAssociation` takes (an Application Load Balancer, an
|
|
106
|
+
* AppSync API), or an API Gateway stage, as a concrete value or a `ref`.
|
|
107
|
+
*
|
|
108
|
+
* A CLOUDFRONT web ACL cannot be associated: pass its ARN to the
|
|
109
|
+
* distribution's `webAclId` instead.
|
|
110
|
+
*/
|
|
111
|
+
associateWith(target: Resolvable<WebAclAssociationTarget>): this;
|
|
112
|
+
/**
|
|
113
|
+
* Adds a custom alarm. The metric factory receives the web ACL's
|
|
114
|
+
* {@link WebAclMetrics}, so a rule's metrics come by its name:
|
|
115
|
+
* `(m) => m.rule("PerIpRate").countedRequests()`.
|
|
116
|
+
*/
|
|
117
|
+
addAlarm(key: string, configure: AlarmConfigurer<WebAclMetrics>): this;
|
|
118
|
+
/**
|
|
119
|
+
* Adds a set of custom alarms in one call: each entry exactly as if by
|
|
120
|
+
* `addAlarm(key, configure)`, in the record's iteration order.
|
|
121
|
+
*/
|
|
122
|
+
addAlarms(alarms: Record<string, AlarmConfigurer<WebAclMetrics>>): this;
|
|
123
|
+
/** @internal — see ADR-0005. */
|
|
124
|
+
[COPY_STATE](target: WebAclBuilder): void;
|
|
125
|
+
build(scope: IConstruct, id: string, context?: Record<string, object>): WebAclBuilderResult;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Creates a builder for an AWS WAF web ACL, with its rules, its associations
|
|
129
|
+
* and the visibility settings the rest of the package relies on.
|
|
130
|
+
*
|
|
131
|
+
* By default the web ACL is regional, allows what no rule blocks, publishes
|
|
132
|
+
* CloudWatch metrics for itself and every rule ({@link WEB_ACL_VISIBILITY_DEFAULTS}),
|
|
133
|
+
* and keeps no sampled requests ({@link WEB_ACL_DEFAULTS}).
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* createWebAclBuilder()
|
|
138
|
+
* .associateWith(ref("api", (r: RestApiBuilderResult) => r.api.deploymentStage))
|
|
139
|
+
* .addRule("BlockAdmin", (rule) =>
|
|
140
|
+
* rule.statement({
|
|
141
|
+
* byteMatchStatement: {
|
|
142
|
+
* fieldToMatch: { uriPath: {} },
|
|
143
|
+
* positionalConstraint: "STARTS_WITH",
|
|
144
|
+
* searchString: "/admin",
|
|
145
|
+
* textTransformations: [{ priority: 0, type: "NONE" }],
|
|
146
|
+
* },
|
|
147
|
+
* }).respond(404));
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare function createWebAclBuilder(): IWebAclBuilder;
|
|
151
|
+
export {};
|
|
152
|
+
//# sourceMappingURL=web-acl-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-acl-builder.d.ts","sourceRoot":"","sources":["../../src/web-acl-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createWebAclBuilder = createWebAclBuilder;
|
|
4
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
5
|
+
const aws_wafv2_1 = require("aws-cdk-lib/aws-wafv2");
|
|
6
|
+
const core_1 = require("@composurecdk/core");
|
|
7
|
+
const cloudformation_1 = require("@composurecdk/cloudformation");
|
|
8
|
+
const cloudwatch_1 = require("@composurecdk/cloudwatch");
|
|
9
|
+
const alarms_js_1 = require("./alarms.js");
|
|
10
|
+
const association_js_1 = require("./association.js");
|
|
11
|
+
const custom_response_js_1 = require("./custom-response.js");
|
|
12
|
+
const defaults_js_1 = require("./defaults.js");
|
|
13
|
+
const names_js_1 = require("./names.js");
|
|
14
|
+
const rule_builder_js_1 = require("./rule-builder.js");
|
|
15
|
+
const logging_js_1 = require("./logging.js");
|
|
16
|
+
const metrics_js_1 = require("./metrics.js");
|
|
17
|
+
const scope_js_1 = require("./scope.js");
|
|
18
|
+
const SAMPLING_ANNOTATION = "@composurecdk/wafv2:sampling-unredacted";
|
|
19
|
+
const MAX_RATE_BASED_RULES = 10;
|
|
20
|
+
class WebAclBuilder {
|
|
21
|
+
props = {};
|
|
22
|
+
#rules = [];
|
|
23
|
+
#associations = [];
|
|
24
|
+
#defaultAction = { action: "allow" };
|
|
25
|
+
#customAlarms = [];
|
|
26
|
+
/**
|
|
27
|
+
* What happens to a request no rule decides on. `allow` is the default;
|
|
28
|
+
* `block` can answer with a custom response.
|
|
29
|
+
*/
|
|
30
|
+
defaultAction(action, response) {
|
|
31
|
+
if (action === "allow" && response !== undefined) {
|
|
32
|
+
throw new Error(`defaultAction("allow") cannot take a custom response; only Block can.`);
|
|
33
|
+
}
|
|
34
|
+
this.#defaultAction = { action, response };
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Adds a rule. Rules are evaluated in the order they are added, unless a
|
|
39
|
+
* rule sets its own `.priority(n)`.
|
|
40
|
+
*
|
|
41
|
+
* @param name - Unique within the web ACL; also the rule's default metric name.
|
|
42
|
+
* @param configure - Sets the rule's statement, action and the rest.
|
|
43
|
+
*/
|
|
44
|
+
addRule(name, configure) {
|
|
45
|
+
(0, names_js_1.validateRuleName)(name);
|
|
46
|
+
if (this.#rules.some((r) => r.name === name)) {
|
|
47
|
+
throw new Error(`WebAclBuilder.addRule: duplicate rule name "${name}". Each rule must have a unique name.`);
|
|
48
|
+
}
|
|
49
|
+
this.#rules.push({ name, configure });
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Protects a regional resource with this web ACL. Call it once per
|
|
54
|
+
* resource. Accepts an ARN, a CDK reference CloudFormation's
|
|
55
|
+
* `AWS::WAFv2::WebACLAssociation` takes (an Application Load Balancer, an
|
|
56
|
+
* AppSync API), or an API Gateway stage, as a concrete value or a `ref`.
|
|
57
|
+
*
|
|
58
|
+
* A CLOUDFRONT web ACL cannot be associated: pass its ARN to the
|
|
59
|
+
* distribution's `webAclId` instead.
|
|
60
|
+
*/
|
|
61
|
+
associateWith(target) {
|
|
62
|
+
this.#associations.push(target);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Adds a custom alarm. The metric factory receives the web ACL's
|
|
67
|
+
* {@link WebAclMetrics}, so a rule's metrics come by its name:
|
|
68
|
+
* `(m) => m.rule("PerIpRate").countedRequests()`.
|
|
69
|
+
*/
|
|
70
|
+
addAlarm(key, configure) {
|
|
71
|
+
this.#customAlarms.push(configure(new cloudwatch_1.AlarmDefinitionBuilder(key)));
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Adds a set of custom alarms in one call: each entry exactly as if by
|
|
76
|
+
* `addAlarm(key, configure)`, in the record's iteration order.
|
|
77
|
+
*/
|
|
78
|
+
addAlarms(alarms) {
|
|
79
|
+
for (const [key, configure] of Object.entries(alarms)) {
|
|
80
|
+
this.addAlarm(key, configure);
|
|
81
|
+
}
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
/** @internal — see ADR-0005. */
|
|
85
|
+
[core_1.COPY_STATE](target) {
|
|
86
|
+
target.#rules.push(...this.#rules);
|
|
87
|
+
target.#associations.push(...this.#associations);
|
|
88
|
+
target.#defaultAction = this.#defaultAction;
|
|
89
|
+
target.#customAlarms.push(...this.#customAlarms);
|
|
90
|
+
}
|
|
91
|
+
build(scope, id, context = {}) {
|
|
92
|
+
const owner = `WebAclBuilder "${id}"`;
|
|
93
|
+
const { scope: aclScope = defaults_js_1.WEB_ACL_DEFAULTS.scope, metricName: userMetricName, sampledRequests = defaults_js_1.WEB_ACL_DEFAULTS.sampledRequests, logging: loggingConfig = defaults_js_1.WEB_ACL_DEFAULTS.logging, recommendedAlarms, ...rest } = this.props;
|
|
94
|
+
if (rest.name !== undefined)
|
|
95
|
+
(0, names_js_1.validateWebAclName)(rest.name);
|
|
96
|
+
if (aclScope === "CLOUDFRONT") {
|
|
97
|
+
if (this.#associations.length > 0) {
|
|
98
|
+
throw new Error(`${owner} has scope CLOUDFRONT, so it cannot be associated with associateWith(). ` +
|
|
99
|
+
`Pass its ARN (result.webAcl.attrArn) to the distribution's webAclId instead.`);
|
|
100
|
+
}
|
|
101
|
+
(0, scope_js_1.checkCloudFrontRegion)(scope, id);
|
|
102
|
+
}
|
|
103
|
+
if (userMetricName !== undefined)
|
|
104
|
+
(0, names_js_1.validateMetricName)(userMetricName);
|
|
105
|
+
const bodies = new custom_response_js_1.CustomResponseBodies(owner);
|
|
106
|
+
const { action, response } = this.#defaultAction;
|
|
107
|
+
const defaultAction = action === "allow"
|
|
108
|
+
? { allow: {} }
|
|
109
|
+
: { block: bodies.block("default action", "DefaultAction", response) };
|
|
110
|
+
const rules = this.#buildRules(owner, bodies, sampledRequests);
|
|
111
|
+
const usesSampling = sampledRequests || rules.some((r) => r.sampled);
|
|
112
|
+
// Read through `in`, not as a typed property: `dataProtectionConfig` is not in
|
|
113
|
+
// CfnWebACLProps at this package's aws-cdk-lib floor (2.93.0), so it only
|
|
114
|
+
// reaches `rest` as a pass-through prop on a newer install.
|
|
115
|
+
const hasDataProtection = "dataProtectionConfig" in rest && rest.dataProtectionConfig !== undefined;
|
|
116
|
+
if (usesSampling && !hasDataProtection) {
|
|
117
|
+
aws_cdk_lib_1.Annotations.of(scope).addWarningV2(SAMPLING_ANNOTATION, `${owner} keeps sampled requests without a dataProtectionConfig. Samples keep every ` +
|
|
118
|
+
`header and are not redacted by logging, so credentials in them are visible in the ` +
|
|
119
|
+
`console. Set a dataProtectionConfig, or turn sampling off.`);
|
|
120
|
+
}
|
|
121
|
+
// The derived name comes from the construct's path, so it is read once the construct exists.
|
|
122
|
+
const metricName = userMetricName ??
|
|
123
|
+
aws_cdk_lib_1.Lazy.string({ produce: () => (0, names_js_1.sanitizeMetricName)((0, names_js_1.webAclBaseName)(webAcl)) });
|
|
124
|
+
const webAcl = new aws_wafv2_1.CfnWebACL(scope, id, {
|
|
125
|
+
...rest,
|
|
126
|
+
scope: aclScope,
|
|
127
|
+
defaultAction,
|
|
128
|
+
rules: rules.map((r) => r.property),
|
|
129
|
+
customResponseBodies: bodies.toProperty(),
|
|
130
|
+
visibilityConfig: {
|
|
131
|
+
...defaults_js_1.WEB_ACL_VISIBILITY_DEFAULTS,
|
|
132
|
+
metricName,
|
|
133
|
+
sampledRequestsEnabled: sampledRequests,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
const associations = (0, association_js_1.associate)(scope, id, webAcl.attrArn, this.#associations.map((target) => (0, core_1.resolve)(target, context)));
|
|
137
|
+
const metrics = (0, metrics_js_1.webAclMetrics)(webAcl, metricName, new Map(rules.map((r) => [r.property.name, r.metricName])));
|
|
138
|
+
const alarms = (0, cloudwatch_1.createAlarms)(scope, id, [
|
|
139
|
+
...(0, alarms_js_1.resolveWebAclAlarmDefinitions)(owner, metrics, rules, recommendedAlarms),
|
|
140
|
+
...this.#customAlarms.map((b) => b.resolve(metrics)),
|
|
141
|
+
]);
|
|
142
|
+
return {
|
|
143
|
+
webAcl,
|
|
144
|
+
associations,
|
|
145
|
+
...(0, logging_js_1.configureLogging)(scope, id, owner, webAcl, loggingConfig, context),
|
|
146
|
+
metrics,
|
|
147
|
+
alarms,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
#buildRules(owner, bodies, sampledRequests) {
|
|
151
|
+
const rules = this.#rules.map((entry, index) => {
|
|
152
|
+
const builder = (0, rule_builder_js_1.createWebAclRuleBuilder)();
|
|
153
|
+
entry.configure(builder);
|
|
154
|
+
return builder.toRule({
|
|
155
|
+
name: entry.name,
|
|
156
|
+
owner,
|
|
157
|
+
defaultPriority: index * defaults_js_1.DEFAULT_RULE_PRIORITY_STEP,
|
|
158
|
+
sampledRequests,
|
|
159
|
+
bodies,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
const byPriority = new Map();
|
|
163
|
+
for (const { property: rule } of rules) {
|
|
164
|
+
const other = byPriority.get(rule.priority);
|
|
165
|
+
if (other !== undefined) {
|
|
166
|
+
throw new Error(`${owner}: rules "${other}" and "${rule.name}" both have priority ${String(rule.priority)}. ` +
|
|
167
|
+
`Priorities must be unique; change one rule's .priority(n).`);
|
|
168
|
+
}
|
|
169
|
+
byPriority.set(rule.priority, rule.name);
|
|
170
|
+
}
|
|
171
|
+
const rateBased = rules.filter((r) => r.rateBased);
|
|
172
|
+
if (rateBased.length > MAX_RATE_BASED_RULES) {
|
|
173
|
+
throw new Error(`${owner} has ${String(rateBased.length)} rate-based rules; AWS WAF allows at most ` +
|
|
174
|
+
`${String(MAX_RATE_BASED_RULES)} per web ACL. See https://docs.aws.amazon.com/waf/latest/developerguide/limits.html.`);
|
|
175
|
+
}
|
|
176
|
+
return rules;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Creates a builder for an AWS WAF web ACL, with its rules, its associations
|
|
181
|
+
* and the visibility settings the rest of the package relies on.
|
|
182
|
+
*
|
|
183
|
+
* By default the web ACL is regional, allows what no rule blocks, publishes
|
|
184
|
+
* CloudWatch metrics for itself and every rule ({@link WEB_ACL_VISIBILITY_DEFAULTS}),
|
|
185
|
+
* and keeps no sampled requests ({@link WEB_ACL_DEFAULTS}).
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* createWebAclBuilder()
|
|
190
|
+
* .associateWith(ref("api", (r: RestApiBuilderResult) => r.api.deploymentStage))
|
|
191
|
+
* .addRule("BlockAdmin", (rule) =>
|
|
192
|
+
* rule.statement({
|
|
193
|
+
* byteMatchStatement: {
|
|
194
|
+
* fieldToMatch: { uriPath: {} },
|
|
195
|
+
* positionalConstraint: "STARTS_WITH",
|
|
196
|
+
* searchString: "/admin",
|
|
197
|
+
* textTransformations: [{ priority: 0, type: "NONE" }],
|
|
198
|
+
* },
|
|
199
|
+
* }).respond(404));
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
function createWebAclBuilder() {
|
|
203
|
+
return (0, cloudformation_1.taggedBuilder)(WebAclBuilder);
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=web-acl-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-acl-builder.js","sourceRoot":"","sources":["../../src/web-acl-builder.ts"],"names":[],"mappings":"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"}
|