Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save fancyremarker/075ffaa4fa9019ceffc310c210d86242 to your computer and use it in GitHub Desktop.
Save fancyremarker/075ffaa4fa9019ceffc310c210d86242 to your computer and use it in GitHub Desktop.
AWSTemplateFormatVersion: '2010-09-09'
Description: |
Template for a CloudFront distribution that serves Secure Headers
Parameters:
Domain:
Type: String
Description: |
CNAME at which CloudFront distribution will be served (e.g., foobar.com)
BaseReportUri:
Type: String
Description: |
Base URL for Report URI (e.g., https://foobar.report-uri.com/r/d)
OriginDomain:
Type: String
Description: |
Domain name of server to which CloudFront distribution should proxy
requests (e.g., foobar.com.s3-website-us-east-1.amazonaws.com)
Resources:
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
DefaultCacheBehavior:
LambdaFunctionAssociations:
- EventType: viewer-response
LambdaFunctionARN:
!Ref SecureHeadersLambdaFunctionWithVersion
ForwardedValues:
QueryString: true
MinTTL: '0'
SmoothStreaming: false
TargetOriginId: !Ref Domain
ViewerProtocolPolicy: redirect-to-https
Aliases:
- !Ref Domain
Enabled: true
Origins:
- CustomOriginConfig:
HTTPPort: '80'
HTTPSPort: '443'
OriginProtocolPolicy: http-only
DomainName: !Ref OriginDomain
Id: !Ref Domain
SecureHeadersSourceCode:
Type: AWS::CloudFormation::CustomResource
Properties:
ServiceToken: !GetAtt LambdaIdentity.Arn
Code: !Sub
- |
'use strict';
exports.handler = (event, context, callback) => {
const response = event.Records[0].cf.response;
const headers = response.headers;
headers['strict-transport-security'] = [{
key: 'Strict-Transport-Security',
value: 'max-age=31536000'
}];
headers['x-frame-options'] = [{
key: 'X-Frame-Options',
value: 'DENY'
}];
headers['x-xss-protection'] = [{
key: 'X-XSS-Protection',
value: '1; report-uri=${BaseReportUri}/xss/enforce'
}];
headers['x-content-type-options'] = [{
key: 'X-Content-Type-Options',
value: 'nosniff'
}];
headers['referrer-policy'] = [{
key: 'Referrer-Policy',
value: 'strict-origin'
}];
headers['expect-ct'] = [{
key: 'Expect-CT',
value: 'max-age=3600; report-uri=${BaseReportUri}/ct/reportOnly'
}];
callback(null, response);
};
- BaseReportUri: !Ref BaseReportUri
SecureHeadersLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: !GetAtt SecureHeadersSourceCode.Code
Runtime: nodejs6.10
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
Path: /service-role/
RoleName: !Join
- "-"
- - !Ref Domain
- SecureHeadersExecutionRole
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- edgelambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
SecureHeadersLambdaFunctionWithVersion:
Type: AWS::CloudFormation::CustomResource
Properties:
ServiceToken: !GetAtt LambdaVersioner.Arn
FunctionName:
!Ref SecureHeadersLambdaFunction
Code:
!GetAtt SecureHeadersSourceCode.Code
DependsOn: SecureHeadersLambdaFunction
LambdaVersioner:
Type: AWS::Lambda::Function
Properties:
Role: !GetAtt LambdaVersionerExecutionRole.Arn
Code:
ZipFile: !Sub |
var AWS = require('aws-sdk');
var response = require('cfn-response');
exports.handler = (event, context) => {
console.log("Request received:\n", JSON.stringify(event));
if (event.RequestType == 'Delete') {
return response.send(event, context, response.SUCCESS);
}
var lambda = new AWS.Lambda();
lambda.publishVersion({ FunctionName: event.ResourceProperties.FunctionName }).promise().then((data) => {
return response.send(event, context, response.SUCCESS, { Version: data.Version }, data.FunctionArn);
}).catch((e) => {
return response.send(event, context, response.FAILED, e);
});
};
Handler: index.handler
Runtime: nodejs6.10
Timeout: 30
LambdaVersionerExecutionRole:
Type: AWS::IAM::Role
Properties:
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: PublishVersion
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: ['lambda:PublishVersion']
Resource: '*'
LambdaIdentity:
Type: AWS::Lambda::Function
Properties:
Role: !GetAtt LambdaIdentityExecutionRole.Arn
Code:
ZipFile: >
var r = require('cfn-response');
exports.handler = function(ev, ctx) {
r.send(ev, ctx, r.SUCCESS, ev.ResourceProperties);
};
Handler: index.handler
Runtime: nodejs6.10
Timeout: 30
LambdaIdentityExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment