@dga-itc/aws-cdk-constructs 1.0.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/README.md +219 -0
- package/dist/aws-cdk/constructs/acm.d.ts +28 -0
- package/dist/aws-cdk/constructs/acm.js +239 -0
- package/dist/aws-cdk/constructs/alb.d.ts +28 -0
- package/dist/aws-cdk/constructs/alb.js +304 -0
- package/dist/aws-cdk/constructs/bastion.d.ts +46 -0
- package/dist/aws-cdk/constructs/bastion.js +332 -0
- package/dist/aws-cdk/constructs/cloudfront.d.ts +45 -0
- package/dist/aws-cdk/constructs/cloudfront.js +261 -0
- package/dist/aws-cdk/constructs/ecr.d.ts +17 -0
- package/dist/aws-cdk/constructs/ecr.js +143 -0
- package/dist/aws-cdk/constructs/ecs-cluster.d.ts +21 -0
- package/dist/aws-cdk/constructs/ecs-cluster.js +124 -0
- package/dist/aws-cdk/constructs/ecs-service.d.ts +72 -0
- package/dist/aws-cdk/constructs/ecs-service.js +682 -0
- package/dist/aws-cdk/constructs/efs.d.ts +31 -0
- package/dist/aws-cdk/constructs/efs.js +241 -0
- package/dist/aws-cdk/constructs/elasticache.d.ts +35 -0
- package/dist/aws-cdk/constructs/elasticache.js +210 -0
- package/dist/aws-cdk/constructs/nacl.d.ts +37 -0
- package/dist/aws-cdk/constructs/nacl.js +88 -0
- package/dist/aws-cdk/constructs/nlb.d.ts +39 -0
- package/dist/aws-cdk/constructs/nlb.js +276 -0
- package/dist/aws-cdk/constructs/rds.d.ts +40 -0
- package/dist/aws-cdk/constructs/rds.js +320 -0
- package/dist/aws-cdk/constructs/self-signed-cert.d.ts +83 -0
- package/dist/aws-cdk/constructs/self-signed-cert.js +215 -0
- package/dist/aws-cdk/constructs/sqs.d.ts +30 -0
- package/dist/aws-cdk/constructs/sqs.js +268 -0
- package/dist/aws-cdk/constructs/vpc.d.ts +30 -0
- package/dist/aws-cdk/constructs/vpc.js +423 -0
- package/dist/aws-cdk/constructs/waf.d.ts +37 -0
- package/dist/aws-cdk/constructs/waf.js +350 -0
- package/dist/aws-cdk/interfaces/account-config.d.ts +18 -0
- package/dist/aws-cdk/interfaces/account-config.js +2 -0
- package/dist/aws-cdk/interfaces/acm-config.d.ts +94 -0
- package/dist/aws-cdk/interfaces/acm-config.js +14 -0
- package/dist/aws-cdk/interfaces/alb-config.d.ts +72 -0
- package/dist/aws-cdk/interfaces/alb-config.js +2 -0
- package/dist/aws-cdk/interfaces/bastion-config.d.ts +77 -0
- package/dist/aws-cdk/interfaces/bastion-config.js +10 -0
- package/dist/aws-cdk/interfaces/cloudfront-config.d.ts +154 -0
- package/dist/aws-cdk/interfaces/cloudfront-config.js +15 -0
- package/dist/aws-cdk/interfaces/ecr-config.d.ts +40 -0
- package/dist/aws-cdk/interfaces/ecr-config.js +2 -0
- package/dist/aws-cdk/interfaces/ecs-cluster-config.d.ts +30 -0
- package/dist/aws-cdk/interfaces/ecs-cluster-config.js +2 -0
- package/dist/aws-cdk/interfaces/ecs-service-config.d.ts +237 -0
- package/dist/aws-cdk/interfaces/ecs-service-config.js +2 -0
- package/dist/aws-cdk/interfaces/efs-config.d.ts +56 -0
- package/dist/aws-cdk/interfaces/efs-config.js +7 -0
- package/dist/aws-cdk/interfaces/elasticache-config.d.ts +56 -0
- package/dist/aws-cdk/interfaces/elasticache-config.js +7 -0
- package/dist/aws-cdk/interfaces/nacl-config.d.ts +1 -0
- package/dist/aws-cdk/interfaces/nacl-config.js +3 -0
- package/dist/aws-cdk/interfaces/nlb-config.d.ts +69 -0
- package/dist/aws-cdk/interfaces/nlb-config.js +2 -0
- package/dist/aws-cdk/interfaces/rds-config.d.ts +84 -0
- package/dist/aws-cdk/interfaces/rds-config.js +7 -0
- package/dist/aws-cdk/interfaces/sqs-config.d.ts +145 -0
- package/dist/aws-cdk/interfaces/sqs-config.js +12 -0
- package/dist/aws-cdk/interfaces/tag-config.d.ts +18 -0
- package/dist/aws-cdk/interfaces/tag-config.js +2 -0
- package/dist/aws-cdk/interfaces/vpc-config.d.ts +72 -0
- package/dist/aws-cdk/interfaces/vpc-config.js +2 -0
- package/dist/aws-cdk/interfaces/waf-config.d.ts +180 -0
- package/dist/aws-cdk/interfaces/waf-config.js +2 -0
- package/dist/aws-cdk/utils/priority-tracker.d.ts +60 -0
- package/dist/aws-cdk/utils/priority-tracker.js +131 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +55 -0
- package/dist/terraform-cdk/constructs/alb-listener-rule.d.ts +33 -0
- package/dist/terraform-cdk/constructs/alb-listener-rule.js +81 -0
- package/dist/terraform-cdk/constructs/ecs-service.d.ts +29 -0
- package/dist/terraform-cdk/constructs/ecs-service.js +238 -0
- package/dist/terraform-cdk/interfaces/ecs-service-config.d.ts +53 -0
- package/dist/terraform-cdk/interfaces/ecs-service-config.js +25 -0
- package/dist/terraform-cdk/interfaces/infrastructure-refs.d.ts +16 -0
- package/dist/terraform-cdk/interfaces/infrastructure-refs.js +8 -0
- package/dist/terraform-cdk/utils/priority-tracker.d.ts +60 -0
- package/dist/terraform-cdk/utils/priority-tracker.js +131 -0
- package/package.json +46 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WafConstruct = void 0;
|
|
37
|
+
const constructs_1 = require("constructs");
|
|
38
|
+
const cdk = __importStar(require("aws-cdk-lib"));
|
|
39
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
40
|
+
const wafv2 = __importStar(require("aws-cdk-lib/aws-wafv2"));
|
|
41
|
+
const logs = __importStar(require("aws-cdk-lib/aws-logs"));
|
|
42
|
+
/**
|
|
43
|
+
* WAF v2 Construct — สร้าง WebACL พร้อม AWS Managed Rule Sets
|
|
44
|
+
*
|
|
45
|
+
* Features:
|
|
46
|
+
* - AWS Managed Rule Groups (Common, SQLi, KnownBadInputs, etc.)
|
|
47
|
+
* - Rate Limiting per IP
|
|
48
|
+
* - Geo Blocking
|
|
49
|
+
* - IP Allow/Block Lists
|
|
50
|
+
* - CloudWatch Logging (filtered)
|
|
51
|
+
*
|
|
52
|
+
* Scopes:
|
|
53
|
+
* - CLOUDFRONT → ต้อง deploy ใน us-east-1, ใช้กับ CloudFront
|
|
54
|
+
* - REGIONAL → ใช้กับ ALB, API Gateway, AppSync
|
|
55
|
+
*/
|
|
56
|
+
class WafConstruct extends constructs_1.Construct {
|
|
57
|
+
constructor(scope, id, props) {
|
|
58
|
+
super(scope, id);
|
|
59
|
+
const { config } = props;
|
|
60
|
+
this.removalPolicy = config.removalPolicy === 'retain'
|
|
61
|
+
? aws_cdk_lib_1.RemovalPolicy.RETAIN
|
|
62
|
+
: aws_cdk_lib_1.RemovalPolicy.DESTROY;
|
|
63
|
+
// A. Create IP Sets (if configured)
|
|
64
|
+
const ipSets = this.createIpSets(config);
|
|
65
|
+
// B. Build all rules
|
|
66
|
+
const rules = this.buildRules(config, ipSets);
|
|
67
|
+
// C. Create WebACL
|
|
68
|
+
this.webAcl = this.createWebAcl(config, rules);
|
|
69
|
+
this.webAclArn = this.webAcl.attrArn;
|
|
70
|
+
// D. Create Logging (optional)
|
|
71
|
+
if (config.logging) {
|
|
72
|
+
this.logGroup = this.createLogging(config);
|
|
73
|
+
}
|
|
74
|
+
// E. (Optional) Associate with resources (REGIONAL scope only)
|
|
75
|
+
if (config.scope === 'REGIONAL' && config.associatedResourceArns) {
|
|
76
|
+
this.createAssociations(config.associatedResourceArns);
|
|
77
|
+
}
|
|
78
|
+
// F. Apply Removal Policy
|
|
79
|
+
this.webAcl.applyRemovalPolicy(this.removalPolicy);
|
|
80
|
+
// G. Outputs
|
|
81
|
+
this.createOutputs(config);
|
|
82
|
+
}
|
|
83
|
+
// ==========================================
|
|
84
|
+
// IP Sets
|
|
85
|
+
// ==========================================
|
|
86
|
+
createIpSets(config) {
|
|
87
|
+
const ipSets = new Map();
|
|
88
|
+
if (config.ipAllowList) {
|
|
89
|
+
const allowSet = this.createIpSet('AllowList', config.ipAllowList, config.scope);
|
|
90
|
+
ipSets.set('allow', allowSet);
|
|
91
|
+
}
|
|
92
|
+
if (config.ipBlockList) {
|
|
93
|
+
const blockSet = this.createIpSet('BlockList', config.ipBlockList, config.scope);
|
|
94
|
+
ipSets.set('block', blockSet);
|
|
95
|
+
}
|
|
96
|
+
return ipSets;
|
|
97
|
+
}
|
|
98
|
+
createIpSet(logicalId, ipSetConfig, scope) {
|
|
99
|
+
const ipSet = new wafv2.CfnIPSet(this, logicalId, {
|
|
100
|
+
name: ipSetConfig.name,
|
|
101
|
+
scope,
|
|
102
|
+
ipAddressVersion: ipSetConfig.ipAddressVersion ?? 'IPV4',
|
|
103
|
+
addresses: ipSetConfig.addresses,
|
|
104
|
+
description: `${ipSetConfig.action} list — ${ipSetConfig.name}`,
|
|
105
|
+
});
|
|
106
|
+
ipSet.applyRemovalPolicy(this.removalPolicy);
|
|
107
|
+
return ipSet;
|
|
108
|
+
}
|
|
109
|
+
// ==========================================
|
|
110
|
+
// Build Rules
|
|
111
|
+
// ==========================================
|
|
112
|
+
buildRules(config, ipSets) {
|
|
113
|
+
const rules = [];
|
|
114
|
+
const metricsEnabled = config.enableCloudWatchMetrics !== false;
|
|
115
|
+
const sampledEnabled = config.enableSampledRequests !== false;
|
|
116
|
+
// ── IP Allow List ──
|
|
117
|
+
if (config.ipAllowList && ipSets.has('allow')) {
|
|
118
|
+
rules.push({
|
|
119
|
+
name: config.ipAllowList.name,
|
|
120
|
+
priority: config.ipAllowList.priority,
|
|
121
|
+
statement: {
|
|
122
|
+
ipSetReferenceStatement: {
|
|
123
|
+
arn: ipSets.get('allow').attrArn,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
action: this.resolveAction(config.ipAllowList.action),
|
|
127
|
+
visibilityConfig: {
|
|
128
|
+
cloudWatchMetricsEnabled: metricsEnabled,
|
|
129
|
+
sampledRequestsEnabled: sampledEnabled,
|
|
130
|
+
metricName: config.ipAllowList.name.replace(/[^a-zA-Z0-9]/g, ''),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// ── IP Block List ──
|
|
135
|
+
if (config.ipBlockList && ipSets.has('block')) {
|
|
136
|
+
rules.push({
|
|
137
|
+
name: config.ipBlockList.name,
|
|
138
|
+
priority: config.ipBlockList.priority,
|
|
139
|
+
statement: {
|
|
140
|
+
ipSetReferenceStatement: {
|
|
141
|
+
arn: ipSets.get('block').attrArn,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
action: this.resolveAction(config.ipBlockList.action),
|
|
145
|
+
visibilityConfig: {
|
|
146
|
+
cloudWatchMetricsEnabled: metricsEnabled,
|
|
147
|
+
sampledRequestsEnabled: sampledEnabled,
|
|
148
|
+
metricName: config.ipBlockList.name.replace(/[^a-zA-Z0-9]/g, ''),
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// ── Geo Blocking ──
|
|
153
|
+
if (config.geoBlock) {
|
|
154
|
+
const geoAction = config.geoBlock.action === 'count'
|
|
155
|
+
? { count: {} }
|
|
156
|
+
: { block: {} };
|
|
157
|
+
rules.push({
|
|
158
|
+
name: 'GeoBlocking',
|
|
159
|
+
priority: config.geoBlock.priority,
|
|
160
|
+
statement: {
|
|
161
|
+
geoMatchStatement: {
|
|
162
|
+
countryCodes: config.geoBlock.countryCodes,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
action: geoAction,
|
|
166
|
+
visibilityConfig: {
|
|
167
|
+
cloudWatchMetricsEnabled: metricsEnabled,
|
|
168
|
+
sampledRequestsEnabled: sampledEnabled,
|
|
169
|
+
metricName: 'GeoBlocking',
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// ── Rate Limiting ──
|
|
174
|
+
if (config.rateLimit) {
|
|
175
|
+
const rlAction = config.rateLimit.action === 'count'
|
|
176
|
+
? { count: {} }
|
|
177
|
+
: { block: {} };
|
|
178
|
+
rules.push({
|
|
179
|
+
name: 'RateLimitPerIP',
|
|
180
|
+
priority: config.rateLimit.priority,
|
|
181
|
+
statement: {
|
|
182
|
+
rateBasedStatement: {
|
|
183
|
+
limit: config.rateLimit.limit,
|
|
184
|
+
aggregateKeyType: 'IP',
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
action: rlAction,
|
|
188
|
+
visibilityConfig: {
|
|
189
|
+
cloudWatchMetricsEnabled: metricsEnabled,
|
|
190
|
+
sampledRequestsEnabled: sampledEnabled,
|
|
191
|
+
metricName: 'RateLimitPerIP',
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
// ── AWS Managed Rule Groups ──
|
|
196
|
+
if (config.managedRuleGroups) {
|
|
197
|
+
config.managedRuleGroups.forEach((rg) => {
|
|
198
|
+
rules.push(this.buildManagedRuleGroup(rg, metricsEnabled, sampledEnabled));
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return rules;
|
|
202
|
+
}
|
|
203
|
+
buildManagedRuleGroup(rg, metricsEnabled, sampledEnabled) {
|
|
204
|
+
// Build excluded rules list
|
|
205
|
+
const excludedRules = rg.excludedRules?.map((ruleName) => ({
|
|
206
|
+
name: ruleName,
|
|
207
|
+
}));
|
|
208
|
+
// Build rule action overrides
|
|
209
|
+
const ruleActionOverrides = rg.ruleOverrides
|
|
210
|
+
? Object.entries(rg.ruleOverrides).map(([ruleName, action]) => ({
|
|
211
|
+
name: ruleName,
|
|
212
|
+
actionToUse: this.resolveAction(action),
|
|
213
|
+
}))
|
|
214
|
+
: undefined;
|
|
215
|
+
// Override action: 'none' = enforce rules, 'count' = count only
|
|
216
|
+
const overrideAction = rg.overrideAction === 'count'
|
|
217
|
+
? { count: {} }
|
|
218
|
+
: { none: {} };
|
|
219
|
+
return {
|
|
220
|
+
name: rg.name,
|
|
221
|
+
priority: rg.priority,
|
|
222
|
+
overrideAction,
|
|
223
|
+
statement: {
|
|
224
|
+
managedRuleGroupStatement: {
|
|
225
|
+
vendorName: rg.vendorName ?? 'AWS',
|
|
226
|
+
name: rg.name,
|
|
227
|
+
excludedRules,
|
|
228
|
+
ruleActionOverrides,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
visibilityConfig: {
|
|
232
|
+
cloudWatchMetricsEnabled: metricsEnabled,
|
|
233
|
+
sampledRequestsEnabled: sampledEnabled,
|
|
234
|
+
metricName: rg.name.replace(/[^a-zA-Z0-9]/g, ''),
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
resolveAction(action) {
|
|
239
|
+
switch (action) {
|
|
240
|
+
case 'allow':
|
|
241
|
+
return { allow: {} };
|
|
242
|
+
case 'block':
|
|
243
|
+
return { block: {} };
|
|
244
|
+
case 'count':
|
|
245
|
+
default:
|
|
246
|
+
return { count: {} };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// ==========================================
|
|
250
|
+
// WebACL
|
|
251
|
+
// ==========================================
|
|
252
|
+
createWebAcl(config, rules) {
|
|
253
|
+
const defaultAction = config.defaultAction === 'block'
|
|
254
|
+
? { block: {} }
|
|
255
|
+
: { allow: {} };
|
|
256
|
+
const metricsEnabled = config.enableCloudWatchMetrics !== false;
|
|
257
|
+
const sampledEnabled = config.enableSampledRequests !== false;
|
|
258
|
+
return new wafv2.CfnWebACL(this, 'WebACL', {
|
|
259
|
+
name: config.webAclName,
|
|
260
|
+
description: config.description ?? `WAF WebACL for ${config.stackName}`,
|
|
261
|
+
scope: config.scope,
|
|
262
|
+
defaultAction,
|
|
263
|
+
rules,
|
|
264
|
+
visibilityConfig: {
|
|
265
|
+
cloudWatchMetricsEnabled: metricsEnabled,
|
|
266
|
+
sampledRequestsEnabled: sampledEnabled,
|
|
267
|
+
metricName: config.webAclName.replace(/[^a-zA-Z0-9]/g, ''),
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
// ==========================================
|
|
272
|
+
// Logging
|
|
273
|
+
// ==========================================
|
|
274
|
+
createLogging(config) {
|
|
275
|
+
const logConfig = config.logging;
|
|
276
|
+
const logRemoval = logConfig.removalPolicy === 'retain'
|
|
277
|
+
? aws_cdk_lib_1.RemovalPolicy.RETAIN
|
|
278
|
+
: aws_cdk_lib_1.RemovalPolicy.DESTROY;
|
|
279
|
+
// ⚠️ WAF log group name ต้องขึ้นต้นด้วย 'aws-waf-logs-'
|
|
280
|
+
const logGroup = new logs.LogGroup(this, 'LogGroup', {
|
|
281
|
+
logGroupName: `aws-waf-logs-${config.stackName}`,
|
|
282
|
+
retention: logConfig.retention ?? logs.RetentionDays.ONE_MONTH,
|
|
283
|
+
removalPolicy: logRemoval,
|
|
284
|
+
});
|
|
285
|
+
// Logging Configuration
|
|
286
|
+
const loggingConfig = new wafv2.CfnLoggingConfiguration(this, 'LoggingConfig', {
|
|
287
|
+
resourceArn: this.webAcl.attrArn,
|
|
288
|
+
logDestinationConfigs: [logGroup.logGroupArn],
|
|
289
|
+
});
|
|
290
|
+
// Log filter — default: log blocked requests only
|
|
291
|
+
if (logConfig.logFilter !== 'all') {
|
|
292
|
+
loggingConfig.addPropertyOverride('LoggingFilter', {
|
|
293
|
+
DefaultBehavior: 'DROP',
|
|
294
|
+
Filters: [{
|
|
295
|
+
Behavior: 'KEEP',
|
|
296
|
+
Conditions: [{
|
|
297
|
+
ActionCondition: { Action: 'BLOCK' },
|
|
298
|
+
}],
|
|
299
|
+
Requirement: 'MEETS_ANY',
|
|
300
|
+
}],
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
// Dependency: LoggingConfiguration ต้องสร้างหลัง WebACL
|
|
304
|
+
loggingConfig.addDependency(this.webAcl);
|
|
305
|
+
return logGroup;
|
|
306
|
+
}
|
|
307
|
+
// ==========================================
|
|
308
|
+
// Resource Associations (REGIONAL only)
|
|
309
|
+
// ==========================================
|
|
310
|
+
createAssociations(resourceArns) {
|
|
311
|
+
resourceArns.forEach((arn, index) => {
|
|
312
|
+
const assoc = new wafv2.CfnWebACLAssociation(this, `Association${index}`, {
|
|
313
|
+
webAclArn: this.webAcl.attrArn,
|
|
314
|
+
resourceArn: arn,
|
|
315
|
+
});
|
|
316
|
+
assoc.addDependency(this.webAcl);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
// ==========================================
|
|
320
|
+
// Outputs
|
|
321
|
+
// ==========================================
|
|
322
|
+
createOutputs(config) {
|
|
323
|
+
const stack = cdk.Stack.of(this);
|
|
324
|
+
new cdk.CfnOutput(stack, 'WebACLArn', {
|
|
325
|
+
value: this.webAcl.attrArn,
|
|
326
|
+
description: 'WAF WebACL ARN',
|
|
327
|
+
exportName: `${config.stackName}-WebACL-Arn`,
|
|
328
|
+
});
|
|
329
|
+
new cdk.CfnOutput(stack, 'WebACLId', {
|
|
330
|
+
value: this.webAcl.ref,
|
|
331
|
+
description: 'WAF WebACL ID',
|
|
332
|
+
exportName: `${config.stackName}-WebACL-Id`,
|
|
333
|
+
});
|
|
334
|
+
new cdk.CfnOutput(stack, 'WebACLName', {
|
|
335
|
+
value: config.webAclName,
|
|
336
|
+
description: 'WAF WebACL Name',
|
|
337
|
+
});
|
|
338
|
+
new cdk.CfnOutput(stack, 'WAFScope', {
|
|
339
|
+
value: config.scope,
|
|
340
|
+
description: 'WAF Scope (CLOUDFRONT or REGIONAL)',
|
|
341
|
+
});
|
|
342
|
+
if (this.logGroup) {
|
|
343
|
+
new cdk.CfnOutput(stack, 'LogGroupName', {
|
|
344
|
+
value: this.logGroup.logGroupName,
|
|
345
|
+
description: 'WAF Log Group Name',
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
exports.WafConstruct = WafConstruct;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account Configuration Interface
|
|
3
|
+
*
|
|
4
|
+
* ใช้เป็น "resource" กลางสำหรับกำหนด region ให้แต่ละ environment
|
|
5
|
+
*
|
|
6
|
+
* account ID มาจาก --profile ตอน deploy (CDK_DEFAULT_ACCOUNT) ไม่ต้องใส่ใน config
|
|
7
|
+
*
|
|
8
|
+
* วิธีใช้:
|
|
9
|
+
* 1. สร้าง AccountConfig ใน configs/accounts/ (เช่น myapp-prod.ts)
|
|
10
|
+
* 2. ทุก service config ใส่ accountConfigName เพื่ออ้างอิง
|
|
11
|
+
* 3. bin/main.ts จะ resolve region จาก accountConfigName, account จาก --profile
|
|
12
|
+
*/
|
|
13
|
+
export interface AccountConfig {
|
|
14
|
+
/** ชื่อ account config (ใช้อ้างอิงจาก service configs) */
|
|
15
|
+
name: string;
|
|
16
|
+
/** AWS Region (e.g., 'us-east-1') — override region จาก profile */
|
|
17
|
+
region: string;
|
|
18
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACM Configuration Interface
|
|
3
|
+
*
|
|
4
|
+
* รองรับ 2 โหมด:
|
|
5
|
+
* - request: ขอ certificate ใหม่จาก ACM (DNS/Email validation)
|
|
6
|
+
* - import: นำเข้า certificate ที่มีอยู่แล้ว (PEM format)
|
|
7
|
+
*
|
|
8
|
+
* วิธีใช้:
|
|
9
|
+
* 1. สร้าง AcmConfig ใน configs/acm/ (เช่น demo-dev-acm-e1.ts)
|
|
10
|
+
* 2. ลงทะเบียนใน configs/acm/index.ts
|
|
11
|
+
* 3. bin/main.ts จะสร้าง AcmStack อัตโนมัติ
|
|
12
|
+
*/
|
|
13
|
+
/** โหมดการสร้าง Certificate */
|
|
14
|
+
export type AcmMode = 'request' | 'import';
|
|
15
|
+
/** วิธี Validation สำหรับ request mode */
|
|
16
|
+
export type AcmValidationMethod = 'DNS' | 'EMAIL';
|
|
17
|
+
/**
|
|
18
|
+
* Request mode config — ขอ certificate ใหม่จาก ACM
|
|
19
|
+
*/
|
|
20
|
+
export interface AcmRequestConfig {
|
|
21
|
+
/** Domain name หลัก (e.g., 'example.com') */
|
|
22
|
+
domainName: string;
|
|
23
|
+
/** Subject Alternative Names (e.g., ['*.example.com', 'api.example.com']) */
|
|
24
|
+
subjectAlternativeNames?: string[];
|
|
25
|
+
/** Validation method (default: 'DNS') */
|
|
26
|
+
validationMethod?: AcmValidationMethod;
|
|
27
|
+
/**
|
|
28
|
+
* Route53 Hosted Zone ID — ถ้าใส่จะสร้าง DNS validation record อัตโนมัติ
|
|
29
|
+
* (ใช้ได้เฉพาะ validationMethod = 'DNS')
|
|
30
|
+
*/
|
|
31
|
+
hostedZoneId?: string;
|
|
32
|
+
/** Hosted Zone name (required if hostedZoneId is set) */
|
|
33
|
+
hostedZoneName?: string;
|
|
34
|
+
/** Key algorithm (default: 'RSA_2048') */
|
|
35
|
+
keyAlgorithm?: 'RSA_2048' | 'EC_prime256v1' | 'EC_secp384r1';
|
|
36
|
+
/**
|
|
37
|
+
* Enable transparency logging (default: true)
|
|
38
|
+
* Certificate Transparency Logging ช่วยตรวจสอบว่า cert ถูก issue อย่างถูกต้อง
|
|
39
|
+
*/
|
|
40
|
+
transparencyLoggingEnabled?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Import mode config — นำเข้า certificate ที่มีอยู่แล้ว
|
|
44
|
+
*
|
|
45
|
+
* ใช้ SSM Parameter Store หรือ Secrets Manager เก็บค่า PEM
|
|
46
|
+
* (ไม่ควร hardcode ค่า PEM ใน config โดยตรง)
|
|
47
|
+
*/
|
|
48
|
+
export interface AcmImportConfig {
|
|
49
|
+
/** Certificate body (PEM format) — หรือ SSM parameter name ที่เก็บค่า */
|
|
50
|
+
certificateBodyParam: string;
|
|
51
|
+
/** Certificate private key (PEM format) — หรือ Secrets Manager ARN ที่เก็บค่า */
|
|
52
|
+
privateKeyParam: string;
|
|
53
|
+
/** Certificate chain (PEM format) — optional, หรือ SSM parameter name */
|
|
54
|
+
certificateChainParam?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* ACM Config — รวม request + import mode
|
|
58
|
+
*/
|
|
59
|
+
export interface AcmConfig {
|
|
60
|
+
/** ชื่อ CloudFormation stack (ถ้าใช้ regions จะถูก suffix ด้วย region shorthand อัตโนมัติ) */
|
|
61
|
+
stackName: string;
|
|
62
|
+
/** AWS Region (ถ้าไม่กำหนดจะใช้ CDK_DEFAULT_REGION) */
|
|
63
|
+
region?: string;
|
|
64
|
+
/** AWS Account (ถ้าไม่กำหนดจะใช้ CDK_DEFAULT_ACCOUNT) */
|
|
65
|
+
account?: string;
|
|
66
|
+
/** อ้างอิง Account Config name (ดู configs/accounts/) */
|
|
67
|
+
accountConfigName?: string;
|
|
68
|
+
/**
|
|
69
|
+
* สร้าง certificate ในหลาย regions พร้อมกัน
|
|
70
|
+
* เช่น ['ap-southeast-7', 'us-east-1']
|
|
71
|
+
*
|
|
72
|
+
* ถ้าใส่ regions:
|
|
73
|
+
* - stackName จะถูก suffix ด้วย region shorthand (e.g., 'my-acm' → 'my-acm-apse7', 'my-acm-use1')
|
|
74
|
+
* - จะสร้าง 1 stack ต่อ 1 region
|
|
75
|
+
* - ไม่ต้องใส่ region / accountConfigName (จะถูก override)
|
|
76
|
+
*
|
|
77
|
+
* ถ้าไม่ใส่ regions → ใช้ region เดียวจาก accountConfigName / region / CDK_DEFAULT_REGION
|
|
78
|
+
*/
|
|
79
|
+
regions?: string[];
|
|
80
|
+
/** โหมดการสร้าง certificate */
|
|
81
|
+
mode: AcmMode;
|
|
82
|
+
/** Config สำหรับ request mode (required if mode = 'request') */
|
|
83
|
+
request?: AcmRequestConfig;
|
|
84
|
+
/** Config สำหรับ import mode (required if mode = 'import') */
|
|
85
|
+
import?: AcmImportConfig;
|
|
86
|
+
/** Removal policy (default: 'destroy') */
|
|
87
|
+
removalPolicy?: 'destroy' | 'retain';
|
|
88
|
+
/** Export certificate ARN เป็น CfnOutput (default: true) */
|
|
89
|
+
enableExport?: boolean;
|
|
90
|
+
/** อ้างอิง Tag Config name (ดู configs/tags/) */
|
|
91
|
+
tagConfigName?: string;
|
|
92
|
+
/** Tags เสริม — merge กับ tagConfigName (override ถ้า key ซ้ำ) */
|
|
93
|
+
tags?: Record<string, string>;
|
|
94
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ACM Configuration Interface
|
|
4
|
+
*
|
|
5
|
+
* รองรับ 2 โหมด:
|
|
6
|
+
* - request: ขอ certificate ใหม่จาก ACM (DNS/Email validation)
|
|
7
|
+
* - import: นำเข้า certificate ที่มีอยู่แล้ว (PEM format)
|
|
8
|
+
*
|
|
9
|
+
* วิธีใช้:
|
|
10
|
+
* 1. สร้าง AcmConfig ใน configs/acm/ (เช่น demo-dev-acm-e1.ts)
|
|
11
|
+
* 2. ลงทะเบียนใน configs/acm/index.ts
|
|
12
|
+
* 3. bin/main.ts จะสร้าง AcmStack อัตโนมัติ
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
|
|
2
|
+
export type AlbScheme = 'internet-facing' | 'internal';
|
|
3
|
+
export interface AlbListenerConfig {
|
|
4
|
+
/** Enable HTTP listener (default: true) */
|
|
5
|
+
httpEnabled?: boolean;
|
|
6
|
+
/** HTTP port (default: 80) */
|
|
7
|
+
httpPort?: number;
|
|
8
|
+
/** Redirect HTTP to HTTPS (default: true if HTTPS is enabled) */
|
|
9
|
+
httpRedirectToHttps?: boolean;
|
|
10
|
+
/** Enable HTTPS listener (default: true) */
|
|
11
|
+
httpsEnabled?: boolean;
|
|
12
|
+
/** HTTPS port (default: 443) */
|
|
13
|
+
httpsPort?: number;
|
|
14
|
+
/** ACM certificate ARN - required if httpsEnabled */
|
|
15
|
+
certificateArn?: string;
|
|
16
|
+
/** Additional certificates for same listener */
|
|
17
|
+
additionalCertificateArns?: string[];
|
|
18
|
+
/** SSL policy (default: RECOMMENDED_TLS) */
|
|
19
|
+
sslPolicy?: elbv2.SslPolicy;
|
|
20
|
+
}
|
|
21
|
+
export interface AlbSecurityConfig {
|
|
22
|
+
/** Allow HTTP from 0.0.0.0/0 (default: true for internet-facing) */
|
|
23
|
+
allowHttpFromAnywhere?: boolean;
|
|
24
|
+
/** Allow HTTPS from 0.0.0.0/0 (default: true for internet-facing) */
|
|
25
|
+
allowHttpsFromAnywhere?: boolean;
|
|
26
|
+
/** Additional CIDRs to allow */
|
|
27
|
+
allowFromCidrs?: string[];
|
|
28
|
+
/** Additional Security Group IDs to allow */
|
|
29
|
+
allowFromSecurityGroupIds?: string[];
|
|
30
|
+
/** Drop invalid HTTP headers (default: true) */
|
|
31
|
+
dropInvalidHeaderFields?: boolean;
|
|
32
|
+
/** Idle timeout in seconds (default: 60) */
|
|
33
|
+
idleTimeoutSeconds?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* VpcRef - อ้างอิง VPC stack ใน CDK app เดียวกัน
|
|
37
|
+
*/
|
|
38
|
+
export interface AlbVpcRefSource {
|
|
39
|
+
vpcStackName: string;
|
|
40
|
+
/** ใช้ subnet ชื่ออะไร (default: 'public') */
|
|
41
|
+
subnetName?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface AlbConfig {
|
|
44
|
+
stackName: string;
|
|
45
|
+
albName: string;
|
|
46
|
+
region?: string;
|
|
47
|
+
account?: string;
|
|
48
|
+
/** อ้างอิง Account Config name (ดู configs/accounts/) */
|
|
49
|
+
accountConfigName?: string;
|
|
50
|
+
/** Custom Security Group name (optional, removes CDK hash suffix) */
|
|
51
|
+
securityGroupName?: string;
|
|
52
|
+
/** ALB scheme */
|
|
53
|
+
scheme: AlbScheme;
|
|
54
|
+
/** VPC source - อ้างอิง VPC stack */
|
|
55
|
+
source: AlbVpcRefSource;
|
|
56
|
+
/** Listener configuration */
|
|
57
|
+
listener?: AlbListenerConfig;
|
|
58
|
+
/** Security configuration */
|
|
59
|
+
security?: AlbSecurityConfig;
|
|
60
|
+
/** Deletion protection (default: false) */
|
|
61
|
+
deletionProtection?: boolean;
|
|
62
|
+
/** Removal policy (default: 'destroy') */
|
|
63
|
+
removalPolicy?: 'destroy' | 'retain';
|
|
64
|
+
/** Access logs */
|
|
65
|
+
accessLogsEnabled?: boolean;
|
|
66
|
+
accessLogsBucket?: string;
|
|
67
|
+
accessLogsPrefix?: string;
|
|
68
|
+
/** อ้างอิง Tag Config name (ดู configs/tags/) */
|
|
69
|
+
tagConfigName?: string;
|
|
70
|
+
/** Tags เสริม — merge กับ tagConfigName (override ถ้า key ซ้ำ) */
|
|
71
|
+
tags?: Record<string, string>;
|
|
72
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bastion Host Configuration Interface
|
|
3
|
+
*
|
|
4
|
+
* สำหรับสร้าง EC2 Bastion Host เพื่อ:
|
|
5
|
+
* - เข้าถึง RDS สำหรับ migrate ข้อมูล
|
|
6
|
+
* - Mount EFS สำหรับอัปโหลดไฟล์
|
|
7
|
+
* - ส่ง SSH key ให้ vendor ได้
|
|
8
|
+
*/
|
|
9
|
+
export interface BastionVpcRefSource {
|
|
10
|
+
vpcStackName: string;
|
|
11
|
+
/** ใช้ subnet ชื่ออะไรสำหรับวาง Bastion (default: 'private') */
|
|
12
|
+
subnetName?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface BastionEfsMount {
|
|
15
|
+
/** EFS stack name สำหรับ cross-stack reference */
|
|
16
|
+
efsStackName: string;
|
|
17
|
+
/** Mount path บน EC2 (default: '/mnt/efs') */
|
|
18
|
+
mountPath?: string;
|
|
19
|
+
/** Access Point name ที่จะใช้ (optional) */
|
|
20
|
+
accessPointName?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface BastionRdsAccess {
|
|
23
|
+
/** RDS stack name สำหรับ cross-stack reference */
|
|
24
|
+
rdsStackName: string;
|
|
25
|
+
/** Port ของ database (default: 3306) */
|
|
26
|
+
port?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface BastionRedisAccess {
|
|
29
|
+
/** ElastiCache stack name สำหรับ cross-stack reference */
|
|
30
|
+
elastiCacheStackName: string;
|
|
31
|
+
/** Port ของ Redis (default: 6379) */
|
|
32
|
+
port?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface BastionConfig {
|
|
35
|
+
stackName: string;
|
|
36
|
+
instanceName: string;
|
|
37
|
+
region?: string;
|
|
38
|
+
account?: string;
|
|
39
|
+
/** อ้างอิง Account Config name (ดู configs/accounts/) */
|
|
40
|
+
accountConfigName?: string;
|
|
41
|
+
/** VPC source - อ้างอิง VPC stack */
|
|
42
|
+
source: BastionVpcRefSource;
|
|
43
|
+
/** Custom Security Group name (optional) */
|
|
44
|
+
securityGroupName?: string;
|
|
45
|
+
/** EC2 Instance Type (default: 't3.micro') */
|
|
46
|
+
instanceType?: string;
|
|
47
|
+
/** Key Pair name - ถ้าไม่ระบุจะสร้างใหม่ */
|
|
48
|
+
keyPairName?: string;
|
|
49
|
+
/** สร้าง Key Pair ใหม่หรือไม่ (default: true) */
|
|
50
|
+
createKeyPair?: boolean;
|
|
51
|
+
/** AMI ID (optional - ถ้าไม่ระบุจะใช้ Amazon Linux 2023) */
|
|
52
|
+
amiId?: string;
|
|
53
|
+
/** Allowed SSH CIDR blocks (default: ไม่เปิด SSH จากข้างนอก) */
|
|
54
|
+
allowedSshCidrs?: string[];
|
|
55
|
+
/** Associate public IP address (default: false) - ต้องวางใน public subnet */
|
|
56
|
+
associatePublicIpAddress?: boolean;
|
|
57
|
+
/** EBS volume size in GB (default: 20) */
|
|
58
|
+
volumeSize?: number;
|
|
59
|
+
/** EBS volume type (default: 'gp3') */
|
|
60
|
+
volumeType?: 'gp2' | 'gp3';
|
|
61
|
+
/** EFS mount configuration (optional) */
|
|
62
|
+
efsMount?: BastionEfsMount;
|
|
63
|
+
/** RDS access configuration (optional) */
|
|
64
|
+
rdsAccess?: BastionRdsAccess;
|
|
65
|
+
/** Redis access configuration (optional) */
|
|
66
|
+
redisAccess?: BastionRedisAccess;
|
|
67
|
+
/** Install MySQL/MariaDB client (default: true) */
|
|
68
|
+
installMysqlClient?: boolean;
|
|
69
|
+
/** Additional user data script (optional) */
|
|
70
|
+
additionalUserData?: string;
|
|
71
|
+
/** Removal policy (default: 'destroy' - bastion เป็น temporary) */
|
|
72
|
+
removalPolicy?: 'destroy' | 'retain';
|
|
73
|
+
/** อ้างอิง Tag Config name (ดู configs/tags/) */
|
|
74
|
+
tagConfigName?: string;
|
|
75
|
+
/** Tags เสริม — merge กับ tagConfigName (override ถ้า key ซ้ำ) */
|
|
76
|
+
tags?: Record<string, string>;
|
|
77
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bastion Host Configuration Interface
|
|
4
|
+
*
|
|
5
|
+
* สำหรับสร้าง EC2 Bastion Host เพื่อ:
|
|
6
|
+
* - เข้าถึง RDS สำหรับ migrate ข้อมูล
|
|
7
|
+
* - Mount EFS สำหรับอัปโหลดไฟล์
|
|
8
|
+
* - ส่ง SSH key ให้ vendor ได้
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|