- Newest
- Most votes
- Most comments
This article demonstrates how to set up a centralised mTLS truststore in a single Amazon Simple Storage Service (Amazon S3) bucket that API Gateway custom domains can reference across multiple accounts and regions.
Resolution
Prerequisites
Before you begin, verify that you have the following:
- AWS accounts where you want to deploy API Gateway custom domains with mTLS
- Permissions to create S3 buckets and configure bucket policies in the central account
- Permissions to create API Gateway custom domains in the target accounts
- AWS Command Line Interface (AWS CLI) installed and configured
Step 1: Create the Root CA and client certificates
First, create your Root Certificate Authority (CA) and client certificates. The Root CA certificate forms the basis of your truststore.
To create the certificates, follow the instructions in Introducing mutual TLS authentication for Amazon API Gateway.
After you complete the certificate creation process, you have the following files:
- RootCA.pem: Your Root CA certificate (this becomes your truststore)
- client.pem: Client certificate
- client.key: Client private key
Step 2: Create a central S3 bucket and upload the truststore
Create an S3 bucket in your central account (Account A) to store the truststore. The bucket can be in any AWS Region because API Gateway can reference truststores cross-region.
- Open the Amazon S3 console.
- Create a new bucket with a unique name, such as my-central-truststore-bucket.
- Upload your truststore file to the bucket using the AWS CLI:
aws s3 cp RootCA.pem s3://my-central-truststore-bucket/truststore.pem
Note: Organize multiple truststores in the same bucket using different paths or filenames.
Step 3: Configure S3 bucket policy for cross-account access
Configure the S3 bucket policy in Account A to allow IAM principals from other accounts to access the truststore. API Gateway accesses the truststore using the credentials of the IAM principal that creates or updates the custom domain.
- Open the Amazon S3 console.
- Navigate to your central truststore bucket.
- Choose the Permissions tab.
- Choose Bucket Policy and add the following policy:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "CrossAccountTruststoreAccess", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::111122223333:role/APIGatewayDeploymentRole", "arn:aws:iam::444455556666:role/APIGatewayDeploymentRole" ] }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-central-truststore-bucket/truststore.pem" } ] }
Replace the following values:
- 111122223333 and 444455556666: The AWS account IDs where you want to deploy API Gateway custom domains
- APIGatewayDeploymentRole: The IAM role name that creates or updates the custom domains
- my-central-truststore-bucket: Your actual bucket name
Important: Grant access only to the specific IAM roles that need to create or update custom domains. This follows the principle of least privilege. the Role also need to have s3:GetObject permissions to the key s3://my-central-truststore-bucket/truststore.pem
Step 4: Create API Gateway custom domain with mTLS
Create an API Gateway custom domain in Account B (or any other target account) that references the centralised truststore.
Using the AWS Management Console:
- Open the API Gateway console.
- In the navigation pane, choose Custom domain names.
- Choose Create.
- Enter your domain name (for example, api.example.com).
- Configure your TLS certificate settings.
- Under Mutual TLS authentication, turn on Mutual TLS authentication.
- For Truststore URI, enter the S3 URI of your centralised truststore:
s3://my-central-truststore-bucket/truststore.pem - Choose Create domain name.
Using the AWS CLI:
aws apigateway create-domain-name \ --domain-name api.example.com \ --regional-certificate-arn arn:aws:acm:us-east-1:111122223333:certificate/abc123 \ --endpoint-configuration types=REGIONAL \ --security-policy TLS_1_2 \ --mutual-tls-authentication truststoreUri=s3://my-central-truststore-bucket/truststore.pem
For detailed instructions on creating custom domains with mTLS, see Setting up custom domain names for REST APIs.
Step 5: Test the mTLS connection
After you create the custom domain and configure the base path mapping to your API, test the mTLS connection using a client certificate.
Run the following command to test the connection:
curl --key client.key --cert client.pem https://api.example.com/your-api-path
Replace the following values:
- client.key: Path to your client private key
- client.pem: Path to your client certificate
- api.example.com: Your custom domain name
- /your-api-path: The path to your API resource
If the mTLS configuration is correct, the request succeeds and returns the expected response from your API. If the client certificate is invalid or not signed by the trusted CA, API Gateway returns a 403 Forbidden error.
Step 6: Implement certificate rotation strategy
To rotate certificates without downtime, follow this strategy:
-
Generate a new Root CA certificate using the same process from Step 1.
-
Create the truststore with the new CA certificate:
cp RootCA-new.pem truststore.pem
- Upload the combined truststore to S3:
aws s3 cp truststore.pem s3://my-central-truststore-bucket/truststore.pem
- Trigger a custom domain update in each account to re-fetch the truststore. API Gateway caches the truststore, so you must explicitly update the custom domain:
aws apigateway update-domain-name \ --domain-name api.example.com \ --patch-operations op=replace,path=/mutualTlsAuthentication/truststoreUri,value=s3://my-central-truststore-bucket/truststore.pem
Note: Use the same S3 URI in the update command. The update operation forces API Gateway to re-fetch and cache the new truststore content.
-
Issue new client certificates signed by the new CA and distribute them to your clients.
-
After all clients migrate to the new certificates, remove the old CA from the truststore:
cp RootCA-new.pem truststore.pem aws s3 cp truststore.pem s3://my-central-truststore-bucket/truststore.pem
- Update the custom domains again to fetch the final truststore with only the new CA.
Additional considerations
Truststore caching: API Gateway caches the truststore content. When you update the truststore in S3, you must update each custom domain to force API Gateway to re-fetch the new content.
S3 versioning: Turn on S3 versioning for your truststore bucket to maintain a history of truststore changes. This allows you to roll back to a previous version if needed.
Monitoring: Set up Amazon CloudWatch alarms to monitor API Gateway 4XX errors, which can show mTLS authentication failures. For more information, see Monitoring REST API execution with Amazon CloudWatch metrics.
Regional considerations: Although the S3 bucket can be in any region, consider placing the bucket in a region close to your primary API Gateway deployments to minimize latency during truststore fetches.
Related information
answered 8 months ago
Yes, it is possible to configure an API Gateway REST API for mutual TLS (mTLS) authentication using a certificate truststore S3 object stored in a different AWS account. Here's how you can achieve this:
-
Create an S3 Bucket Policy: In the account where the truststore S3 object is stored, create an S3 bucket policy that grants the necessary permissions to the IAM role or user from the account where your API Gateway is deployed. The policy should allow the s3:GetObject action on the truststore object.
-
Create an IAM Role: In the account where your API Gateway is deployed, create an IAM role with a trust policy that allows API Gateway to assume the role. Attach a policy to this role that grants the s3:GetObject permission on the truststore object in the other account.
-
Configure API Gateway Domain Name: In the API Gateway console, navigate to the "Custom Domain Names" section and create a new domain name or use an existing one. During the domain name configuration, you'll need to specify the following:
- Domain Name Certificate: Upload or select an ACM (AWS Certificate Manager) certificate for your domain.
- Security Policy: Choose a security policy that supports mTLS, such as TLS_1_2 or TLS_1_2_2021.
- Mutual TLS Authentication: Enable this option.
- Truststore URI: Specify the S3 URI of the truststore object in the other account, using the format s3://<bucket-name>/<object-key>.
- Truststore Version: Specify the version ID of the truststore object, if applicable.
- Truststore AWS ARN Role: Select the IAM role you created in step 2, which has permissions to access the truststore object in the other account.
-
Configure API Gateway Stage: In the API Gateway console, navigate to the "Stages" section and create a new stage or update an existing one. Associate the stage with the domain name you configured for mTLS.
-
Configure Client Certificates: Clients connecting to your API Gateway REST API will need to present a client certificate that is trusted by the truststore you configured. You'll need to distribute these client certificates to your API consumers.
Note that you'll need to ensure that the IAM role and S3 bucket policies are configured correctly to grant the necessary permissions for API Gateway to access the truststore object in the other account.
answered 2 years ago
Hi Mr_Mike_G, The option "Truststore AWS ARN Role" doesn't exist when creating nor editing a custom domain name. Is that a new feature that will be available in the console soon? Or is this option perhaps only available in certain regions?
Relevant content
- AWS OFFICIALUpdated 10 months ago

Could you please confirm if the API Gateway and domain name in the same region as the S3 bucket containing the truststore certificate?
Hi Malini, Yes: the API Gateway REST API, API Gateway domain name, and S3 bucket are all in the us-east-2 region
to troubleshoot also check S3 Logs to see API Gateway is attempting to even access or access denied then issue is permission-related. As you mentioned, the bucket policy allow API Gateway cross-account access. If you haven’t done so already, also check that the URI format in API Gateway matches the S3 format exactly (s3://<bucket-name>/<path-to-file>.pem). I have done the similar with Lambda integration I faced the mostly permission issue. needed on both ends.