Skip to content

Commit 2eccf0c

Browse files
authored
Fix: Mitigate public access and add deployment stack (#153)
* fix: mitigate public S3 access by implementing CloudFront OAC - Set publicReadAccess=false for both dashboard and SDK buckets - Enable BLOCK_ALL public access on S3 buckets - Implement Origin Access Control (OAC) for CloudFront distributions - Add proper IAM policies for CloudFront to access S3 buckets - Remove insecure CORS configurations * Update Tracker * Add MLEW Tracker deployment CloudFormation stack
1 parent 3f69fa6 commit 2eccf0c

File tree

2 files changed

+127
-30
lines changed

2 files changed

+127
-30
lines changed

.amazonq/cli-agents/developer.json

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"name": "developer",
3+
"description": "Default agent configuration",
4+
"mcpServers": {
5+
"AWS Core MCP Server": {
6+
"command": "uvx",
7+
"args": [
8+
"awslabs.core-mcp-server@latest"
9+
],
10+
"env": {
11+
"FASTMCP_LOG_LEVEL": "ERROR"
12+
},
13+
"timeout": 60000
14+
},
15+
"AWS Documentation MCP Server": {
16+
"command": "uvx",
17+
"args": [
18+
"awslabs.aws-documentation-mcp-server@latest"
19+
],
20+
"env": {
21+
"FASTMCP_LOG_LEVEL": "ERROR",
22+
"AWS_DOCUMENTATION_PARTITION": "aws"
23+
},
24+
"timeout": 60000
25+
},
26+
"awslabs.aws-diagram-mcp-server": {
27+
"command": "uvx",
28+
"args": [
29+
"awslabs.aws-diagram-mcp-server"
30+
],
31+
"env": {
32+
"FASTMCP_LOG_LEVEL": "ERROR"
33+
}
34+
},
35+
"awslabs.cdk-mcp-server": {
36+
"command": "uvx",
37+
"args": [
38+
"awslabs.cdk-mcp-server@latest"
39+
],
40+
"env": {
41+
"FASTMCP_LOG_LEVEL": "ERROR"
42+
}
43+
},
44+
"aws-knowledge-mcp-server": {
45+
"command": "npx",
46+
"args": [
47+
"mcp-remote",
48+
"https://knowledge-mcp.global.api.aws"
49+
]
50+
}
51+
},
52+
"tools": [
53+
"*"
54+
],
55+
"allowedTools": [
56+
"fs_read",
57+
"fs_write",
58+
"prompt_understanding",
59+
"read_documentation",
60+
"search_documentation"
61+
],
62+
"toolsSettings": {
63+
"execute_bash": {
64+
"alwaysAllow": [
65+
{
66+
"preset": "readOnly"
67+
}
68+
]
69+
},
70+
"use_aws": {
71+
"alwaysAllow": [
72+
{
73+
"preset": "readOnly"
74+
}
75+
]
76+
}
77+
},
78+
"resources": [
79+
]
80+
}

yourwork/tracker/cdk-app/lib/tracker-stack.ts

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -83,53 +83,37 @@ export class TrackerStack extends Stack {
8383
versioned: isProd,
8484
autoDeleteObjects: !isProd,
8585
encryption: s3.BucketEncryption.S3_MANAGED,
86-
blockPublicAccess: new s3.BlockPublicAccess({
87-
blockPublicAcls: false,
88-
blockPublicPolicy: false,
89-
ignorePublicAcls: false,
90-
restrictPublicBuckets: false,
91-
}),
92-
publicReadAccess: true,
93-
websiteIndexDocument: 'index.html',
94-
websiteErrorDocument: 'error.html',
86+
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
87+
publicReadAccess: false,
9588
removalPolicy: persistentResourcePolicy,
9689
});
97-
dashboardBucket.addCorsRule({
98-
allowedHeaders: ['*'],
99-
allowedMethods: [s3.HttpMethods.GET],
100-
allowedOrigins: ['*'],
101-
maxAge: 3600,
102-
});
10390
tagResource(dashboardBucket, 'Storage');
10491

