AWS Compute Blog
Propagating valid mTLS client certificate identity to downstream services using Amazon API Gateway
This blog written by Omkar Deshmane, Senior SA and Anton Aleksandrov, Principal SA, Serverless.
This blog shows how to use Amazon API Gateway with a custom authorizer to process incoming requests, validate the mTLS client certificate, extract the client certificate subject, and propagate it to the downstream application in a base64 encoded HTTP header.
This pattern allows you to terminate mTLS at the edge so downstream applications do not need to perform client certificate validation. With this approach, developers can focus on application logic and offload mTLS certificate management and validation to a dedicated service, such as API Gateway.
Overview
Authentication is one of the core security aspects that you must address when building a cloud application. Successful authentication proves you are who you are claiming to be. There are various common authentication patterns, such as cookie-based authentication, token-based authentication, or the topic of this blog – a certificate-based authentication.
Transport Layer Security (TLS) certificates are at the core of a secure and safe internet. TLS certificates secure the connection between the client and server by encrypting data, ensuring private communication. When using the TLS protocol, the server must prove its identity to the client using a certificate signed by a certificate authority trusted by the client.
Mutual TLS (mTLS) introduces an additional layer of security, in which both the client and server must prove their identities to each other. Developers commonly use mTLS for application-to-application authentication — using digital certificates to represent both client and server apps is a common authentication pattern for building such workflows. We highly recommended decoupling the mTLS implementation from the application business logic so that you do not have to update the application when changing the mTLS configuration. It is a common pattern to implement the mTLS authentication and termination in a network appliance at the edge, such as Amazon API Gateway.
In this solution, we show a pattern of using API Gateway with an authorizer implemented with AWS Lambda to validate the mTLS client certificate, extract the client certificate subject, and propagate it to the downstream application in a base64 encoded HTTP header.
While this blog describes how to implement this pattern for identities extracted from the mTLS client certificates, you can generalize it and apply to propagating information obtained via any other means of authentication.
mTLS Sample Application
This blog includes a sample application implemented using the AWS Serverless Application Model (AWS SAM). It creates a demo environment containing resources like API Gateway, a Lambda authorizer, and an Amazon EC2 instance, which simulates the backend application.
The EC2 instance is used for the backend application to mimic common customer scenarios. You can use any other type of compute, such as Lambda functions or containerized applications with Amazon Elastic Container Service (Amazon ECS) or Amazon Elastic Kubernetes Service (Amazon EKS), as a backend application layer as well.
The following diagram shows the solution architecture:
- Store the client certificate in a trust store in an Amazon S3 bucket.
- The client makes a request to the API Gateway endpoint, supplying the client certificate to establish the mTLS session.
- API Gateway retrieves the trust store from the S3 bucket. It validates the client certificate, matches the trusted authorities, and terminates the mTLS connection.
- API Gateway invokes the Lambda authorizer, providing the request context and the client certificate information.
- The Lambda authorizer extracts the client certificate subject. It performs any necessary custom validation, and returns the extracted subject to API Gateway as a part of the authorization context.
- API Gateway injects the subject extracted in the previous step into the integration request HTTP header and sends the request to a downstream endpoint.
- The backend application receives the request, extracts the injected subject, and uses it with custom business logic.
Prerequisites and deployment
Some resources created as part of this sample architecture deployment have associated costs, both when running and idle. This includes resources like Amazon Virtual Private Cloud (Amazon VPC), VPC NAT Gateway, and EC2 instances. We recommend deleting the deployed stack after exploring the solution to avoid unexpected costs. See the Cleaning Up section for details.
Refer to the project code repository for instructions to deploy the solution using AWS SAM. The deployment provisions multiple resources, taking several minutes to complete.
Following the successful deployment, refer to the RestApiEndpoint variable in the Output section to locate the API Gateway endpoint. Note this value for testing later.
Key areas in the sample code
There are two key areas in the sample project code.
In src/authorizer/index.js, the Lambda authorizer code extracts the subject from the client certificate. It returns the value as part of the context object to API Gateway. This allows API Gateway to use this value in the subsequent integration request.
const crypto = require('crypto');
exports.handler = async (event) => {
console.log ('> handler', JSON.stringify(event, null, 4));
const clientCertPem = event.requestContext.identity.clientCert.clientCertPem;
const clientCert = new crypto.X509Certificate(clientCertPem);
const clientCertSub = clientCert.subject.replaceAll('\n', ',');
const response = {
principalId: clientCertSub,
context: { clientCertSub },
policyDocument: {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: 'allow',
Resource: event.methodArn
}]
}
};
console.log('Authorizer Response', JSON.stringify(response, null, 4));
return response;
};
In template.yaml, API Gateway injects the client certificate subject extracted by the Lambda authorizer previously into the integration request as X-Client-Cert-Sub HTTP header. X-Client-Cert-Sub is a custom header name and you can choose any other custom header name instead.
SayHelloGetMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: CUSTOM
AuthorizerId: !Ref CustomAuthorizer
HttpMethod: GET
ResourceId: !Ref SayHelloResource
RestApiId: !Ref RestApi
Integration:
Type: HTTP_PROXY
ConnectionType: VPC_LINK
ConnectionId: !Ref VpcLink
IntegrationHttpMethod: GET
Uri: !Sub 'http://${NetworkLoadBalancer.DNSName}:3000/'
RequestParameters:
'integration.request.header.X-Client-Cert-Sub': 'context.authorizer.clientCertSub'
Testing the example
You create a client key and certificate during the deployment, which are stored in the /certificates directory. Use the curl command to make a request to the REST API endpoint using these files.
curl --cert certificates/client.pem --key certificates/client.key \
<use the RestApiEndpoint found in CloudFormation output>
The client request to API Gateway uses mTLS with a client certificate supplied for mutual TLS authentication. API Gateway uses the Lambda authorizer to extract the certificate subject, and inject it into the Integration request.
The HTTP server runs on the EC2 instance, simulating the backend application. It accepts the incoming request and echoes it back, supplying request headers as part of the response body. The HTTP response received from the backend application contains a simple message and a copy of the request headers sent from API Gateway to the backend.
One header is x-client-cert-sub with the Common Name value you provided to the client certificate during creation. Verify that the value matches the Common Name that you provided when generating the client certificate.
API Gateway validated the mTLS client certificate, used the Lambda authorizer to extract the subject common name from the certificate, and forwarded it to the downstream application.
Cleaning up
Use the sam delete command in the api-gateway-certificate-propagation directory to delete the sample application infrastructure:
sam delete
You can also refer to the project code repository for the clean-up instructions.
Conclusion
This blog shows how to use the API Gateway with a Lambda authorizer for mTLS client certificate validation, custom field extraction, and downstream propagation to backend systems. This pattern allows you to terminate mTLS at the edge so that downstream applications are not responsible for client certificate validation.
For additional documentation, refer to Using API Gateway with Lambda Authorizer. Download the sample code from the project code repository. For more serverless learning resources, visit Serverless Land.