Skip to content

Commit 3261e11

Browse files
committed
Update backet stack resources
1 parent be7152f commit 3261e11

File tree

1 file changed

+69
-128
lines changed

1 file changed

+69
-128
lines changed

packages/cdk/stacks/EpsAssistMeStack.ts

Lines changed: 69 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import {
1010
Bucket,
1111
BucketEncryption,
1212
BlockPublicAccess,
13-
CfnBucket,
14-
CfnBucketPolicy
13+
ObjectOwnership
1514
} from "aws-cdk-lib/aws-s3"
16-
import {CfnFunction} from "aws-cdk-lib/aws-lambda"
15+
import * as AWSCDK from "aws-cdk-lib/aws-s3"
1716
import {Key} from "aws-cdk-lib/aws-kms"
1817
import {PolicyStatement} from "aws-cdk-lib/aws-iam"
1918
import {
@@ -59,13 +58,14 @@ export class EpsAssistMeStack extends Stack {
5958
encryption: BucketEncryption.KMS,
6059
encryptionKey: cloudWatchLogsKmsKey,
6160
removalPolicy: RemovalPolicy.DESTROY,
62-
autoDeleteObjects: true,
6361
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
64-
versioned: true
62+
versioned: true,
63+
objectLockEnabled: true,
64+
objectOwnership: ObjectOwnership.BUCKET_OWNER_ENFORCED
6565
})
6666