10592
const sdkBucket = new s3.Bucket(this, 'SdkBucket', {
10693
bucketName: `mlew-sdk-${accountId}`,
10794
versioned: isProd,
10895
autoDeleteObjects: !isProd,
10996
encryption: s3.BucketEncryption.S3_MANAGED,
110-
blockPublicAccess: new s3.BlockPublicAccess({
111-
blockPublicAcls: false,
112-
blockPublicPolicy: false,
113-
ignorePublicAcls: false,
114-
restrictPublicBuckets: false,
115-
}),
116-
publicReadAccess: true,
97+
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
98+
publicReadAccess: false,
11799
removalPolicy: persistentResourcePolicy,
118100
});
119-
sdkBucket.addCorsRule({
120-
allowedHeaders: ['*'],
121-
allowedMethods: [s3.HttpMethods.GET],
122-
allowedOrigins: ['*'],
123-
maxAge: 3600,
124-
});
125101
tagResource(sdkBucket, 'Storage');
126102

127103

128104
// CloudFront Distributions
105+
const dashboardOAC = new cloudfront.S3OriginAccessControl(this, 'DashboardOAC', {
106+
originAccessControlName: `mlew-dashboard-oac-${accountId}`,
107+
description: 'OAC for Dashboard S3 bucket',
108+
signing: cloudfront.Signing.SIGV4_ALWAYS,
109+
});
110+
129111
const dashboardDistribution = new cloudfront.Distribution(this, 'DashboardDistribution', {
130112
defaultRootObject: 'index.html',
131113
defaultBehavior: {
132-
origin: new origins.S3Origin(dashboardBucket),
114+
origin: origins.S3BucketOrigin.withOriginAccessControl(dashboardBucket, {
115+
originAccessControl: dashboardOAC,
116+
}),
133117
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
134118
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
135119
cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
@@ -145,9 +129,17 @@ export class TrackerStack extends Stack {
145129
],
146130
});
147131

132+
const sdkOAC = new cloudfront.S3OriginAccessControl(this, 'SdkOAC', {
133+
originAccessControlName: `mlew-sdk-oac-${accountId}`,
134+
description: 'OAC for SDK S3 bucket',
135+
signing: cloudfront.Signing.SIGV4_ALWAYS,
136+
});
137+
148138
const sdkDistribution = new cloudfront.Distribution(this, 'SdkDistribution', {
149139
defaultBehavior: {
150-
origin: new origins.S3Origin(sdkBucket),
140+
origin: origins.S3BucketOrigin.withOriginAccessControl(sdkBucket, {
141+
originAccessControl: sdkOAC,
142+
}),
151143
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
152144
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
153145
cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
@@ -158,6 +150,31 @@ export class TrackerStack extends Stack {
158150
tagResource(dashboardDistribution, 'Delivery');
159151
tagResource(sdkDistribution, 'Delivery');
160152

153+
// Grant CloudFront OAC access to S3 buckets
154+
dashboardBucket.addToResourcePolicy(new iam.PolicyStatement({
155+
effect: iam.Effect.ALLOW,
156+
principals: [new iam.ServicePrincipal('cloudfront.amazonaws.com')],
157+
actions: ['s3:GetObject'],
158+
resources: [dashboardBucket.arnForObjects('*')],
159+
conditions: {
160+
StringEquals: {
161+
'AWS:SourceArn': `arn:aws:cloudfront::${accountId}:distribution/${dashboardDistribution.distributionId}`,
162+
},
163+
},
164+
}));
165+
166+
sdkBucket.addToResourcePolicy(new iam.PolicyStatement({
167+
effect: iam.Effect.ALLOW,
168+
principals: [new iam.ServicePrincipal('cloudfront.amazonaws.com')],
169+
actions: ['s3:GetObject'],
170+
resources: [sdkBucket.arnForObjects('*')],
171+
conditions: {
172+
StringEquals: {
173+
'AWS:SourceArn': `arn:aws:cloudfront::${accountId}:distribution/${sdkDistribution.distributionId}`,
174+
},
175+
},
176+
}));
177+
161178
// Lambda functions
162179
const bundling: lambdaNodejs.BundlingOptions = {
163180
externalModules: ['aws-sdk'],

0 commit comments

Comments
 (0)