@quiltdata/benchling-webhook 0.9.0-20251129T063536Z → 0.9.0-20251130T062440Z

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.
@@ -36,11 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.HttpApiGateway = void 0;
37
37
  const cdk = __importStar(require("aws-cdk-lib"));
38
38
  const apigatewayv2 = __importStar(require("aws-cdk-lib/aws-apigatewayv2"));
39
- const apigatewayv2Authorizers = __importStar(require("aws-cdk-lib/aws-apigatewayv2-authorizers"));
40
39
  const apigatewayv2Integrations = __importStar(require("aws-cdk-lib/aws-apigatewayv2-integrations"));
40
+ const wafv2 = __importStar(require("aws-cdk-lib/aws-wafv2"));
41
41
  const logs = __importStar(require("aws-cdk-lib/aws-logs"));
42
- const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
43
- const iam = __importStar(require("aws-cdk-lib/aws-iam"));
42
+ const waf_web_acl_1 = require("./waf-web-acl");
44
43
  class HttpApiGateway {
45
44
  constructor(scope, id, props) {
46
45
  // Access logs for HTTP API
@@ -49,126 +48,42 @@ class HttpApiGateway {
49
48
  retention: logs.RetentionDays.ONE_WEEK,
50
49
  removalPolicy: cdk.RemovalPolicy.DESTROY,
51
50
  });
52
- // VPC Link for private integration with Cloud Map service
51
+ // VPC Link for private integration with Network Load Balancer
52
+ // v0.9.0: NLB provides reliable health checks for ECS tasks
53
53
  this.vpcLink = new apigatewayv2.VpcLink(scope, "VpcLink", {
54
54
  vpc: props.vpc,
55
55
  securityGroups: [props.serviceSecurityGroup],
56
56
  vpcLinkName: "benchling-webhook-vpclink",
57
57
  });
58
- // Lambda authorizer for webhook verification (HTTP API v2 SIMPLE response)
59
- const verificationEnabled = props.config.security?.enableVerification !== false;
60
- const benchlingSecretArn = props.config.benchling.secretArn;
61
- if (!benchlingSecretArn) {
62
- throw new Error("Benchling secret ARN is required to configure the Lambda authorizer");
63
- }
64
- // Create authorizer Lambda if verification is enabled
65
- let httpAuthorizer;
66
- if (verificationEnabled) {
67
- this.authorizerLogGroup = new logs.LogGroup(scope, "WebhookAuthorizerLogGroup", {
68
- retention: logs.RetentionDays.ONE_WEEK,
69
- removalPolicy: cdk.RemovalPolicy.DESTROY,
70
- });
71
- // Lambda bundling: Install dependencies at build time
72
- // NOTE: For local development, pre-build with: make lambda-bundle
73
- // This reduces CDK build time by using cached wheels
74
- const bundlingCommands = [
75
- "set -euo pipefail",
76
- "export PIP_NO_BUILD_ISOLATION=1 PIP_ONLY_BINARY=:all: PIP_DISABLE_PIP_VERSION_CHECK=1 PIP_CACHE_DIR=/tmp/pipcache",
77
- "pip install -q --platform manylinux2014_x86_64 --implementation cp --python-version 3.12 --abi cp312 --only-binary=:all: -t /asset-output -r /asset-input/lambda/authorizer/requirements.txt -c /asset-input/lambda/authorizer/constraints.txt",
78
- "cp /asset-input/docker/src/lambda_authorizer.py /asset-output/index.py",
79
- ].join(" && ");
80
- const authorizerCode = process.env.NODE_ENV === "test"
81
- ? lambda.Code.fromInline("def handler(event, context):\n return {'isAuthorized': True}")
82
- : lambda.Code.fromAsset(".", {
83
- bundling: {
84
- image: lambda.Runtime.PYTHON_3_12.bundlingImage,
85
- command: ["bash", "-c", bundlingCommands],
86
- },
87
- });
88
- this.authorizer = new lambda.Function(scope, "WebhookAuthorizerFunction", {
89
- runtime: lambda.Runtime.PYTHON_3_12,
90
- handler: "index.handler",
91
- memorySize: 128,
92
- timeout: cdk.Duration.seconds(10),
93
- architecture: lambda.Architecture.X86_64,
94
- description: "Benchling webhook signature verification (HTTP API v2)",
95
- environment: {
96
- BENCHLING_SECRET_ARN: benchlingSecretArn,
97
- LOG_LEVEL: props.config.logging?.level || "INFO",
98
- },
99
- code: authorizerCode,
100
- logGroup: this.authorizerLogGroup,
101
- });
102
- // Grant Secrets Manager access
103
- this.authorizer.addToRolePolicy(new iam.PolicyStatement({
104
- actions: ["secretsmanager:GetSecretValue"],
105
- resources: [benchlingSecretArn],
106
- }));
107
- // Create HTTP Lambda Authorizer with SIMPLE response format
108
- // Note: HTTP API v2 uses a simpler response format than REST API (REQUEST authorizer)
109
- httpAuthorizer = new apigatewayv2Authorizers.HttpLambdaAuthorizer("WebhookAuthorizer", this.authorizer, {
110
- authorizerName: "WebhookAuthorizer",
111
- identitySource: [
112
- "$request.header.webhook-signature",
113
- "$request.header.webhook-id",
114
- "$request.header.webhook-timestamp",
115
- ],
116
- responseTypes: [apigatewayv2Authorizers.HttpLambdaResponseType.SIMPLE],
117
- resultsCacheTtl: cdk.Duration.seconds(0), // No caching for HMAC signatures
118
- });
119
- }
120
58
  // Create HTTP API v2
121
59
  this.api = new apigatewayv2.HttpApi(scope, "BenchlingWebhookHttpAPI", {
122
60
  apiName: "BenchlingWebhookHttpAPI",
123
- description: "HTTP API for Benchling webhook integration (v0.9.0+)",
61
+ description: "HTTP API for Benchling webhook integration (v0.9.0+ with NLB and optional WAF)",
62
+ });
63
+ // Network Load Balancer integration via VPC Link
64
+ // v0.9.0: Replaced Cloud Map with NLB for reliable routing
65
+ const integration = new apigatewayv2Integrations.HttpNlbIntegration("NlbIntegration", props.nlbListener, {
66
+ vpcLink: this.vpcLink,
67
+ });
68
+ // Webhook routes - HMAC verification handled by FastAPI application
69
+ // Event webhooks
70
+ this.api.addRoutes({
71
+ path: "/event",
72
+ methods: [apigatewayv2.HttpMethod.POST],
73
+ integration,
74
+ });
75
+ // Lifecycle webhooks
76
+ this.api.addRoutes({
77
+ path: "/lifecycle",
78
+ methods: [apigatewayv2.HttpMethod.POST],
79
+ integration,
80
+ });
81
+ // Canvas webhooks
82
+ this.api.addRoutes({
83
+ path: "/canvas",
84
+ methods: [apigatewayv2.HttpMethod.POST],
85
+ integration,
124
86
  });
125
- // Service Discovery integration via VPC Link
126
- const integration = new apigatewayv2Integrations.HttpServiceDiscoveryIntegration("CloudMapIntegration", props.cloudMapService, { vpcLink: this.vpcLink });
127
- // Webhook routes - protected by Lambda authorizer
128
- if (httpAuthorizer) {
129
- // Event webhooks (protected)
130
- this.api.addRoutes({
131
- path: "/event",
132
- methods: [apigatewayv2.HttpMethod.POST],
133
- integration,
134
- authorizer: httpAuthorizer,
135
- });
136
- // Lifecycle webhooks (protected)
137
- this.api.addRoutes({
138
- path: "/lifecycle",
139
- methods: [apigatewayv2.HttpMethod.POST],
140
- integration,
141
- authorizer: httpAuthorizer,
142
- });
143
- // Canvas webhooks (protected)
144
- this.api.addRoutes({
145
- path: "/canvas",
146
- methods: [apigatewayv2.HttpMethod.POST],
147
- integration,
148
- authorizer: httpAuthorizer,
149
- });
150
- }
151
- else {
152
- // No authorizer - allow all webhook routes (for testing only)
153
- this.api.addRoutes({
154
- path: "/event",
155
- methods: [apigatewayv2.HttpMethod.POST],
156
- integration,
157
- });
158
- this.api.addRoutes({
159
- path: "/lifecycle",
160
- methods: [apigatewayv2.HttpMethod.POST],
161
- integration,
162
- });
163
- this.api.addRoutes({
164
- path: "/canvas",
165
- methods: [apigatewayv2.HttpMethod.POST],
166
- integration,
167
- });
168
- console.warn("WARNING: Webhook signature verification is DISABLED. " +
169
- "This should only be used for testing. Enable it in production by setting " +
170
- "config.security.enableVerification = true");
171
- }
172
87
  // Health check routes - always unauthenticated
173
88
  this.api.addRoutes({
174
89
  path: "/health",
@@ -191,6 +106,31 @@ class HttpApiGateway {
191
106
  methods: [apigatewayv2.HttpMethod.GET],
192
107
  integration,
193
108
  });
109
+ // Conditionally create WAF Web ACL for IP filtering
110
+ // Only deploy WAF if webhookAllowList is configured
111
+ const webhookAllowList = props.config.security?.webhookAllowList || "";
112
+ if (webhookAllowList) {
113
+ console.log("WAF IP filtering: ENABLED");
114
+ // Create WAF Web ACL
115
+ this.wafWebAcl = new waf_web_acl_1.WafWebAcl(scope, "WafWebAcl", {
116
+ ipAllowList: webhookAllowList,
117
+ });
118
+ // Construct HTTP API ARN for WAF association
119
+ // Format: arn:aws:apigateway:{region}::/apis/{api-id}/stages/{stage-name}
120
+ const apiArn = cdk.Stack.of(scope).formatArn({
121
+ service: "apigateway",
122
+ resource: `/apis/${this.api.apiId}/stages/${this.api.defaultStage?.stageName || "$default"}`,
123
+ arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME,
124
+ });
125
+ // Associate WAF with HTTP API
126
+ new wafv2.CfnWebACLAssociation(scope, "WafAssociation", {
127
+ resourceArn: apiArn,
128
+ webAclArn: this.wafWebAcl.webAcl.attrArn,
129
+ });
130
+ }
131
+ else {
132
+ console.log("WAF IP filtering: DISABLED (no webhookAllowList configured)");
133
+ }
194
134
  // Configure access logging on the default stage
195
135
  const stage = this.api.defaultStage?.node.defaultChild;
196
136
  if (stage) {
@@ -207,16 +147,18 @@ class HttpApiGateway {
207
147
  responseLength: "$context.responseLength",
208
148
  errorMessage: "$context.error.message",
209
149
  errorType: "$context.error.messageString",
210
- authorizerError: "$context.authorizer.error",
211
150
  }),
212
151
  };
213
152
  }