67-
// Add S3 replication and logging
68-
const accessLogBucketCfn = accessLogBucket.node.defaultChild as CfnBucket
67+
// Replication config via escape hatch
68+
const accessLogBucketCfn = accessLogBucket.node.defaultChild as AWSCDK.CfnBucket
6969
accessLogBucketCfn.replicationConfiguration = {
7070
role: `arn:aws:iam::${account}:role/account-resources-s3-replication-role`,
7171
rules: [{
@@ -77,24 +77,25 @@ export class EpsAssistMeStack extends Stack {
7777
deleteMarkerReplication: {status: "Disabled"}
7878
}]
7979
}
80-
accessLogBucketCfn.loggingConfiguration = {
81-
destinationBucketName: accessLogBucket.bucketName,
82-
logFilePrefix: "self-logs/"
83-
}
8480

85-
new CfnBucketPolicy(this, "AccessLogsBucketStrictTLSOnly", {
86-
bucket: accessLogBucket.bucketName,
81+
// TLS-only policy (strictly compliant for cfn-guard)
82+
new AWSCDK.CfnBucketPolicy(this, "AccessLogsBucketTlsPolicy", {
83+
bucket: accessLogBucketCfn.ref,
8784
policyDocument: {
8885
Version: "2012-10-17",
89-
Statement: [{
90-
Action: "s3:*",
91-
Effect: "Deny",
92-
Principal: "*",
93-
Resource: "*",
94-
Condition: {
95-
Bool: {"aws:SecureTransport": false}
86+
Statement: [
87+
{
88+
Action: "s3:*",
89+
Effect: "Deny",
90+
Principal: "*",
91+
Resource: "*",
92+
Condition: {
93+
Bool: {
94+
"aws:SecureTransport": false
95+
}
96+
}
9697
}
97-
}]
98+
]
9899
}
99100
})
100101

@@ -103,14 +104,16 @@ export class EpsAssistMeStack extends Stack {
103104
encryptionKey: cloudWatchLogsKmsKey,
104105
encryption: BucketEncryption.KMS,
105106
removalPolicy: RemovalPolicy.DESTROY,
106-
autoDeleteObjects: true,
107107
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
108108
versioned: true,
109+
objectLockEnabled: true,
110+
objectOwnership: ObjectOwnership.BUCKET_OWNER_ENFORCED,
109111
serverAccessLogsBucket: accessLogBucket,
110112
serverAccessLogsPrefix: "s3-access-logs/"
111113
})
112114

113-
const kbDocsBucketCfn = kbDocsBucket.node.defaultChild as CfnBucket
115+
// Replication config via escape hatch
116+
const kbDocsBucketCfn = kbDocsBucket.node.defaultChild as AWSCDK.CfnBucket
114117
kbDocsBucketCfn.replicationConfiguration = {
115118
role: `arn:aws:iam::${account}:role/account-resources-s3-replication-role`,
116119
rules: [{
@@ -123,19 +126,24 @@ export class EpsAssistMeStack extends Stack {
123126
}]
124127
}
125128

126-
new CfnBucketPolicy(this, "KbDocsBucketStrictTLSOnly", {
127-
bucket: kbDocsBucket.bucketName,
129+
// TLS-only policy (strictly compliant for cfn-guard)
130+
new AWSCDK.CfnBucketPolicy(this, "KbDocsTlsPolicy", {
131+
bucket: kbDocsBucketCfn.ref,
128132
policyDocument: {
129133
Version: "2012-10-17",
130-
Statement: [{
131-
Action: "s3:*",
132-
Effect: "Deny",
133-
Principal: "*",
134-
Resource: "*",
135-
Condition: {
136-
Bool: {"aws:SecureTransport": false}
134+
Statement: [
135+
{
136+
Action: "s3:*",
137+
Effect: "Deny",
138+
Principal: "*",
139+
Resource: "*",
140+
Condition: {
141+
Bool: {
142+
"aws:SecureTransport": false
143+
}
144+
}
137145
}
138-
}]
146+
]
139147
}
140148
})
141149

@@ -175,6 +183,7 @@ export class EpsAssistMeStack extends Stack {
175183
type: "VECTORSEARCH"
176184
})
177185

186+
// OpenSearch encryption policy (AWS-owned key)
178187
new ops.CfnSecurityPolicy(this, "OsEncryptionPolicy", {
179188
name: "eps-assist-encryption-policy",
180189
type: "encryption",
@@ -184,16 +193,19 @@ export class EpsAssistMeStack extends Stack {
184193
})
185194
})
186195

196+
// OpenSearch network policy (allow public access for demo purposes)
187197
new ops.CfnSecurityPolicy(this, "OsNetworkPolicy", {
188198
name: "eps-assist-network-policy",
189199
type: "network",
190-
policy: JSON.stringify([{
191-
Rules: [
192-
{ResourceType: "collection", Resource: ["collection/eps-assist-vector-db"]},
193-
{ResourceType: "dashboard", Resource: ["collection/eps-assist-vector-db"]}
194-
],
195-
AllowFromPublic: true
196-
}])
200+
policy: JSON.stringify([
201+
{
202+
Rules: [
203+
{ResourceType: "collection", Resource: ["collection/eps-assist-vector-db"]},
204+
{ResourceType: "dashboard", Resource: ["collection/eps-assist-vector-db"]}
205+
],
206+
AllowFromPublic: true
207+
}
208+
])
197209
})
198210

199211
// ==== Lambda Function: CreateIndex ====
@@ -208,32 +220,22 @@ export class EpsAssistMeStack extends Stack {
208220
additionalPolicies: []
209221
})
210222

211-
// Add cfn-guard suppressions for CreateIndex Lambda
212-
const createIndexLambdaCfn = createIndexFunction.function.node.defaultChild as CfnFunction
213-
createIndexLambdaCfn.cfnOptions.metadata = {
214-
guard: {
215-
SuppressedRules: [
216-
"LAMBDA_DLQ_CHECK",
217-
"LAMBDA_INSIDE_VPC",
218-
"LAMBDA_CONCURRENCY_CHECK"
219-
]
220-
}
221-
}
222-
223-
// ==== OpenSearch Access Policy ====
223+
// Access policy for Bedrock + Lambda to use the collection and index
224224
new ops.CfnAccessPolicy(this, "OsAccessPolicy", {
225225
name: "eps-assist-access-policy",
226226
type: "data",
227-
policy: JSON.stringify([{
228-
Rules: [
229-
{ResourceType: "collection", Resource: ["collection/*"], Permission: ["aoss:*"]},
230-
{ResourceType: "index", Resource: ["index/*/*"], Permission: ["aoss:*"]}
231-
],
232-
Principal: [
233-
`arn:aws:iam::${account}:role/${createIndexFunction.function.role?.roleName}`,
234-
`arn:aws:iam::${account}:root`
235-
]
236-
}])
227+
policy: JSON.stringify([
228+
{
229+
Rules: [
230+
{ResourceType: "collection", Resource: ["collection/*"], Permission: ["aoss:*"]},
231+
{ResourceType: "index", Resource: ["index/*/*"], Permission: ["aoss:*"]}
232+
],
233+
Principal: [
234+
`arn:aws:iam::${account}:role/${createIndexFunction.function.role?.roleName}`,
235+
`arn:aws:iam::${account}:root`
236+
]
237+
}
238+
])
237239
})
238240

239241
// ==== Trigger Vector Index Creation ====
@@ -302,6 +304,7 @@ export class EpsAssistMeStack extends Stack {
302304
}
303305
})
304306

307+
// Attach S3 data source to Knowledge Base
305308
new CfnDataSource(this, "EpsKbDataSource", {
306309
name: "eps-assist-kb-ds",
307310
knowledgeBaseId: kb.attrKnowledgeBaseId,
@@ -330,6 +333,7 @@ export class EpsAssistMeStack extends Stack {
330333
SLACK_SIGNING_SECRET: slackSigningSecret
331334
}
332335

336+
// SlackBot Lambda function
333337
const slackBotLambda = new LambdaFunction(this, "SlackBotLambda", {
334338
stackName: props.stackName,
335339
functionName: `${props.stackName}-SlackBotFunction`,
@@ -341,34 +345,6 @@ export class EpsAssistMeStack extends Stack {
341345
additionalPolicies: []
342346
})
343347

344-
const slackBotLambdaCfn = slackBotLambda.function.node.defaultChild as CfnFunction
345-
slackBotLambdaCfn.cfnOptions.metadata = {
346-
guard: {
347-
SuppressedRules: [
348-
"LAMBDA_DLQ_CHECK",
349-
"LAMBDA_INSIDE_VPC",
350-
"LAMBDA_CONCURRENCY_CHECK"
351-
]
352-
}
353-
}
354-
355-
// AwsCustomResource internal Lambda handler suppression
356-
const customResourceHandler = this.node
357-
.tryFindChild("VectorIndex")
358-
?.node.tryFindChild("CustomResourceProvider")
359-
?.node.tryFindChild("Handler")
360-
const customResourceHandlerCfn = customResourceHandler?.node.defaultChild as CfnFunction
361-
if (customResourceHandlerCfn) {
362-
customResourceHandlerCfn.cfnOptions.metadata = {
363-
guard: {
364-
SuppressedRules: [
365-
"LAMBDA_DLQ_CHECK",
366-
"LAMBDA_INSIDE_VPC"
367-
]
368-
}
369-
}
370-
}
371-
372348
// ==== API Gateway + Slack Route ====
373349
const apiGateway = new RestApiGateway(this, "EpsAssistApiGateway", {
374350
stackName: props.stackName,
@@ -378,54 +354,19 @@ export class EpsAssistMeStack extends Stack {
378354
truststoreVersion: "unused"
379355
})
380356

357+
// API Route
381358
const slackRoute = apiGateway.api.root.addResource("slack").addResource("ask-eps")
382359
slackRoute.addMethod("POST", new LambdaIntegration(slackBotLambda.function, {
383360
credentialsRole: apiGateway.role
384361
}))
385362

386363
apiGateway.role.addManagedPolicy(slackBotLambda.executionPolicy)
387364

365+
// Output the SlackBot API endpoint
388366
new CfnOutput(this, "SlackBotEndpoint", {
389367
value: `https://${apiGateway.api.domainName?.domainName}/slack/ask-eps`
390368
})
391369

392-
// ==== Suppressions for CDK-generated Lambda handlers ====
393-
// Suppress CDK-generated S3 AutoDeleteObjects handler
394-
const customS3Handler = Stack.of(this).node
395-
.tryFindChild("Custom::S3AutoDeleteObjectsCustomResourceProvider")
396-
?.node.tryFindChild("Handler")
397-
398-
const customS3HandlerCfn = customS3Handler?.node.defaultChild as CfnFunction
399-
400-
if (customS3HandlerCfn) {
401-
customS3HandlerCfn.cfnOptions.metadata = {
402-
guard: {
403-
SuppressedRules: [
404-
"LAMBDA_DLQ_CHECK",
405-
"LAMBDA_INSIDE_VPC"
406-
]
407-
}
408-
}
409-
}
410-
411-
// Suppress AWS679f... CDK-generated unnamed Lambda (e.g., AwsCustomResource)
412-
for (const construct of this.node.findAll()) {
413-
const fn = construct.node.defaultChild
414-
if (
415-
fn instanceof CfnFunction &&
416-
fn.logicalId.startsWith("AWS679f")
417-
) {
418-
fn.cfnOptions.metadata = {
419-
guard: {
420-
SuppressedRules: [
421-
"LAMBDA_DLQ_CHECK",
422-
"LAMBDA_INSIDE_VPC"
423-
]
424-
}
425-
}
426-
}
427-
}
428-
429370
// ==== Final CDK Nag Suppressions ====
430371
nagSuppressions(this)
431372
}

0 commit comments

Comments
 (0)