AWS Compute Blog
Automating mutual TLS setup for Amazon API Gateway
This post is courtesy of Pankaj Agrawal, Solutions Architect.
In September 2020, Amazon API Gateway announced support for mutual Transport Layer Security (TLS) authentication. This is a new method for client-to-server authentication that can be used with API Gateway’s existing authorization options. Mutual TLS (mTLS) is an extension of Transport Layer Security(TLS), requiring both the server and client to verify each other.
Mutual TLS is commonly used for business-to-business (B2B) applications. It’s used in standards such as Open Banking, which enables secure open API integrations for financial institutions. It’s also common for Internet of Things (IoT) applications to authenticate devices using digital certificates.
This post covers automating the mTLS setup for API Gateway HTTP APIs, but the same steps can also be used for REST APIs as well. Download the code used in this walkthrough from the project’s GitHub repo.
Overview
To enable mutual TLS, you must create an API with a valid custom domain name. Mutual TLS is available for both regional REST APIs and the newer HTTP APIs. To set up mutual TLS with API Gateway, you must upload a certificate authority (CA) public key certificate to Amazon S3. This is called a truststore and is used for validating client certificates.
The AWS Certificate Manager Private Certificate Authority (ACM Private CA) is a highly available private CA service. I am using the ACM Private CA as a certificate authority to configure HTTP APIs and to distribute certificates to clients.
Deploying the solution
To deploy the application, the solution uses the AWS Serverless Application Model (AWS SAM). AWS SAM provides shorthand syntax to define functions, APIs, databases, and event source mappings. As a prerequisite, you must have AWS SAM CLI and Java 8 installed. You must also have the AWS CLI configured.
To deploy the solution:
- Clone the GitHub repository and build the application with the AWS SAM CLI. Run the following commands in a terminal:
git clone https://github.com/aws-samples/api-gateway-auth.git cd api-gateway-auth sam build
- Deploy the application:
sam deploy --guided
Provide a stack name and preferred AWS Region for the deployment process. The template requires three parameters:
- HostedZoneId: The template uses an Amazon Route 53 public hosted zone to configure the custom domain. Provide the hosted zone ID where the record set must be created.
- DomainName: The custom domain name for the API Gateway HTTP API.
- TruststoreKey: The name for the trust store file in S3 bucket, which is used by API Gateway for mTLS. By default its truststore.pem.
After deployment, the stack outputs the ARN of a test client certificate (ClientOneCertArn). This is used to validate the setup later. The API Gateway HTTP API endpoint is also provided as output.
You have now created an API Gateway HTTP APIs endpoint using mTLS.
Setting up the ACM Private CA
The AWS SAM template starts with setting up the ACM Private CA. This enables you to create a hierarchy of certificate authorities with up to five levels. A well-designed CA hierarchy offers benefits such as granular security controls and division of administrative tasks. To learn more about the CA hierarchy, visit designing a CA hierarchy. The ACM Private CA is used to configure HTTP APIs and to distribute certificates to clients.
First, a root CA is created and activated, followed by a subordinate CA following best practices. The subordinate CA is used to configure mTLS for the API and distribute the client certificates.
PrivateCA:
Type: AWS::ACMPCA::CertificateAuthority
Properties:
KeyAlgorithm: RSA_2048
SigningAlgorithm: SHA256WITHRSA
Subject:
CommonName: !Sub "${AWS::StackName}-rootca"
Type: ROOT
PrivateCACertificate:
Type: AWS::ACMPCA::Certificate
Properties:
CertificateAuthorityArn: !Ref PrivateCA
CertificateSigningRequest: !GetAtt PrivateCA.CertificateSigningRequest
SigningAlgorithm: SHA256WITHRSA
TemplateArn: 'arn:aws:acm-pca:::template/RootCACertificate/V1'
Validity:
Type: YEARS
Value: 10
PrivateCAActivation:
Type: AWS::ACMPCA::CertificateAuthorityActivation
Properties:
Certificate: !GetAtt
- PrivateCACertificate
- Certificate
CertificateAuthorityArn: !Ref PrivateCA
Status: ACTIVE
MtlsCA:
Type: AWS::ACMPCA::CertificateAuthority
Properties:
Type: SUBORDINATE
KeyAlgorithm: RSA_2048
SigningAlgorithm: SHA256WITHRSA
Subject:
CommonName: !Sub "${AWS::StackName}-mtlsca"
MtlsCertificate:
DependsOn: PrivateCAActivation
Type: AWS::ACMPCA::Certificate
Properties:
CertificateAuthorityArn: !Ref PrivateCA
CertificateSigningRequest: !GetAtt
- MtlsCA
- CertificateSigningRequest
SigningAlgorithm: SHA256WITHRSA
TemplateArn: 'arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen3/V1'
Validity:
Type: YEARS
Value: 3
MtlsActivation:
Type: AWS::ACMPCA::CertificateAuthorityActivation
Properties:
CertificateAuthorityArn: !Ref MtlsCA
Certificate: !GetAtt
- MtlsCertificate
- Certificate
CertificateChain: !GetAtt
- PrivateCAActivation
- CompleteCertificateChain
Status: ACTIVE
Issuing client certificate from ACM Private CA
Create a client certificate, which is used as a test certificate to validate the mTLS setup:
ClientOneCert:
DependsOn: MtlsActivation
Type: AWS::CertificateManager::Certificate
Properties:
CertificateAuthorityArn: !Ref MtlsCA
CertificateTransparencyLoggingPreference: ENABLED
DomainName: !Ref DomainName
Tags:
- Key: Name
Value: ClientOneCert
Setting up a truststore in Amazon S3
The ACM Private CA is ready for configuring mTLS on the API. The configuration uses an S3 object as its truststore to validate client certificates. To automate this, an AWS Lambda backed custom resource copies the public certificate chain of the ACM Private CA to the S3 bucket:
TrustStoreBucket:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: Enabled
TrustedStoreCustomResourceFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: TrustedStoreCustomResourceFunction
Handler: com.auth.TrustedStoreCustomResourceHandler::handleRequest
Timeout: 120
Policies:
- S3CrudPolicy:
BucketName: !Ref TrustStoreBucket
The example custom resource is written in Java but it could also be written in another language runtime. The custom resource is invoked with the public certificate details of the private root CA, subordinate CAs, and the target S3 bucket. The Lambda function then concatenates the certificate chain and stores the object in the S3 bucket.
TrustedStoreCustomResource:
Type: Custom::TrustedStore
Properties:
ServiceToken: !GetAtt TrustedStoreCustomResourceFunction.Arn
TrustStoreBucket: !Ref TrustStoreBucket
TrustStoreKey: !Ref TruststoreKey
Certs:
- !GetAtt MtlsCertificate.Certificate
- !GetAtt PrivateCACertificate.Certificate
You can view and download the handler code for the Lambda-backed custom resource from the repo.
Configuring Amazon API Gateway HTTP APIs with mTLS
With a valid truststore object in the S3 bucket, you can set up the API. A valid custom domain must be configured for API Gateway to enable mTLS. The following code creates and sets up a custom domain for HTTP APIs. See template.yaml for a complete example.
CustomDomainCert:
Type: AWS::CertificateManager::Certificate
Properties:
CertificateTransparencyLoggingPreference: ENABLED
DomainName: !Ref DomainName
DomainValidationOptions:
- DomainName: !Ref DomainName
HostedZoneId: !Ref HostedZoneId
ValidationMethod: DNS
SampleHttpApi:
Type: AWS::Serverless::HttpApi
DependsOn: TrustedStoreCustomResource
Properties:
CorsConfiguration:
AllowMethods:
- GET
AllowOrigins:
- http://localhost:8080
Domain:
CertificateArn: !Ref CustomDomainCert
DomainName: !Ref DomainName
EndpointConfiguration: REGIONAL
SecurityPolicy: TLS_1_2
MutualTlsAuthentication:
TruststoreUri: !GetAtt TrustedStoreCustomResource.TrustStoreUri
TruststoreVersion: !GetAtt TrustedStoreCustomResource.ObjectVersion
Route53:
EvaluateTargetHealth: False
HostedZoneId: !Ref HostedZoneId
DisableExecuteApiEndpoint: true
An Amazon Route 53 public hosted zone is used to configure the custom domain. This must be set up in your AWS account separately and you must provide the hosted zone ID as a parameter to the template.
Since the HTTP APIs default endpoint does not require mutual TLS, it is disabled via DisableExecuteApiEndpoint
. This helps to ensure that mTLS authentication is enforced for all traffic to the API.
The sample API invokes a Lambda function and returns the request payload as the response.
Testing and validating the setup
To validate the setup, first export the client certificate created earlier. You can export the certificate by using the AWS Management Console or AWS CLI. This example uses the AWS CLI to export the certificate. To learn how to do this via the console, see exporting a private certificate using the console.
- Export the base64 PEM-encoded certificate to a local file, client.pem.
aws acm export-certificate --certificate-arn <<Certificat ARN from stack output>>
--passphrase $(echo -n 'your paraphrase' | base64) --region us-east-2 | jq -r '"\(.Certificate)"' > client.pem
- Export the encrypted private key associated with the public key in the certificate and save it to a local file client.encrypted.key. You must provide a passphrase to associate with the encrypted private key. This is used to decrypt the exported private key.
aws acm export-certificate --certificate-arn <<Certificat ARN from stack output>>
--passphrase $(echo -n 'your paraphrase' | base64) --region us-east-2| jq -r '"\(.PrivateKey)"' > client.encrypted.key
- Decrypt the exported private key using passphrase and OpenSSL:
openssl rsa -in client.encrypted.key -out client.decrypted.key
- Access the API using mutual TLS:
curl -v --cert client.pem --key client.decrypted.key https://demo-api.example.com
Adding a certificate revocation list
AWS Certificate Manager Private Certificate Authority (ACM Private CA) can be natively configured with an optional certificate revocation list (CRL).
CRL is a way for certificate authority (CA) to make it known that one or more of their digital certificates is no longer trustworthy. When they revoke a certificate, they invalidate the certificate ahead of its expiration date. The certificate authority can revoke an issued certificate for several reasons, the most common one being that the certificate’s private key are compromised.
API Gateway HTTP APIs mTLS setup can be used along with all existing API Gateway authorizer options. You can further extend validation to AWS Lambda authorizers, which can be configured to validate the client certificates against this certificate revocation list (CRL). For example:
For Lambda authorizer blueprint examples, refer to aws-apigateway-lambda-authorizer-blueprints.
Conclusion
Mutual TLS (mTLS) for API Gateway is now generally available at no additional cost. This post shows how to automate mutual TLS for Amazon API Gateway HTTP APIs using the AWS Certificate Manager Private Certificate Authority as a private CA. Using infrastructure as code (IaC) enables you to develop, deploy, and scale cloud applications, often with greater speed, less risk, and reduced cost.
Download the complete working example for deploying mTLS with API Gateway at this GitHub repo. To learn more about Amazon API Gateway, visit the API Gateway developer guide documentation.
For more serverless learning resources, visit Serverless Land.