Secure your API Gateway APIs mutual TLS

jimmydqv

Jimmy Dahlqvist

Posted on January 17, 2022

Secure your API Gateway APIs mutual TLS

This is my second post, in the series, on the topic around authentication and authorization for Amazon API Gateway. In my last post I discussed how to use Auth0 and JWT Authorizer with API Gateway.
In this post we will take a closer look at using Mutual TLS together with API Gateway.
The reason to use Mutual TLS is often for service-2-service integration scenarios where one service run on-premesis or in an other Public Cloud Vendor making IAM Authorization not an option.

I will use the console and CLI to do the entire setup. As normal everything exists as CloudFormation and is available on GitHub

Create certificates

First of all we are going to create the client certificate and the private certificate authority (CA) that we need. I will be using OpenSSL on Mac to create this.

The first step is to create the private CA public and private keys, that we then use to create the client certificates. To do this run the following commands and supply the requested input.


 bash

openssl genrsa -out PrivateCA.key 4096
openssl req -new -x509 -days 3650 -key PrivateCA.key -out PrivateCA.pem



Enter fullscreen mode Exit fullscreen mode

Next step is to create the client private key and a certificate signing request (CSR), run the following commands and supply the requested input.


 bash

openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr



Enter fullscreen mode Exit fullscreen mode

Last step is to sign the client certificate with our private CA. Run the following command to do that.


 bash

openssl x509 -req -in client.csr -CA PrivateCA.pem -CAkey PrivateCA.key -set_serial 01 -out client.pem -days 3650 -sha256



Enter fullscreen mode Exit fullscreen mode

After this you should have the following files created:


 bash

PrivateCA.key
PrivateCA.pem
client.key
client.csr
client.pem



Enter fullscreen mode Exit fullscreen mode

Now we need to upload the PrivateCA.pem file to an Amazon S3 Bucket, so we can configure it as TrustStore for API Gateway. To make things clearer let's copy PrivateCA.pem to truststore.pem.


 bash

cp PrivateCA.pem truststore.pem



Enter fullscreen mode Exit fullscreen mode

Then let's create the bucket and upload the file.


 bash

aws s3 mb s3://<bucket_name> --region <region>
aws s3 cp truststore.pem s3://<bucket_name>/truststore.pem



Enter fullscreen mode Exit fullscreen mode

Create API Gateway & Lambda

With the truststore in place in S3 we can start to create the API. We start by creating a simple Lambda function to use as integration.

Create the Lambda function

Jump into the Lambda part of the console and start authoring a function from scratch. Name the function api-hello-world, set the runtime to python 3.8, leave rest as default anc click Create Function

image

In the next step update the code and hit Deploy


 python

import json

def lambda_handler(event, context):

    print("Hello world from Lambda!")

    return {
        'statusCode': 200,
        'body': json.dumps('Hello World!')
    }



Enter fullscreen mode Exit fullscreen mode

Create API Gateway

Time to start setting up API Gateway. Navigate to API gateway part of the console and click Create API.
In the selection screen click Build for the HTTP API.
image

We create one integration for the Lambda function and name the API, I will call mine mtls-hello-world and click Next.
image

Now we need to configure the route. Set the method to GET and add a resource path, point the route to the corresponding integration.
image

Move on to the next part of the configuration, to setup the stages. You can leave this with default settings with a $default stage with auto-deploy on.
image

Final step is to review and create the API. Your configuration should look something like this. Click Create to create the API.
image

Now let's test it all out before moving to the next part of the configuration. Select you newly created API and find the Invoke URL. Copy and paste the URL into a browser and don't forget to add the resources path, e.g. /api-hello-world if everything is working you should now see result in the browser window.

Create Custom Domain

Mutal TLS requires a custom domain name. First of all we must create a certificate in ACM to use. Navigate to ACM and create a certificate for your domain.
image

Next navigate to the Custom Domain Name section in API Gateway and create a domain name that match the certificate that we just created, make sure to select the certificate in the dropdown menu as well.
image

Select the API Mappings Tab and configure the API mappings, select API and stage.
image

Make sure that you configure your DNS to point to the API Gateway Domain name, I use Route53 and add a new A record.

Now let's test that we can call our API using our custom domain name. Paste the URL, including route in a browser e.g https:///api-hello-world or run CURL command:


 bash

curl https://<your domain>/api-hello-world



Enter fullscreen mode Exit fullscreen mode

You should get a Hello World reply back from API Gateway.

Configure Mutual TLS

Now it's time to configure our Mutual TLS authentication. This is done on our custom domain name. Select the custom domain name and click edit on domain details.
Enable Mutual TLS authentication and fill in the S3 path to the truststore and click Save.

image

Since Mutal TLS is configured on our custom domain name we must ensure that a user can't circumvent the authorization by calling the default API endpoint. To do that we should disable it, this will require all calls to come in via the custom domain name. Navigate to the API and Edit.
image

Select Disable on the default endpoint and click Save.

Trying to access the API via the default endpoint should now give a message "Not Found"


bash

curl https://<default endpoint>/api-hello-world

Enter fullscreen mode Exit fullscreen mode




Final wrap up

Now we have an API setup on a custom domain name with Mutual TLS for authentication. A setup like this is really powerful when doing service-2-service or machine-2-machine integrations where it's not possible to use IAM. As an example a service running on-premises where you don't want to bundle long lived IAM access tokens, or in the case that it's a legacy service that already use Mutual TLS today.

Gotchas

What is important to remember is that Mutal TLS is configured on the custom domain name and not on the API it self. Therefor the default endpoint for the API in API Gateway must be disabled to force the calls to come in via the custom domain name so it's not possible to circumvent the authentication.

Disabling the default endpoint is a good practice if you use custom domain name even if you don't use Mutual TLS.

Series

This was the second post in the series on how to setup different Authentication and Authorization on API gateway. Don't miss my previous post about Auth0 and built in JWT Authorizer.

💖 💪 🙅 🚩
jimmydqv
Jimmy Dahlqvist

Posted on January 17, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related