Skip to content

Commit d70b051

Browse files
jonathanwelch1-nhswildjamesbencegadanyi1-nhsMatthewPopat-NHS
authored
Chore: [AEA-4762] - removed headers from stateless resources (#1337)
## Summary Removed headers getting returned in curl commands - 🤖 Operational or Infrastructure Change --------- Signed-off-by: Bence Gadanyi <bence.gadanyi1@nhs.net> Co-authored-by: Jim Wild <james.wild6@nhs.net> Co-authored-by: Bence Gadanyi <bence.gadanyi1@nhs.net> Co-authored-by: MatthewPopat-NHS <95283781+MatthewPopat-NHS@users.noreply.github.com>
1 parent 1143726 commit d70b051

File tree

3 files changed

+100
-68
lines changed

3 files changed

+100
-68
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {Construct} from "constructs"
2+
import {Duration} from "aws-cdk-lib"
3+
import {ResponseHeadersPolicy, HeadersFrameOption, HeadersReferrerPolicy} from "aws-cdk-lib/aws-cloudfront"
4+
5+
export interface CustomResponseHeadersPolicyProps {
6+
policyName: string
7+
}
8+
9+
export class CustomSecurityHeadersPolicy extends Construct {
10+
public readonly policy: ResponseHeadersPolicy
11+
12+
constructor(scope: Construct, id: string, props: CustomResponseHeadersPolicyProps) {
13+
super(scope, id)
14+
15+
this.policy = new ResponseHeadersPolicy(this, "CustomSecurityHeadersPolicy", {
16+
responseHeadersPolicyName: props.policyName,
17+
comment: "Security headers policy with inclusion of CSP",
18+
removeHeaders: [
19+
"x-amz-server-side-encryption",
20+
"x-amz-server-side-encryption-aws-kms-key-id",
21+
"x-amz-server-side-encryption-bucket-key-enabled"
22+
],
23+
securityHeadersBehavior: {
24+
contentSecurityPolicy: {
25+
contentSecurityPolicy: `
26+
default-src 'self';
27+
script-src 'self' https://assets.nhs.uk;
28+
style-src 'self' 'unsafe-inline' https://assets.nhs.uk;
29+
font-src 'self' https://assets.nhs.uk;
30+
img-src 'self' data: https://assets.nhs.uk;
31+
connect-src 'self'
32+
https://*.amazonaws.com
33+
https://*.amazoncognito.com;
34+
object-src 'none';
35+
base-uri 'self';
36+
frame-ancestors 'none';
37+
`.replace(/\s+/g, " ").trim(),
38+
override: true
39+
},
40+
strictTransportSecurity: {
41+
accessControlMaxAge: Duration.days(365),
42+
includeSubdomains: true,
43+
preload: true,
44+
override: true
45+
},
46+
contentTypeOptions: {
47+
override: true
48+
},
49+
frameOptions: {
50+
frameOption: HeadersFrameOption.DENY,
51+
override: true
52+
},
53+
referrerPolicy: {
54+
referrerPolicy: HeadersReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
55+
override: true
56+
},
57+
xssProtection: {
58+
protection: true,
59+
modeBlock: true,
60+
override: true
61+
}
62+
},
63+
customHeadersBehavior: {
64+
customHeaders: [
65+
{
66+
header: "Permissions-Policy",
67+
value: "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), \
68+
cross-origin-isolated=(),display-capture=(), document-domain=(), encrypted-media=(),\
69+
execution-while-not-rendered=(), execution-while-out-of-viewport=(),\
70+
gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), \
71+
keyboard-map=(), magnetometer=(), microphone=(), midi=(),\
72+
otp-credentials=(), payment=(), picture-in-picture=(), \
73+
publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker-selection=(),\
74+
sync-xhr=(), usb=(), vertical-scroll=(), web-share=(),\
75+
window-placement=(), xr-spatial-tracking=()",
76+
override: true
77+
}
78+
]
79+
}
80+
})
81+
}
82+
}

