In this post we'll walk through a step-by-step guide to implement mutual TLS (mTLS) configuration on AWS Application Load Balancer(ALB) and verifying the setup using curl
.
mTLS requires both the server and the client to have trusted certificates, issued by trusted CAs. If you are building mTLS on production use, I suggest to take a look at AWS Private CA.
In this blog post, we'll be using self-signed certificates, which are certificates generated and signed locally without the involvement of a trusted Certificate Authority (CA). This allows us to create all necessary certificates directly on your local machine and upload them to AWS services. While self-signed certificates are typically not used in production due to their lack of trust from external entities, they offer a practical way to understand how mTLS works when a client initiates a session with a server.
Let's dive in and get started!
Generate self-signed certificates
Step 1: Generate x.509v3 Configuration Files
When working with mTLS on ALB, it's important to use x.509 Version 3 certificates, as they are required for proper mutual authentication. OpenSSL certificates are by default Version 1 unless you explicitly specify otherwise. To ensure compatibility with ALB, we first need to create specific configuration files, which will be referenced during the certificate creation process.
Below, we'll outline the steps to create these x.509v3 configuration files.
# openssl-ca.cnf
[ req ]
default_bits = 2048
default_md = sha256
default_keyfile = client-private.key
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
[ req_distinguished_name ]
C = FI
ST = State
L = Tampere
O = MyOrg
OU = IT
CN = MyCertificateAuthority
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
# openssl-client.cnf
[ v3_req ]
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
Step 2: Generate x.509v3 Certificates
In this step, we will generate the necessary certificates for both the server (ALB with Lambda function) and the client (your laptop). These certificates will enable secure communication through mutual authentication.
Generate Certicates:
-
Generate the Private Key
The private key is used to sign the certificates.
openssl genrsa -out client-private.key 4096
-
Create the CA Certificate
The Certificate Authority (CA) certificate will be uploaded to AWS Load Balancer's Trust Store to establish trust.
openssl req -new -x509 -days 3650 -key client-private.key -out client-ca-cert.pem -config openssl-ca.cnf
-
Generate Client Certificates
Now, create two client certificates. These will be signed by the CA certificate created in the previous step.- Generate Certificate Signing Request (CSRs) for each client:
openssl req -new -key client-private.key -out client1.csr -subj "/C=FI/ST=State/L=Tampere/O=MyOrg/OU=IT/CN=MyClient001" openssl req -new -key client-private.key -out client2.csr -subj "/C=FI/ST=State/L=Tampere/O=MyOrg/OU=IT/CN=MyClient002"
-
Sign each CSR to create the certificates:
openssl x509 -req -in client1.csr -CA client-ca-cert.pem -CAkey client-private.key -set_serial 01 -out client-public-1.pem -sha256 -extensions v3_req -extfile openssl-client.cnf openssl x509 -req -in client2.csr -CA client-ca-cert.pem -CAkey client-private.key -set_serial 01 -out client-public-2.pem -sha256 -extensions v3_req -extfile openssl-client.cnf
Key Points
At this stage, we have successfully generated three x509.v3 certificates:
- CA Certificate (
client-ca-cert.pem
): This certificate will be uploaded to AWS ALB's Trust Store to establish trust between the ALB and clients. - Client Certificates (
client-public-1.pem
andclient-public-2.pem
): These will be used by the clients (e.g., your laptop) to authenticate with the ALB during the mutual TLS handshake.
Create an Application Load Balancer configured for mTLS
Before starting, ensure that your AWS CLI is properly configured. You'll also need a Cloudformation template to provision the required infrastructure.
You can find a ready-made Cloudformation template and necessary resources here: https://github.com/markymarkus/cloudformation/tree/master/alb_lambda_mtls
Step 1: Upload the CA Certificate to S3
For mutual TLS (mTLS) authentication, ALB requires the CA certificate chain to be stored in an S3 bucket. This S3 bucket, along with the certificate object, will be referenced when the ALB's Trust Store is created.
Run the following command to create a new S3 bucket(replace dev-trust-store-certs with your preferred bucket name). After that is done, CA Certificate is copied to the bucket.
aws s3 mb s3://dev-trust-store-certs --region eu-west-1
aws s3 cp client-ca-cert.pem s3://dev-trust-store-certs
Step 2: Provision the ALB infrastructure
Now that the CA certificate is stored in S3, we can proceed to provision the Application Load Balancer and Lambda function using a CloudFormation template.
Run the following command to deploy the ALB and backed Lambda function infrastructure. Make sure to update parameters.json
with the necessary configuration values(e.g., VPC, certificate and S3 bucket details).
aws cloudformation deploy --stack-name mtls-demo --template-file alb_lambda_mtls.yaml --parameter-overrides file://parameters.json --capabilities CAPABILITY_IAM
Use cURL to Test mTLS
The final step is to verify the mutual TLS (mTLS) handshake using cURL with the newly created ALB. We'll first attempt a standard TLS connection without client certificates, followed by a successful mTLS connection with the necessary certificates.
Step 1: Test mTLS Connection Without Client Certificate
Run the following cURL command to test a simple TLS connection to the ALB without providing a client certificate:
curl https://mtls-server.XXXXXXXX.fi
This command will fail with the error:
curl: (35) Recv failure: Connection reset by peer
This failure occurs because the client did not present a certificate to prove its identity, which is required for mutual TLS authentication.
Step 2: Test mTLS with Client Certificates
Now, let's add the client’s private key and public certificate to authenticate the client and complete the mTLS handshake
curl --key client-private.key --cert client-public-1.pem https://mtls-server.XXXXXXXX.fi
This time, the command should succeed, and you should receive a response similar to the following to indicate that authorized client has invocated it:
Hello: CN=MyClient001,OU=IT,O=MyOrg,L=Tampere,ST=State,C=FI
The response from the Lambda function confirms that the client has successfully authenticated with the ALB, indicating that mTLS is functioning as expected. This response includes details from the client’s certificate, verifying that the ALB has properly verified client's identity.
To further test the setup, try updating the previous curl
command to use the client-public-2.pem
certificate. You'll notice that the response changes accordingly, reflecting the new client certificate being used for authentication.
Conclusion
In this tutorial, we walked through the process of generating x.509v3 certificates, configuring an AWS Application Load Balancer for mutual TLS (mTLS), and successfully testing the setup using cURL. By securing communication between clients and the ALB with mTLS, you ensure that both parties authenticate each other, enhancing the security of your application.