214
- // Output verification status
153
+ // Webhook verification status
154
+ const verificationEnabled = props.config.security?.enableVerification !== false;
215
155
  if (verificationEnabled) {
216
- console.log("Webhook signature verification: ENABLED (Lambda authorizer)");
156
+ console.log("Webhook signature verification: ENABLED (FastAPI application)");
217
157
  }
218
158
  else {
219
- console.log("Webhook signature verification: DISABLED (testing mode)");
159
+ console.warn("WARNING: Webhook signature verification is DISABLED. " +
160
+ "This should only be used for testing. Enable it in production by setting " +
161
+ "config.security.enableVerification = true");
220
162
  }
221
163
  }
222
164
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http-api-gateway.js","sourceRoot":"","sources":["../../lib/http-api-gateway.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http-api-gateway.js","sourceRoot":"","sources":["../../lib/http-api-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import * as ec2 from "aws-cdk-lib/aws-ec2";
2
+ import * as elbv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
3
+ import { Construct } from "constructs";
4
+ export interface NetworkLoadBalancerProps {
5
+ readonly vpc: ec2.IVpc;
6
+ }
7
+ /**
8
+ * Network Load Balancer for ECS Fargate service
9
+ *
10
+ * Provides reliable health checks and routing for ECS tasks.
11
+ * Replaces Cloud Map service discovery which has issues with custom health checks.
12
+ *
13
+ * Architecture:
14
+ * - Internal NLB (not internet-facing)
15
+ * - TCP listener on port 80
16
+ * - Target Group with IP targets for ECS Fargate tasks
17
+ * - HTTP health checks on /health endpoint
18
+ *
19
+ * @since v0.9.0
20
+ */
21
+ export declare class NetworkLoadBalancer extends Construct {
22
+ readonly loadBalancer: elbv2.NetworkLoadBalancer;
23
+ readonly targetGroup: elbv2.NetworkTargetGroup;
24
+ readonly listener: elbv2.NetworkListener;
25
+ constructor(scope: Construct, id: string, props: NetworkLoadBalancerProps);
26
+ }
27
+ //# sourceMappingURL=network-load-balancer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-load-balancer.d.ts","sourceRoot":"","sources":["../../lib/network-load-balancer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,KAAK,MAAM,wCAAwC,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,wBAAwB;IACrC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAC9C,SAAgB,YAAY,EAAE,KAAK,CAAC,mBAAmB,CAAC;IACxD,SAAgB,WAAW,EAAE,KAAK,CAAC,kBAAkB,CAAC;IACtD,SAAgB,QAAQ,EAAE,KAAK,CAAC,eAAe,CAAC;gBAEpC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB;CA4D5E"}
@@ -0,0 +1,111 @@
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.NetworkLoadBalancer = void 0;
37
+ const cdk = __importStar(require("aws-cdk-lib"));
38
+ const elbv2 = __importStar(require("aws-cdk-lib/aws-elasticloadbalancingv2"));
39
+ const constructs_1 = require("constructs");
40
+ /**
41
+ * Network Load Balancer for ECS Fargate service
42
+ *
43
+ * Provides reliable health checks and routing for ECS tasks.
44
+ * Replaces Cloud Map service discovery which has issues with custom health checks.
45
+ *
46
+ * Architecture:
47
+ * - Internal NLB (not internet-facing)
48
+ * - TCP listener on port 80
49
+ * - Target Group with IP targets for ECS Fargate tasks
50
+ * - HTTP health checks on /health endpoint
51
+ *
52
+ * @since v0.9.0
53
+ */
54
+ class NetworkLoadBalancer extends constructs_1.Construct {
55
+ constructor(scope, id, props) {
56
+ super(scope, id);
57
+ // Create internal Network Load Balancer
58
+ // Internal = only accessible within VPC (not from internet)
59
+ this.loadBalancer = new elbv2.NetworkLoadBalancer(this, "LoadBalancer", {
60
+ vpc: props.vpc,
61
+ internetFacing: false,
62
+ loadBalancerName: "benchling-webhook-nlb",
63
+ vpcSubnets: {
64
+ subnets: props.vpc.privateSubnets,
65
+ },
66
+ crossZoneEnabled: true, // Distribute traffic evenly across AZs
67
+ });
68
+ // Create Target Group for ECS tasks
69
+ // IP target type is required for Fargate tasks
70
+ this.targetGroup = new elbv2.NetworkTargetGroup(this, "TargetGroup", {
71
+ vpc: props.vpc,
72
+ port: 8080,
73
+ protocol: elbv2.Protocol.TCP,
74
+ targetType: elbv2.TargetType.IP, // Required for Fargate
75
+ targetGroupName: "benchling-webhook-tg",
76
+ deregistrationDelay: cdk.Duration.seconds(30), // Quick deregistration
77
+ // HTTP health checks for application health
78
+ // NLB supports HTTP health checks even with TCP listener
79
+ healthCheck: {
80
+ enabled: true,
81
+ protocol: elbv2.Protocol.HTTP,
82
+ path: "/health",
83
+ interval: cdk.Duration.seconds(30),
84
+ timeout: cdk.Duration.seconds(10),
85
+ healthyThresholdCount: 2, // 2 successful checks = healthy
86
+ unhealthyThresholdCount: 3, // 3 failed checks = unhealthy
87
+ healthyHttpCodes: "200",
88
+ },
89
+ });
90
+ // Create TCP listener on port 80
91
+ // API Gateway will connect to this via VPC Link
92
+ this.listener = this.loadBalancer.addListener("Listener", {
93
+ port: 80,
94
+ protocol: elbv2.Protocol.TCP,
95
+ defaultTargetGroups: [this.targetGroup],
96
+ });
97
+ // Outputs for debugging
98
+ new cdk.CfnOutput(this, "LoadBalancerDnsName", {
99
+ value: this.loadBalancer.loadBalancerDnsName,
100
+ description: "Network Load Balancer DNS name",
101
+ exportName: "BenchlingWebhookNLBDnsName",
102
+ });
103
+ new cdk.CfnOutput(this, "TargetGroupArn", {
104
+ value: this.targetGroup.targetGroupArn,
105
+ description: "Target Group ARN for ECS tasks",
106
+ exportName: "BenchlingWebhookTargetGroupArn",
107
+ });
108
+ }
109
+ }
110
+ exports.NetworkLoadBalancer = NetworkLoadBalancer;
111
+ //# sourceMappingURL=network-load-balancer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-load-balancer.js","sourceRoot":"","sources":["../../lib/network-load-balancer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAmC;AAEnC,8EAAgE;AAChE,2CAAuC;AAMvC;;;;;;;;;;;;;GAaG;AACH,MAAa,mBAAoB,SAAQ,sBAAS;IAK9C,YAAY,KAAgB,EAAE,EAAU,EAAE,KAA+B;QACrE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEjB,wCAAwC;QACxC,4DAA4D;QAC5D,IAAI,CAAC,YAAY,GAAG,IAAI,KAAK,CAAC,mBAAmB,CAAC,IAAI,EAAE,cAAc,EAAE;YACpE,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,cAAc,EAAE,KAAK;YACrB,gBAAgB,EAAE,uBAAuB;YACzC,UAAU,EAAE;gBACR,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc;aACpC;YACD,gBAAgB,EAAE,IAAI,EAAE,uCAAuC;SAClE,CAAC,CAAC;QAEH,oCAAoC;QACpC,+CAA+C;QAC/C,IAAI,CAAC,WAAW,GAAG,IAAI,KAAK,CAAC,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE;YACjE,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG;YAC5B,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,uBAAuB;YACxD,eAAe,EAAE,sBAAsB;YACvC,mBAAmB,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,uBAAuB;YAEtE,4CAA4C;YAC5C,yDAAyD;YACzD,WAAW,EAAE;gBACT,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI;gBAC7B,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAClC,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACjC,qBAAqB,EAAE,CAAC,EAAG,gCAAgC;gBAC3D,uBAAuB,EAAE,CAAC,EAAE,8BAA8B;gBAC1D,gBAAgB,EAAE,KAAK;aAC1B;SACJ,CAAC,CAAC;QAEH,iCAAiC;QACjC,gDAAgD;QAChD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,UAAU,EAAE;YACtD,IAAI,EAAE,EAAE;YACR,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG;YAC5B,mBAAmB,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;SAC1C,CAAC,CAAC;QAEH,wBAAwB;QACxB,IAAI,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,qBAAqB,EAAE;YAC3C,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,mBAAmB;YAC5C,WAAW,EAAE,gCAAgC;YAC7C,UAAU,EAAE,4BAA4B;SAC3C,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,gBAAgB,EAAE;YACtC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,cAAc;YACtC,WAAW,EAAE,gCAAgC;YAC7C,UAAU,EAAE,gCAAgC;SAC/C,CAAC,CAAC;IACP,CAAC;CACJ;AAjED,kDAiEC"}
@@ -112,7 +112,7 @@ export interface ProfileConfig {
112
112
  * Configuration for Quilt data catalog integration, including service endpoints
113
113
  * and SQS queue for package creation.
114
114
  *
115
- * **Breaking Change (v1.0.0)**: `stackArn` is used at deployment time only to resolve services.
115
+ * **Breaking Change (v0.9.0)**: `stackArn` is used at deployment time only to resolve services.
116
116
  * Services are passed as explicit environment variables to the container.
117
117
  * No runtime CloudFormation API calls are made.
118
118
  *
@@ -128,7 +128,7 @@ export interface QuiltConfig {
128
128
  * The resolved services are then passed as explicit environment variables to the container.
129
129
  *
130
130
  * **Deployment usage only** - not passed to container runtime.
131
- * **Breaking Change (v1.0.0)**: No longer passed as environment variable or CloudFormation parameter.
131
+ * **Breaking Change (v0.9.0)**: No longer passed as environment variable or CloudFormation parameter.
132
132
  *
133
133
  * @example "arn:aws:cloudformation:us-east-1:123456789012:stack/quilt-stack/..."
134
134
  */
@@ -5,7 +5,7 @@
5
5
  * These values are then passed as explicit environment variables to the container,
6
6
  * eliminating the need for runtime CloudFormation API calls.
7
7
  *
8
- * **Breaking Change (v1.0.0)**: Replaces runtime config-resolver with deployment-time resolution.
8
+ * **Breaking Change (v0.9.0)**: Replaces runtime config-resolver with deployment-time resolution.
9
9
  *
10
10
  * @module utils/service-resolver
11
11
  * @version 1.0.0
@@ -6,7 +6,7 @@
6
6
  * These values are then passed as explicit environment variables to the container,
7
7
  * eliminating the need for runtime CloudFormation API calls.
8
8
  *
9
- * **Breaking Change (v1.0.0)**: Replaces runtime config-resolver with deployment-time resolution.
9
+ * **Breaking Change (v0.9.0)**: Replaces runtime config-resolver with deployment-time resolution.
10
10
  *
11
11
  * @module utils/service-resolver
12
12
  * @version 1.0.0
@@ -0,0 +1,51 @@
1
+ import * as wafv2 from "aws-cdk-lib/aws-wafv2";
2
+ import * as logs from "aws-cdk-lib/aws-logs";
3
+ import { Construct } from "constructs";
4
+ export interface WafWebAclProps {
5
+ /**
6
+ * Comma-separated list of allowed IP addresses/CIDR blocks
7
+ *
8
+ * Empty string means no IP filtering (discovery mode - COUNT all requests).
9
+ * Non-empty string enables IP filtering (security mode - BLOCK unknown IPs).
10
+ *
11
+ * @example "192.168.1.0/24,10.0.0.0/8"
12
+ * @default ""
13
+ */
14
+ readonly ipAllowList?: string;
15
+ }
16
+ /**
17
+ * WAF Web ACL for Benchling webhook IP filtering
18
+ *
19
+ * Provides defense-in-depth security at AWS edge with two rules:
20
+ * 1. Health check exception - Always allow /health, /health/ready, /health/live
21
+ * 2. IP allowlist - Allow requests from configured IP ranges
22
+ *
23
+ * **Automatic Mode Selection:**
24
+ * - Empty IP allowlist → COUNT mode (discovery phase - logs requests but doesn't block)
25
+ * - Non-empty IP allowlist → BLOCK mode (security phase - blocks unknown IPs)
26
+ *
27
+ * This allows customers to deploy initially without knowing Benchling IPs,
28
+ * discover them from CloudWatch logs, then add them to enable blocking mode.
29
+ */
30
+ export declare class WafWebAcl extends Construct {
31
+ readonly webAcl: wafv2.CfnWebACL;
32
+ readonly ipSet: wafv2.CfnIPSet;
33
+ readonly logGroup: logs.ILogGroup;
34
+ constructor(scope: Construct, id: string, props?: WafWebAclProps);
35
+ /**
36
+ * Parse IP allowlist string into array of CIDR blocks
37
+ *
38
+ * - Splits by comma
39
+ * - Trims whitespace
40
+ * - Adds /32 suffix if not present
41
+ * - Filters out empty entries
42
+ *
43
+ * @param allowList Comma-separated list of IPs/CIDR blocks
44
+ * @returns Array of CIDR blocks
45
+ *
46
+ * @example
47
+ * parseIpAllowList("192.168.1.0/24, 10.0.0.1") → ["192.168.1.0/24", "10.0.0.1/32"]
48
+ */
49
+ private parseIpAllowList;
50
+ }
51
+ //# sourceMappingURL=waf-web-acl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waf-web-acl.d.ts","sourceRoot":"","sources":["../../lib/waf-web-acl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,IAAI,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,cAAc;IAC3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAU,SAAQ,SAAS;IACpC,SAAgB,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;IACxC,SAAgB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC;IACtC,SAAgB,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC;gBAE7B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,cAAmB;IA2HpE;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;CAU3B"}