Serverless custom domain - amazon-web-services

I want to setup a custom domain with the serverless framework and AWS.
This is what I have in serverless.yml
service: chapiLabs-rest-api
plugins:
- serverless-domain-manager
- serverless-express
custom:
customDomain:
domainName: chapitime.chapilabs.com
basePath: ''
stage: ${self:provider.stage}
certificateName: '*.chapilabs.com'
createRoute53Record: true
package:
exclude:
- node_modules
include:
- dist
provider:
name: aws
runtime: nodejs8.10
webpackIncludeModules:
forceInclude:
- mysql
- mysql2
- sequelize
functions:
get:
handler: dist/get.get
events:
- http:
path: v1/client/{id}
method: get
request:
parameters:
paths:
id: true
create:
handler: dist/auth.auth
events:
- http:
path: v1/auth
method: post
I used this npm package as plugin: https://github.com/amplify-education/serverless-domain-manager
I added a certificate via DNS using CloudFlare
Then I made a deploy and got the next log.
In cloudflare I added a CNAME to add the ACM validation and another CNAME pointing cloudfront.
But the domain name is not working. Any ideas why? I didn't bought the domain in AWS.

After some deployments the cloudfront url changed and I didn't noticed. I just updated the CNAME record in CloudFlare and it started working.

Related

AWS Route 53 message forbidden while trying to add A record to cloud front