packages/cdk/resources/CloudfrontBehaviors.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import {
99
IOrigin,
1010
KeyValueStore,
1111
OriginRequestPolicy,
12-
ViewerProtocolPolicy,
13-
ResponseHeadersPolicy
12+
ViewerProtocolPolicy
1413
} from "aws-cdk-lib/aws-cloudfront"
1514
import {RestApiOrigin} from "aws-cdk-lib/aws-cloudfront-origins"
16-
15+
import {CustomSecurityHeadersPolicy} from "./Cloudfront/CustomSecurityHeaders"
1716
/**
1817
* Resources for cloudfront behaviors
1918
@@ -27,7 +26,6 @@ export interface CloudfrontBehaviorsProps {
2726
readonly oauth2GatewayOrigin: RestApiOrigin
2827
readonly oauth2GatewayRequestPolicy: OriginRequestPolicy
2928
readonly staticContentBucketOrigin: IOrigin
30-
readonly responseHeadersPolicy: ResponseHeadersPolicy
3129
}
3230

3331
/**
@@ -42,7 +40,6 @@ export class CloudfrontBehaviors extends Construct{
4240
public readonly s3StaticContentUriRewriteFunction: CloudfrontFunction
4341
public readonly s3StaticContentRootSlashRedirect: CloudfrontFunction
4442
public readonly keyValueStore: KeyValueStore
45-
public readonly responseHeadersPolicy: ResponseHeadersPolicy
4643

4744
public constructor(scope: Construct, id: string, props: CloudfrontBehaviorsProps){
4845
super(scope, id)
@@ -188,6 +185,10 @@ export class CloudfrontBehaviors extends Construct{
188185
on how many can be created simultaneously */
189186
s3StaticContentRootSlashRedirect.node.addDependency(s3JwksUriRewriteFunction)
190187

188+
const headersPolicy = new CustomSecurityHeadersPolicy(this, "AdditionalBehavioursHeadersPolicy", {
189+
policyName: `${props.serviceName}-AdditionalBehavioursCustomSecurityHeaders`
190+
})
191+
191192
const additionalBehaviors = {
192193
"/site*": {
193194
origin: props.staticContentBucketOrigin,
@@ -199,7 +200,7 @@ export class CloudfrontBehaviors extends Construct{
199200
eventType: FunctionEventType.VIEWER_REQUEST
200201
}
201202
],
202-
responseHeadersPolicy: this.responseHeadersPolicy
203+
responseHeadersPolicy: headersPolicy.policy
203204
},
204205
"/api/*": {
205206
origin: props.apiGatewayOrigin,
@@ -213,7 +214,7 @@ export class CloudfrontBehaviors extends Construct{
213214
eventType: FunctionEventType.VIEWER_REQUEST
214215
}
215216
],
216-
responseHeadersPolicy: this.responseHeadersPolicy
217+
responseHeadersPolicy: headersPolicy.policy
217218
},
218219
"/oauth2/*": {
219220
origin: props.oauth2GatewayOrigin,
@@ -227,7 +228,7 @@ export class CloudfrontBehaviors extends Construct{
227228
eventType: FunctionEventType.VIEWER_REQUEST
228229
}
229230
],
230-
responseHeadersPolicy: this.responseHeadersPolicy
231+
responseHeadersPolicy: headersPolicy.policy
231232
},
232233
"/jwks/": {/* matches exactly <url>/jwks and will only serve the jwks json (via cf function) */
233234
origin: props.staticContentBucketOrigin,
@@ -239,7 +240,7 @@ export class CloudfrontBehaviors extends Construct{
239240
eventType: FunctionEventType.VIEWER_REQUEST
240241
}
241242
],
242-
responseHeadersPolicy: this.responseHeadersPolicy
243+
responseHeadersPolicy: headersPolicy.policy
243244
},
244245
"/500.html": { // matches exactly <url>/500.html and will only serve the 500.html page (via cf function)
245246
origin: props.staticContentBucketOrigin,
@@ -251,7 +252,7 @@ export class CloudfrontBehaviors extends Construct{
251252
eventType: FunctionEventType.VIEWER_REQUEST
252253
}
253254
],
254-
responseHeadersPolicy: this.responseHeadersPolicy
255+
responseHeadersPolicy: headersPolicy.policy
255256
},
256257
"/404.css": {
257258
origin: props.staticContentBucketOrigin,
@@ -261,7 +262,7 @@ export class CloudfrontBehaviors extends Construct{
261262
origin: props.staticContentBucketOrigin,
262263
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
263264
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
264-
responseHeadersPolicy: this.responseHeadersPolicy,
265+
responseHeadersPolicy: headersPolicy.policy,
265266
functionAssociations: [
266267
{
267268
function: s3StaticContentRootSlashRedirect.function,

packages/cdk/stacks/StatelessResourcesStack.ts

Lines changed: 6 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ import {
1515
OriginRequestHeaderBehavior,
1616
OriginRequestPolicy,
1717
OriginRequestQueryStringBehavior,
18-
ViewerProtocolPolicy,
19-
ResponseHeadersPolicy,
20-
HeadersReferrerPolicy,
21-
HeadersFrameOption
18+
ViewerProtocolPolicy
2219
} from "aws-cdk-lib/aws-cloudfront"
2320
import {RestApiOrigin, S3BucketOrigin} from "aws-cdk-lib/aws-cloudfront-origins"
2421
import {Bucket} from "aws-cdk-lib/aws-s3"
@@ -42,7 +39,7 @@ import {Certificate} from "aws-cdk-lib/aws-certificatemanager"
4239
import {WebACL} from "../resources/WebApplicationFirewall"
4340
import {CfnWebACLAssociation} from "aws-cdk-lib/aws-wafv2"
4441
import {ukRegionLogGroups} from "../resources/ukRegionLogGroups"
45-
42+
import {CustomSecurityHeadersPolicy} from "../resources/Cloudfront/CustomSecurityHeaders"
4643
export interface StatelessResourcesStackProps extends StackProps {
4744
readonly serviceName: string
4845
readonly stackName: string
@@ -454,55 +451,8 @@ export class StatelessResourcesStack extends Stack {
454451
})
455452

456453
// --- CloudfrontBehaviors
457-
const responseHeadersPolicy = new ResponseHeadersPolicy(this, "CustomSecurityHeadersPolicy", {
458-
responseHeadersPolicyName: `${props.serviceName}-CustomSecurityHeaders`,
459-
comment: "Security headers policy with inclusion of CSP",
460-
securityHeadersBehavior: {
461-
contentSecurityPolicy: {
462-
contentSecurityPolicy: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " +
463-
"object-src 'none'; base-uri 'self'; frame-ancestors 'none';",
464-
override: true
465-
},
466-
strictTransportSecurity: {
467-
accessControlMaxAge: Duration.days(365),
468-
includeSubdomains: true,
469-
preload: true,
470-
override: true
471-
},
472-
contentTypeOptions: {
473-
override: true
474-
},
475-
frameOptions: {
476-
frameOption: HeadersFrameOption.DENY,
477-
override: true
478-
},
479-
referrerPolicy: {
480-
referrerPolicy: HeadersReferrerPolicy.NO_REFERRER,
481-
override: true
482-
},
483-
xssProtection: {
484-
protection: true,
485-
modeBlock: true,
486-
override: true
487-
}
488-
},
489-
customHeadersBehavior: {
490-
customHeaders: [
491-
{
492-
header: "Permissions-Policy",
493-
value: "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), \
494-
cross-origin-isolated=(),display-capture=(), document-domain=(), encrypted-media=(),\
495-
execution-while-not-rendered=(), execution-while-out-of-viewport=(),\
496-
gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), \
497-
keyboard-map=(), magnetometer=(), microphone=(), midi=(),\
498-
otp-credentials=(), payment=(), picture-in-picture=(), \
499-
publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker-selection=(),\
500-
sync-xhr=(), usb=(), vertical-scroll=(), web-share=(),\
501-
window-placement=(), xr-spatial-tracking=()",
502-
override: true
503-
}
504-
]
505-
}
454+
const headersPolicy = new CustomSecurityHeadersPolicy(this, "DefaultBehaviourHeadersPolicy", {
455+
policyName: `${props.serviceName}-CustomSecurityHeaders`
506456
})
507457

508458
const cloudfrontBehaviors = new CloudfrontBehaviors(this, "CloudfrontBehaviors", {
@@ -512,8 +462,7 @@ export class StatelessResourcesStack extends Stack {
512462
apiGatewayRequestPolicy: apiGatewayRequestPolicy,
513463
oauth2GatewayOrigin: oauth2GatewayOrigin,
514464
oauth2GatewayRequestPolicy: oauth2GatewayRequestPolicy,
515-
staticContentBucketOrigin: staticContentBucketOrigin,
516-
responseHeadersPolicy: responseHeadersPolicy
465+
staticContentBucketOrigin: staticContentBucketOrigin
517466
})
518467

519468
// --- Distribution
@@ -538,7 +487,7 @@ export class StatelessResourcesStack extends Stack {
538487
eventType: FunctionEventType.VIEWER_RESPONSE
539488
}
540489
],
541-
responseHeadersPolicy: responseHeadersPolicy
490+
responseHeadersPolicy: headersPolicy.policy
542491
},
543492
additionalBehaviors: cloudfrontBehaviors.additionalBehaviors,
544493
errorResponses: [

0 commit comments

Comments
 (0)