Skip to content

Commit 096999e

Browse files
committed
chore: enable CloudFront access logging
1 parent ea24fac commit 096999e

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

infra/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,16 @@ Set these repository secrets and variables before running the deployment workflo
6464
- `vars.S3_BUCKET`: Target S3 bucket name (`prjm`).
6565
- `vars.CLOUDFRONT_DISTRIBUTION_ID`: Distribution ID exported by Pulumi.
6666

67+
### CloudFront Access Logging
68+
69+
Provision the log bucket and enable CloudFront logging with the helper script (run once per environment):
70+
71+
```bash
72+
cd infra
73+
AWS_PROFILE=projectm AWS_SDK_LOAD_CONFIG=1 node scripts/enable-cloudfront-logging.mjs
74+
```
75+
76+
By default this creates `projectm-visualizer-cloudfront-logs` in `us-west-2` and configures the distribution to write compressed logs (cookies included) under the `cloudfront/` prefix.
77+
78+
6779
The workflow runs on pushes to `master` and can also be triggered manually.

infra/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"prettier": "prettier --check \"src/**/*.ts\""
1010
},
1111
"dependencies": {
12+
"@aws-sdk/client-cloudfront": "^3.913.0",
13+
"@aws-sdk/client-s3": "^3.913.0",
1214
"@pulumi/aws": "^6.28.0",
1315
"@pulumi/pulumi": "^3.149.0"
1416
},
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {
2+
CloudFrontClient,
3+
GetDistributionCommand,
4+
UpdateDistributionCommand
5+
} from '@aws-sdk/client-cloudfront'
6+
import {
7+
S3Client,
8+
CreateBucketCommand,
9+
PutBucketOwnershipControlsCommand,
10+
PutBucketAclCommand,
11+
PutBucketPolicyCommand
12+
} from '@aws-sdk/client-s3'
13+
14+
const region = process.env.AWS_REGION || 'us-west-2'
15+
const accountId = '533267091967'
16+
const distributionId = process.env.CF_DISTRIBUTION_ID || 'E1DLLQU8OGUHTK'
17+
const logsBucket = process.env.CF_LOG_BUCKET || 'projectm-visualizer-cloudfront-logs'
18+
const logsPrefix = process.env.CF_LOG_PREFIX || 'cloudfront/'
19+
20+
const s3 = new S3Client({ region })
21+
const cf = new CloudFrontClient({ region })
22+
23+
async function ensureBucket() {
24+
try {
25+
await s3.send(new CreateBucketCommand({
26+
Bucket: logsBucket,
27+
CreateBucketConfiguration: { LocationConstraint: region }
28+
}))
29+
console.log(`Created S3 bucket ${logsBucket}`)
30+
} catch (error) {
31+
if (error.name === 'BucketAlreadyOwnedByYou' || error.$metadata?.httpStatusCode === 409) {
32+
console.log(`Bucket ${logsBucket} already exists`)
33+
} else {
34+
throw error
35+
}
36+
}
37+
38+
try {
39+
await s3.send(new PutBucketOwnershipControlsCommand({
40+
Bucket: logsBucket,
41+
OwnershipControls: {
42+
Rules: [{ ObjectOwnership: 'ObjectWriter' }]
43+
}
44+
}))
45+
console.log('Set bucket ownership to ObjectWriter (enables ACLs)')
46+
} catch (error) {
47+
if (error.name !== 'OwnershipControlsNotFoundError') {
48+
console.warn('Unable to set ownership controls:', error.message)
49+
}
50+
}
51+
52+
try {
53+
await s3.send(new PutBucketAclCommand({
54+
Bucket: logsBucket,
55+
ACL: 'log-delivery-write'
56+
}))
57+
console.log('Applied log-delivery-write ACL')
58+
} catch (error) {
59+
console.warn('Skipping ACL update:', error.message)
60+
}
61+
62+
const policy = {
63+
Version: '2012-10-17',
64+
Statement: [
65+
{
66+
Effect: 'Allow',
67+
Principal: { Service: 'cloudfront.amazonaws.com' },
68+
Action: 's3:PutObject',
69+
Resource: `arn:aws:s3:::${logsBucket}/${logsPrefix}*`,
70+
Condition: {
71+
StringEquals: {
72+
'AWS:SourceArn': `arn:aws:cloudfront::${accountId}:distribution/${distributionId}`
73+
}
74+
}
75+
}
76+
]
77+
}
78+
79+
await s3.send(new PutBucketPolicyCommand({
80+
Bucket: logsBucket,
81+
Policy: JSON.stringify(policy)
82+
}))
83+
console.log(`Bucket policy set on ${logsBucket}`)
84+
}
85+
86+
async function enableLogging() {
87+
await ensureBucket()
88+
89+
const { Distribution, ETag } = await cf.send(new GetDistributionCommand({ Id: distributionId }))
90+
if (!Distribution?.DistributionConfig) {
91+
throw new Error('Distribution configuration not found')
92+
}
93+
94+
const config = Distribution.DistributionConfig
95+
config.Logging = {
96+
Enabled: true,
97+
IncludeCookies: true,
98+
Bucket: `${logsBucket}.s3.amazonaws.com`,
99+
Prefix: logsPrefix
100+
}
101+
102+
await cf.send(new UpdateDistributionCommand({
103+
Id: distributionId,
104+
IfMatch: ETag,
105+
DistributionConfig: config
106+
}))
107+
108+
console.log('CloudFront logging enabled for distribution', distributionId)
109+
}
110+
111+
enableLogging().catch((err) => {
112+
console.error('Failed to enable CloudFront logging:', err)
113+
process.exit(1)
114+
})

0 commit comments

Comments
 (0)