I am deploying a React.js application using the server less framework on cloudfront.
The architecture is very simple, I have an S3 bucket and a CloudFront CDN.
CloudFormation ends without any error setting up my configuration and I'm able to visit my react page using the CloudFront Distribution domain name using https and default certificate.
Now I bought a domain in Route 53 and I would like to link it to my CloudFront.
I tried everything but I did not succeed. Can anyone please give me some hint?
Action I already tried:
Adding the certificate's are (after creating it) on my resources.CloudFrontDistribution.ViewerCertificate.AcmCertificateArn
Adding manually an A record to point to my CloudFront. This generates the following JSON error on my browser: {"message":"Forbidden"}
Manually adding the certificate through AWS Console on my CloudFront
None of those action gave me any hint about how to solve or even debug the problem.
This is my current serverless.yml:
service: my-app-react
provider:
name: aws
runtime: nodejs12.x
region: eu-south-1
memorySize: 512
timeout: 6
logRetentionInDays: 7
plugins:
- serverless-s3-sync
custom:
bucketName: my-app-react-33333
s3Sync:
- bucketName: ${self:custom.bucketName}
localDir: build/
domains:
dev:
domainName: <my-domain>
certificateName: <my-domain>
resources:
Resources:
ReactAppBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.bucketName}
AccessControl: PublicRead
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: index.html
S3AccessPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: ReactAppBucket
PolicyDocument:
Statement:
- Sid: PublicReadGetObject
Effect: Allow
Principal: "*"
Action:
- s3:GetObject
Resource:
- arn:aws:s3:::${self:custom.bucketName}/*
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
Origins:
- DomainName: ${self:custom.bucketName}.s3.eu-south-1.amazonaws.com
Id: ReactApp
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
DefaultRootObject: index.html
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
DefaultCacheBehavior:
AllowedMethods:
- HEAD
- GET
- DELETE
- OPTIONS
- PATCH
- POST
- PUT
TargetOriginId: ReactApp
ForwardedValues:
QueryString: false
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
ViewerCertificate:
AcmCertificateArn: <my-certificate-arn>
SslSupportMethod: sni-only
MinimumProtocolVersion: TLSv1.2_2021
Oh, now I think I may know your issue. Cluodfront distributions can only use ACM certs created in us-east-1. See Requirements for using SSL/TLS certificates with CloudFront.
I didn't pick up on that before, but it is likely your issue. You don't have to have your distribution or any other resources there, just the certificates.
When I create certs using cloudformation, I set up the certificate handler templates in us-east-1 and the rest of the resource templates in my local region. With terraform, as another example, I have a separate provider just for these resources.

serverless create_domain - Failed to create custom domain

I am trying to use serverless-domain-manager to deploy serverless to custom domains on AWS but every time I run serverless create_domain I get the generic error "Failed to create custom domain". Here are the steps I've taken and the contents of my yml:
Registered domain on AWS
Set up a hosted zone in route 53
Created a certificate for *.mydomain.com in certificate manager in AWS
Created an iAM user with admin privileges
Run aws configure with iAM user keys
.yml
service: service-name
plugins:
- serverless-apigw-binary
- serverless-apigwy-binary
- serverless-content-encoding
- serverless-api-cloudfront
- serverless-plugin-tracing
- serverless-domain-manager
custom:
apigwBinary:
types:
- 'image/jpeg'
- 'text/html'
- 'text/css'
- 'application/javascript'
- 'application/x-javascript'
- '*/*'
contentEncoding:
minimumCompressionSize: 0
stage: ${opt:stage, self:provider.stage}
domains:
prod: api.mydomain.com
test: api-test.mydomain.com
dev: api-dev.mydomain.com
customDomain:
basePath: ""
domainName: ${self:custom.domains.${self:custom.stage}}
stage: "${self:custom.stage}"
createRoute53Record: true
certificateArn: "cert-arn"
hostedZoneId: "zone-id"
provider:
name: aws
runtime: nodejs12.x
endpointType: regional
region: ap-southeast-2
memorySize: 3008
timeout: 300
tracing: true
iamRoleStatements:
- Effect: 'Allow'
Action:
- 'xray:PutTraceSegments'
- 'xray:PutTelemetryRecords'
- 'lambda:InvokeFunction'
Resource:
- '*'
package:
exclude:
- node_modules/aws-sdk/**
- .vscode/**
- bin/**
- obj/**
functions:
api:
warmup: true
handler: index.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
Every time I run serverless create_domain I get the same generic error with no extra info.
Thanks
As Erico pointed out you get a bit more info with SLS_DEBUG. It could be that you have an issue with domain certificate and that debug message would tell you that.
In my case it seemed to be a cert problem as it was in a different AWS region so sls create_domain couldn't find it. Overall it's been a rather confusing experience.
Check out the endpointType option:
customDomain:
...
endpointType="regional"
https://github.com/amplify-education/serverless-domain-manager/issues/46
https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-api-gateway-supports-regional-api-endpoints/
In my case, my hosted zone is in another AWS account, so createRoute53Record: true failed to create the route53 record and the hence the generic error.
Changing to createRoute53Record: false fixed the error for me.

Custom Domain Name for WebSockets API with Serverless

I'm managing a REST API for an app with Serverless and want to extend this setup with a WebSockets API in the same region. Everything should be handled with the same certificate, but different subdomains.
At first I created a new custom domain with sls create_domain --stage=....
Then I tried to add it to the new WebSockets stack, but ended with this error:
Error: Failed to find CloudFormation resources for ...
I found it on Github that this seems to be not supported by CloudFormation right now so Serverless does not support it.
So I tried to attach my stage to the custom domain name manually in the UI:
Mixing of REST APIs and HTTP APIs on the same domain name can only be
accomplished through API Gateway's V2 DomainName interface. Currently,
WebSocket APIs can only be attached to a domain name with other
WebSocket APIs. This must also occur through API Gateway's V2
DomainName interface.
More confusion arises, as it's not even the same domain name in this case. The new domain name was sockets.<DOMAIN>.com and the existing one was api.<DOMAIN>.com. Or do different subdomains are falling into 'same domain name'?
Nevertheless I tried to create the custom domain again via the apigatewayv2 CLI:
aws apigatewayv2 create-domain-name --domain-name <DOMAIN> --domain-name-configurations file://domain-configuration.json --region eu-west-1
domain-configuration.json:
[
{
"ApiGatewayDomainName": "<DOMAIN>",
"CertificateArn": "arn:aws:acm:us-east-1:<ACCOUNT_ID>:certificate/<CERT_ID>",
"CertificateName": "<DOMAIN>",
"DomainNameStatus": "AVAILABLE",
"EndpointType": "EDGE",
"SecurityPolicy": "TLS_1_2"
}
]
But this results in the following error:
An error occurred (BadRequestException) when calling the CreateDomainName operation: Invalid certificate ARN: arn:aws:acm:us-east-1:924441585974:certificate/b88f0a3f-1393-4a16-a876-9830852b5207. Certificate must be in 'eu-west-1'.
My current state was that API Gateway only allows custom certificates to be located in us-east-1, so this error confuses me even more.
Summary: I'm completely stuck on how to get a custom domain name attached to my WebSocket API stage. I'm happy about every hint in the right direction!
Found a solution with a custom CloudFormation resource template:
resources:
Resources:
WebSocketDomainName:
Type: AWS::ApiGatewayV2::DomainName
Properties:
DomainName: <domain-name>
DomainNameConfigurations:
- EndpointType: 'REGIONAL'
CertificateArn: <cert-arn>
WebSocketMapping:
Type: AWS::ApiGatewayV2::ApiMapping
Properties:
ApiId: <api-id>
DomainName: !Ref WebSocketDomainName
Stage: <stage-name>
DNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: <hosted-zone-name>.
TTL: '900'
ResourceRecords:
- !GetAtt [ WebSocketDomainName, RegionalDomainName ]
Name: <domain-name>
Type: CNAME
Edit: Now working with serverless-domain-manager! 🎉
custom:
customDomain:
rest:
domainName: rest.serverless.foo.com
stage: ci
basePath: api
certificateName: '*.foo.com'
createRoute53Record: true
endpointType: 'regional'
securityPolicy: tls_1_2
http:
domainName: http.serverless.foo.com
stage: ci
basePath: api
certificateName: '*.foo.com'
createRoute53Record: true
endpointType: 'regional'
securityPolicy: tls_1_2
websocket:
domainName: ws.serverless.foo.com
stage: ci
basePath: api
certificateName: '*.foo.com'
createRoute53Record: true
endpointType: 'regional'
securityPolicy: tls_1_2
Thanks #tpschmidt for the helpful response!
Just to add, in case you want a BasePathMapping for your websocket domain, add ApiMappingKey to the Cloudformation provided above. E.g.:
WebSocketMapping:
Type: AWS::ApiGatewayV2::ApiMapping
Properties:
ApiId: <api-id>
DomainName: !Ref WebSocketDomainName
Stage: <stage-name>
ApiMappingKey: <stage-name> #(e.g. prod)
I had to make a couple of minor adjustments to have a Lambda in a private VPC with WebSockets support and a custom domain.
The CertificateArn must be from ACM and cannot be a certificate uploaded to IAM.
I used an A-record instead of a CNAME
WebSocketDomainName:
Type: AWS::ApiGatewayV2::DomainName
Properties:
DomainName: !Ref DomainName
DomainNameConfigurations:
- EndpointType: REGIONAL
CertificateArn: <your-certificate-arn>
# This maps /staging or /prod to just the domain name
WebSocketMapping:
Type: AWS::ApiGatewayV2::ApiMapping
Properties:
ApiId: <your-api-id>
DomainName: !Ref WebSocketDomainName
Stage: <your-stage>
Route53DNS:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: !Ref Route53HostedZone
RecordSets:
- Name: !Ref DomainName
Type: A
AliasTarget:
HostedZoneId: ZLY8HYME6SFDD # This means eu-west-1 ApiGateWay
DNSName:
!GetAtt [WebSocketDomainName, RegionalDomainName]
For your HostedZoneId check: https://docs.aws.amazon.com/general/latest/gr/apigateway.html

How to set up different domains based on stage with serverless-domain-manager plugin?

I am trying to set up different, custom domains in serverless with serverless-domain-manager plugin.
I've followed this tutorial:
https://serverless.com/blog/serverless-api-gateway-domain/
but the example doesn't show how to set up more than one custom domain.
In my serverless.yml file I have:
plugins:
- serverless-domain-manager
custom:
customDomain:
basePath: ''
domainName: MY_DOMAIN_HERE
stage: dev
createRoute53Record: true
How can I add another domain? For example, for prod stage?
Even though serverless.yml doesn't support conditional logic, it is possible to simulate conditions.
In serverless.yml add:
custom:
domain:
dev: MY_DEV_DOMAIN
prod: MY_PROD_DOMAIN
customDomain:
basePath: ''
domainName: ${self:custom.domain.${opt:stage}}
stage: ${opt:stage}
createRoute53Record: true
Depending on the sls deploy --stage parameter, either the dev or prod domain will be configured.
More info on conditional logic:
https://forum.serverless.com/t/conditional-serverless-yml-based-on-stage/1763

How to configure a Serverless S3 bucket resource to use a CORS AllowOrigin set to the http endpoint of its function

I'm using Serverless to create a web application that serves its static content, e.g. a web font, from a S3 bucket. The S3 bucket is configured as a resource in my serverless.yml file. Its CORS configuration has an AllowOrigin set to a wildcard.
I want to change this to have an AllowOrigin with the http endpoint of the service as created by Serverless, e.g. 31alib51b6.execute-api.eu-west-1.amazonaws.com.
I wondered if it's possible to configure this in the serverless.yml file itself.
My example serverless.yml file:
service: example-service
provider:
name: aws
runtime: nodejs4.3
region: eu-west-1
functions:
web:
handler: handler.handler
name: ${self:service}-${self:provider.stage}
description: ${self:service} web application - ${self:provider.stage}
events:
- http:
path: web
method: get
- http:
path: web/{proxy+}
method: get
resources:
Resources:
S3Assets:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:service}-${self:provider.stage}-assets
CorsConfiguration:
CorsRules:
- AllowedMethods:
- GET
- HEAD
AllowedOrigins:
- "*"
You can define the AllowedOrigin with the following statement:
CorsConfiguration:
CorsRules:
- AllowedMethods:
- GET
- HEAD
AllowedOrigins:
- Fn::Join:
- ""
- - "https://"
- Ref: ApiGatewayRestApi
- ".execute-api.eu-west-1.amazonaws.com"
"Ref: ApiGatewayRestApi" references the internal name of the generated API.