Skip to content

Commit

Permalink
fix: Use response headers instead
Browse files Browse the repository at this point in the history
  • Loading branch information
jackylamhk committed Sep 7, 2024
1 parent 08772c5 commit 6e2f107
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 45 deletions.
65 changes: 23 additions & 42 deletions src/constructs/aws/abstracts/StaticWebsiteAbstract.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import * as acm from "aws-cdk-lib/aws-certificatemanager";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import type { CfnDistribution, ErrorResponse } from "aws-cdk-lib/aws-cloudfront";
import {
AllowedMethods,
CachePolicy,
Distribution,
FunctionEventType,
HttpVersion,
ResponseHeadersPolicy,
ViewerProtocolPolicy,
} from "aws-cdk-lib/aws-cloudfront";
import { S3Origin } from "aws-cdk-lib/aws-cloudfront-origins";
import type { IResponseHeadersPolicy } from "aws-cdk-lib/aws-cloudfront";
import type { BucketProps, CfnBucket } from "aws-cdk-lib/aws-s3";
import { Bucket } from "aws-cdk-lib/aws-s3";
import type { Construct as CdkConstruct } from "constructs";
Expand All @@ -26,7 +26,6 @@ import { emptyBucket, invalidateCloudFrontCache } from "../../../classes/aws";
import ServerlessError from "../../../utils/error";
import type { Progress } from "../../../utils/logger";
import { getUtils } from "../../../utils/logger";
import { ensureNameMaxLength } from "../../../utils/naming";
import { s3Sync } from "../../../utils/s3-sync";

export const COMMON_STATIC_WEBSITE_DEFINITION = {
Expand All @@ -47,6 +46,9 @@ export const COMMON_STATIC_WEBSITE_DEFINITION = {
type: "object",
properties: {
allowIframe: { type: "boolean" },
responseHeaderPolicy: {
anyOf: [{ type: "string" }, { type: "object" }],
},
},
additionalProperties: false,
},
Expand Down Expand Up @@ -107,26 +109,36 @@ export abstract class StaticWebsiteAbstract extends AwsConstruct {
);
}

const functionAssociations = [
{
function: this.createResponseFunction(),
eventType: FunctionEventType.VIEWER_RESPONSE,
},
];

let responseHeadersPolicy: IResponseHeadersPolicy;
if (typeof this.configuration.security?.responseHeaderPolicy === "string") {
responseHeadersPolicy = ResponseHeadersPolicy.fromResponseHeadersPolicyId(
scope,
"ResponseHeadersPolicy",
this.configuration.security.responseHeaderPolicy
);
} else if (this.configuration.security?.responseHeaderPolicy !== undefined) {
responseHeadersPolicy = new ResponseHeadersPolicy(
scope,
"ResponseHeadersPolicy",
this.configuration.security.responseHeaderPolicy
);
} else {
responseHeadersPolicy = ResponseHeadersPolicy.SECURITY_HEADERS;
}
this.distribution = new Distribution(this, "CDN", {
comment: `${provider.stackName} ${id} website CDN`,
// Send all page requests to index.html
defaultRootObject: "index.html",
defaultBehavior: {
// Origins are where CloudFront fetches content
origin: new S3Origin(this.bucket),
// origin: S3BucketOrigin.withOriginAccessControl(this.bucket),
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
// Use the "Managed-CachingOptimized" policy
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html#managed-cache-policies-list
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
functionAssociations: functionAssociations,
responseHeadersPolicy,
},
errorResponses: [this.errorResponse()],
// Enable http2 transfer for better performances
Expand Down Expand Up @@ -325,37 +337,6 @@ export abstract class StaticWebsiteAbstract extends AwsConstruct {
};
}

private createResponseFunction(): cloudfront.Function {
const securityHeaders: Record<string, { value: string }> = {
"x-frame-options": { value: "SAMEORIGIN" },
"x-content-type-options": { value: "nosniff" },
"x-xss-protection": { value: "1; mode=block" },
"strict-transport-security": { value: "max-age=63072000" },
};
if (this.configuration.security?.allowIframe === true) {
delete securityHeaders["x-frame-options"];
}
const jsonHeaders = JSON.stringify(securityHeaders, undefined, 4);
/**
* CloudFront function that manipulates the HTTP responses to add security headers.
*/
const code = `function handler(event) {
var response = event.response;
response.headers = Object.assign({}, ${jsonHeaders}, response.headers);
return response;
}`;

const functionName = ensureNameMaxLength(
`${this.provider.stackName}-${this.provider.region}-${this.id}-response`,
64
);

return new cloudfront.Function(this, "ResponseFunction", {
functionName,
code: cloudfront.FunctionCode.fromInline(code),
});
}

getBucketProps(): BucketProps {
return {
// For a static website, the content is code that should be versioned elsewhere
Expand Down
8 changes: 5 additions & 3 deletions src/utils/getDefaultCfnFunctionAssociations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export function getCfnFunctionAssociations(distribution: CfnDistribution): CfnFu
const defaultBehavior = (distribution.distributionConfig as CfnDistribution.DistributionConfigProperty)
.defaultCacheBehavior as CfnDistribution.DefaultCacheBehaviorProperty;

return (defaultBehavior.functionAssociations as Array<CfnDistribution.FunctionAssociationProperty>).map(
cdkFunctionAssociationToCfnFunctionAssociation
);
return defaultBehavior.functionAssociations instanceof Array
? (defaultBehavior.functionAssociations as Array<CfnDistribution.FunctionAssociationProperty>).map(
cdkFunctionAssociationToCfnFunctionAssociation
)
: [];
}

0 comments on commit 6e2f107

Please sign in to comment.