Skip to content

Commit a1f1642

Browse files
author
amazon-meaisiah
authored
Merge pull request awslabs#414 from awslabs/set-security-headers
2 parents 4fdb5be + 2191174 commit a1f1642

File tree

1,573 files changed

+98309
-7
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,573 files changed

+98309
-7
lines changed

BUILDING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ AWS SAM CLI profile option: optional specific profile from your AWS credential f
146146

147147
Set this to `true` if you want to enable development mode. It's `false` by default, and unless you're actively developing on the developer portal itself locally, you should generally leave it unset as it disables most protections, including CORS.
148148

149+
### `edgeLambdaRebuildToken: string`
150+
151+
*Default: `'defaultRebuildToken'`*
152+
153+
Change this value if you want to update the edge lambda or its replicator lambda in the next deployment. In general, you shouldn't need to set it unless either 1. you're developing against the project and need to make changes to it, or 2. the project updated that part of the code and you just pulled its changes in preparation to update your deployment.
154+
155+
> Why is this not handled internally? At the time of writing, edge lambdas are difficult to delete due to how long it takes for all their replicas to delete, and you can't delete lambdas with active replicas. This process could take anywhere from a couple hours to several days (well past the largest timeout supported by Lambda), and neither CloudFront nor Lambda currently offer any hooks to know when all the replicas are gone. So edge lambda versions are only created, never deleted, by the template.
156+
>
157+
> For this reason, it's better to require the user to explicitly choose when to update the lambda, so that during development, you're not flooded with versions, and during production, you can be better aware of when things change (it's on your account, after all).
158+
149159
### `samTemplate: string`
150160

151161
*Default: `cloudformation/template.yaml` relative to the repo's root*

cloudformation/template.yaml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ Parameters:
7979
Description: Provide a token different from the last deployment's token to re-upload the dev portal site's static assets. You can provide a timestamp or GUID on each deployment to always re-upload the assets.
8080
Default: 'defaultRebuildToken'
8181

82+
EdgeLambdaRebuildToken:
83+
Type: String
84+
Description: Provide a token different from the last deployment's token to update the edge lambda. You can provide a timestamp or GUID on each deployment to always update it.
85+
Default: 'defaultRebuildToken'
86+
8287
StaticAssetRebuildMode:
8388
Type: String
8489
Description: By default, a static asset rebuild doesn't overwrite custom-content. Provide the value `overwrite-content` to replace the custom-content with your local version. Don't do this unless you know what you're doing -- all custom changes in your s3 bucket will be lost.
@@ -1714,6 +1719,93 @@ Resources:
17141719
DevelopmentMode: !Ref DevelopmentMode
17151720
FeedbackEnabled: !If [ EnableFeedbackSubmission, 'true', 'false' ]
17161721

1722+
LambdaEdgeFunctionRole:
1723+
Type: AWS::IAM::Role
1724+
Properties:
1725+
Path: "/"
1726+
ManagedPolicyArns:
1727+
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
1728+
AssumeRolePolicyDocument:
1729+
Version: '2012-10-17'
1730+
Statement:
1731+
-
1732+
Sid: "AllowLambdaServiceToAssumeRole"
1733+
Effect: "Allow"
1734+
Action:
1735+
- "sts:AssumeRole"
1736+
Principal:
1737+
Service:
1738+
- "lambda.amazonaws.com"
1739+
- "edgelambda.amazonaws.com"
1740+
1741+
CloudFrontSecurityHeadersLambda:
1742+
Type: AWS::Serverless::Function
1743+
Properties:
1744+
CodeUri: ../lambdas/cloudfront-security
1745+
Handler: replicator.handler
1746+
MemorySize: 128
1747+
Role: !GetAtt CloudFrontEdgeReplicatorRole.Arn
1748+
Runtime: nodejs12.x
1749+
Timeout: 300
1750+
AutoPublishAlias: Live
1751+
Environment:
1752+
Variables:
1753+
Bucket: !Ref ArtifactsS3BucketName
1754+
Layers:
1755+
- !Ref LambdaCommonLayer
1756+
1757+
CloudFrontSecurityHeadersSetup:
1758+
Type: AWS::CloudFormation::CustomResource
1759+
Properties:
1760+
ServiceToken: !GetAtt CloudFrontSecurityHeadersLambda.Arn
1761+
Name: !Ref CloudFrontSecurityHeadersLambda
1762+
RoleArn: !GetAtt LambdaEdgeFunctionRole.Arn
1763+
RebuildToken: !Ref EdgeLambdaRebuildToken
1764+
1765+
CloudFrontEdgeReplicatorRole:
1766+
Type: AWS::IAM::Role
1767+
Properties:
1768+
AssumeRolePolicyDocument:
1769+
Version: '2012-10-17'
1770+
Statement:
1771+
Effect: Allow
1772+
Principal:
1773+
Service: lambda.amazonaws.com
1774+
Action: sts:AssumeRole
1775+
Path: '/'
1776+
Policies:
1777+
- PolicyName: root
1778+
PolicyDocument:
1779+
Version: '2012-10-17'
1780+
Statement:
1781+
- Effect: Allow
1782+
Action:
1783+
- logs:CreateLogGroup
1784+
- logs:CreateLogStream
1785+
- logs:PutLogEvents
1786+
Resource: arn:aws:logs:*:*:*
1787+
- Effect: Allow
1788+
Action:
1789+
- lambda:CreateFunction
1790+
- lambda:GetFunction
1791+
- lambda:UpdateFunctionCode
1792+
- lambda:PublishVersion
1793+
Resource: arn:aws:lambda:*:*:*
1794+
- Effect: Allow
1795+
Action:
1796+
- iam:PassRole
1797+
Resource: !Sub 'arn:aws:iam::${AWS::AccountId}:role/*'
1798+
- Effect: Allow
1799+
Action:
1800+
- s3:GetObject
1801+
- s3:DeleteObject
1802+
- s3:PutObject
1803+
Resource: !Join
1804+
- ''
1805+
- - 'arn:aws:s3:::'
1806+
- !Ref ArtifactsS3BucketName
1807+
- '/*'
1808+
17171809
CloudFrontOriginAccessIdentity:
17181810
Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
17191811
Condition: 'NotDevelopmentMode'
@@ -1738,6 +1830,10 @@ Resources:
17381830
QueryString: true
17391831
TargetOriginId: 'dev-portal-site-s3-bucket'
17401832
ViewerProtocolPolicy: redirect-to-https
1833+
LambdaFunctionAssociations:
1834+
-
1835+
EventType: origin-response
1836+
LambdaFunctionARN: !GetAtt CloudFrontSecurityHeadersSetup.EdgeArn
17411837
DefaultRootObject: index.html
17421838
Enabled: true
17431839
Comment: !Sub '${AWS::StackName} distribution'
@@ -1767,6 +1863,10 @@ Resources:
17671863
QueryString: true
17681864
TargetOriginId: 'dev-portal-site-s3-bucket'
17691865
ViewerProtocolPolicy: redirect-to-https
1866+
LambdaFunctionAssociations:
1867+
-
1868+
EventType: origin-response
1869+
LambdaFunctionARN: !GetAtt CloudFrontSecurityHeadersSetup.EdgeArn
17701870
DefaultRootObject: index.html
17711871
Enabled: true
17721872
Comment: !Sub '${AWS::StackName} distribution'

dev-portal/example-deployer.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@ module.exports = {
3232
// cognitoDomainAcmCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/98765432-9876-9876-9876-987654321098',
3333
// useRoute53Nameservers: true,
3434
// feedbackEmail: '[email protected]',
35+
36+
// Toggle this any time the edge lambda or its replicator lambda need updated. You will be told in
37+
// the migration instructions to do so if you need to.
38+
// edgeLambdaResetToken: 'reset',
3539
}

dev-portal/example-dev-deployer.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,9 @@ module.exports = {
3131

3232
// Set development mode for local use.
3333
developmentMode: true,
34+
35+
// Toggle this any time the edge lambda or its replicator lambda are updated. In general, unless
36+
// either you're modifying them yourself or they were changed upstream and you just pulled those
37+
// changes, you shouldn't need to do anything about this value.
38+
// edgeLambdaResetToken: 'reset',
3439
}

dev-portal/src/components/SwaggerUiLayout.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ function InfoReplacement ({ specSelectors }) {
4545
const docsUrl = externalDocs.get('url')
4646

4747
return <Observer>
48-
{() => <Container fluid textAlign='left' className='fixfloat' style={{ padding: '40px 0px' }}>
48+
{/*
49+
If no API is loaded, let's just swallow the state and move on. (Swagger UI doesn't offer any
50+
way to clean up after itself.)
51+
*/}
52+
{() => store.api == null ? null : <Container fluid textAlign='left' className='fixfloat' style={{ padding: '40px 0px' }}>
4953
<div style={{ display: 'flex' }}>
5054
<div style={{ flex: '0 0 auto', marginRight: '20px' }}>
5155
<Image size='small' src={store.api.logo} />

dev-portal/src/pages/Apis.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ export default observer(class ApisPage extends React.Component {
2727
containerRef = React.createRef()
2828
hasRoot = false
2929

30-
componentDidMount () { this.updateApi() }
31-
componentDidUpdate () { this.updateApi() }
30+
componentDidMount () { this.updateApi(true) }
31+
componentDidUpdate () { this.updateApi(false) }
3232
componentWillUnmount () { this.containerRef = null }
3333

34-
updateApi () {
35-
return getApi(this.props.match.params.apiId || 'ANY', true, this.props.match.params.stage)
34+
updateApi (isInitial) {
35+
return getApi(this.props.match.params.apiId || 'ANY', true, this.props.match.params.stage, isInitial)
3636
.then(api => {
3737
if (this.containerRef == null) return
3838
const elem = this.containerRef.current

lambdas/backend/routes/admin/catalog/visibility.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ async function deleteFile (file) {
168168
}
169169

170170
exports.post = async (req, res) => {
171-
console.log(`POST /admin-catalog-visibility for Cognito ID: ${util.getCognitoIdentityId(req)}`)
171+
console.log(`POST /admin/catalog/visibility for Cognito ID: ${util.getCognitoIdentityId(req)}`)
172172

173173
// for apigateway managed APIs, provide "apiId_stageName"
174174
// in the apiKey field
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict'
2+
3+
const headersToAdd = {
4+
// Don't include subdomains - the user might have a subdomain hosted on something else and not
5+
// support it.
6+
'strict-transport-security': [{ value: 'max-age=63072000' }],
7+
'content-security-policy': [{
8+
value: [
9+
"default-src 'none'",
10+
'connect-src *',
11+
"prefetch-src 'self'",
12+
"font-src 'self' data: fonts.gstatic.com",
13+
"img-src 'self' data:",
14+
"script-src 'self' 'unsafe-inline'",
15+
"style-src 'self' fonts.googleapis.com"
16+
].join(';')
17+
}],
18+
'x-content-type-options': [{ value: 'nosniff' }],
19+
'x-frame-options': [{ value: 'DENY' }],
20+
'x-xss-protection': [{ value: '1; mode=block' }],
21+
'referrer-policy': [{ value: 'same-origin' }]
22+
}
23+
24+
exports.handler = (event, context, callback) => {
25+
const response = event.Records[0].cf.response
26+
console.log('Response headers:', response.headers)
27+
Object.assign(response.headers, headersToAdd)
28+
callback(null, response)
29+
}

lambdas/cloudfront-security/node_modules/archiver-utils/CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lambdas/cloudfront-security/node_modules/archiver-utils/LICENSE

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)