-
Notifications
You must be signed in to change notification settings - Fork 8
/
serverless.yml
326 lines (309 loc) · 12.9 KB
/
serverless.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# Copyright The Linux Foundation and each contributor to CommunityBridge.
# SPDX-License-Identifier: MIT
service: easycla-contributor-console-ui
frameworkVersion: '^3.33.0'
configValidationMode: warn
package:
patterns:
- '!./**'
- edge/dist/*
provider:
name: aws
runtime: nodejs16.x
stage: ${opt:stage}
region: us-east-1 # Region can't be configurable, lambda@edge is us-east-1 only.
deploymentBucket:
serverSideEncryption: AES256 # Make sure items are uploaded encrypted.
role: EdgeRole
tracing:
apiGateway: true
lambda: true
timeout: 60 # optional, in seconds, default is 6
# This will add tags to every resource generated by your CloudFormation template
stackTags:
Name: ${self:service}
STAGE: ${self:provider.stage}
ServiceType: Product
Service: ${self:service}
ServiceRole: UI
ProgrammingPlatform: Angular
Owner: 'David Deal'
iamRoleStatements:
- Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
Resource: "*"
- Effect: Allow
Action:
- s3:GetObject
Resource:
- "arn:aws:s3:::cla-signature-files-${opt:stage}/*"
- "arn:aws:s3:::cla-project-logo-${opt:stage}/*"
- Effect: Allow
Action:
- ssm:GetParameter
Resource:
- arn:aws:ssm:${aws:region}:${aws:accountId}:parameter/cla-*
plugins:
# Serverless Finch does s3 uploading. Called with 'sls client deploy'.
# Also allows bucket removal with 'sls client remove'.
- serverless-finch
# Automatically versions and updates the lambda@edge function
#- serverless-lambda-version
# Automatically invalidates cloudfront after frontend bucket is deployed
#- serverless-cloudfront-invalidate
- serverless-plugin-tracing
- serverless-prune-plugin
- '@silvermine/serverless-plugin-cloudfront-lambda-edge'
custom:
project: ${file(./project-vars.yml):projectIdentifier}
client: # Configurations for serverless finch.
bucketName: ${self:custom.project}-${opt:stage}-${self:service}
distributionFolder: dist/easycla-contributor-console
indexDocument: index.html
# Because our application is a Single Page Application, we always want our index
# documents to handle 404/403 urls.
errorDocument: index.html
product:
root:
name:
dev: 'dev.communitybridge.org'
staging: 'staging.communitybridge.org'
prod: 'easycla.lfx.linuxfoundation.org'
other: 'dev.communitybridge.org'
domain:
name:
dev: 'easycla.dev.communitybridge.org'
staging: 'easycla.staging.communitybridge.org'
prod: 'contributor.easycla.lfx.linuxfoundation.org'
other: 'easycla.dev.communitybridge.org'
certificate:
arn:
# ARN of the Externally generated certificate
dev: arn:aws:acm:us-east-1:395594542180:certificate/14e2ed6d-3484-42ec-aff0-70475c965b6f
staging: arn:aws:acm:us-east-1:844390194980:certificate/2905cc06-b9b5-4cc9-a0f7-f5e201e0a042
prod: arn:aws:acm:us-east-1:716487311010:certificate/4a3c3018-df9e-4c3a-84a6-231317f8bcec
other: 'invalid - value'
# CloudFront invalidation plugin configuration
#cloudfrontInvalidate:
# # Grab the distribution ID key from the output section
# distributionIdKey: 'CloudFrontDistributionId'
# items: # one or more paths required
# - '/*'
prune:
automatic: true
number: 3
functions:
# Configure a lambda@edge handler. Lambda@edge is a function that adds http headers to
# cloudfront requests/responses. We use it to enforce HTTP security best practices.
clientEdgeResponse:
# Should match edge/src/index.js export name - eg. exports.handler => index.handler
handler: edge/dist/index.handler
memorySize: 128 # This is the maximum memory size for lambda@edge functions
timeout: 1 # This is the maximum execution time for lambda@edge functions
lambdaAtEdge:
# The logical name used in your Resources section to define the CloudFront distribution.
distribution: 'CloudFrontDistribution'
eventType: 'viewer-response'
resources:
Conditions:
# true when a TSL certificate should be created by serverless (false created externally)
ShouldGenerateCertificate:
Fn::Not: [Fn::Equals: ["${env:STAGE}", "prod"]]
Resources:
# The bucket the website is uploaded to. We make sure to turn on AES256 encryption, which
# is best practice.
WebsiteDeploymentBucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: ${self:custom.project}-${opt:stage}-${self:service}
# Policy that only exposes bucket to cloudfront with proper
# Origin Access Identity
WebsiteDeploymentBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: WebsiteDeploymentBucket
PolicyDocument:
Statement:
- Action:
- "s3:GetObject"
Effect: Allow
Resource:
"Fn::Join":
- ""
- - "arn:aws:s3:::"
- Ref: WebsiteDeploymentBucket
- "/*"
Principal:
AWS:
"Fn::Join":
- " "
- - "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity"
- Ref: WebsiteOriginAccessIdentity
WebsiteOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: "CloudFrontOriginAccessIdentity for ${self:custom.project}-${opt:stage}-${self:service}"
# The cloudfront distribution wraps around our static website S3 bucket. Using a CDN to host our SPA is good
# practice, and also lets us set custom headers using lambda@edge.
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
DependsOn:
- WebsiteDeploymentBucket
Properties:
DistributionConfig:
Enabled: true
Aliases:
- ${self:custom.product.domain.name.${opt:stage}, self:custom.product.domain.name.other}
ViewerCertificate:
Fn::If:
- ShouldGenerateCertificate
- AcmCertificateArn:
Ref: Cert
# The distribution accepts HTTPS connections from only viewers that support server name indication
# Recommended, most browsers and clients released after 2010 support SNI.
SslSupportMethod: sni-only
# Specify the security policy that you want CloudFront to use for HTTPS connections
# Recommend that you specify TLSv1.2_2018 unless your viewers are using browsers or devices that don’t support TLSv1.2
# Allowed Values: SSLv3 | TLSv1 | TLSv1.1_2016 | TLSv1.2_2018 | TLSv1_2016
MinimumProtocolVersion: TLSv1.2_2018
- AcmCertificateArn: ${self:custom.certificate.arn.${opt:stage}, self:custom.certificate.arn.other}
# The distribution accepts HTTPS connections from only viewers that support server name indication
# Recommended, most browsers and clients released after 2010 support SNI.
SslSupportMethod: sni-only
# Specify the security policy that you want CloudFront to use for HTTPS connections
# Recommend that you specify TLSv1.2_2018 unless your viewers are using browsers or devices that don’t support TLSv1.2
# Allowed Values: SSLv3 | TLSv1 | TLSv1.1_2016 | TLSv1.2_2018 | TLSv1_2016
MinimumProtocolVersion: TLSv1.2_2018
Origins:
- DomainName: { "Fn::GetAtt": [ WebsiteDeploymentBucket, DomainName ] }
Id:
Ref: WebsiteDeploymentBucket
S3OriginConfig:
OriginAccessIdentity:
"Fn::Join":
- ""
- - "origin-access-identity/cloudfront/"
- Ref: WebsiteOriginAccessIdentity
# Routes besides / will result in S3 serving a 403
# Redirect all routes back to the SPA where routes should
# be handled
CustomErrorResponses:
- ErrorCode: 403
ResponseCode: 200
ErrorCachingMinTTL: 1
ResponsePagePath: '/index.html'
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: '/index.html'
HttpVersion: http2
DefaultRootObject: index.html
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
Compress: true # Turns on gzipping
#DefaultTTL: 86400 # Defaults to a day if no Cache-Control header is set.
DefaultTTL: 600 # 10 minutes only due to users seeing a lot of stale cache issues after release (even after invalidating
MinTTL: 0
#MaxTTL: 31536000 # Can keep the file in the cloudfront cache for a maximum of a year.
MaxTTL: 600 # 10 minutes only due to users seeing a lot of stale cache issues after release (even after invalidating
TargetOriginId:
Ref: WebsiteDeploymentBucket
ForwardedValues:
QueryString: true
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_100 # Cheapest class, only hosts content at North American cloudfront locations.
# Severless usually generates our roles out of the box, but lambda@edge support is lacking, so we have to create
# our own. This role can assume the edgelambda.amazonaws.com role, (the lambda won't run without it).
EdgeRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${self:custom.project}-${opt:stage}-${self:service}-edge-role
Path: /
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- edgelambda.amazonaws.com # This is the important part of this role.
Action:
- sts:AssumeRole
Policies:
- PolicyName: ${self:custom.project}-${opt:stage}-${self:service}-origin-response-policy #LogGroupPolicy-${self:provider.stage} # Permissions to access Lambda@edge log groups.
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:GetFunction
- lambda:EnableReplication
Resource:
- arn:aws:lambda:${aws:region}:${aws:accountId}:function:${self:service}-${opt:stage}-*
- Effect: Allow
Action:
- iam:CreateServiceLinkedRole
- cloudFront:UpdateDistribution
- cloudFront:CreateDistribution
Resource:
- '*'
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObjectAcl
- s3:GetObject
- s3:ListBucket
- s3:DeleteObject
- s3:GetBucketLocation
- s3:PutObjectAcl
Resource:
- arn:aws:s3:::${self:custom.project}-${opt:stage}-${self:service}
- arn:aws:s3:::${self:custom.project}-${opt:stage}-${self:service}/*
- Effect: Allow
Action:
- s3:ListAllMyBuckets
Resource:
- '*'
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:DescribeLogGroups
- logs:DescribeLogStreams
- logs:PutLogEvents
- logs:GetLogEvents
- logs:FilterLogEvents
Resource:
- arn:aws:logs:${aws:region}:${aws:accountId}:log-group:*
ManagedPolicyArns:
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Cert:
Type: AWS::CertificateManager::Certificate
Condition: ShouldGenerateCertificate
Properties:
DomainName: ${self:custom.product.root.name.${opt:stage}, self:custom.product.root.name.other}
SubjectAlternativeNames:
- ${self:custom.product.domain.name.${opt:stage}, self:custom.product.domain.name.other}
ValidationMethod: DNS
Outputs:
CloudFrontDistributionId:
Value:
Ref: CloudFrontDistribution
WebsiteDeploymentBucketName:
Value:
Ref: WebsiteDeploymentBucket
Export:
Name: WebsiteDeploymentBucketName