Mutual TLS (mTLS) is often thought of as a way to secure communication between clients and servers, but its real magic in Traefik lies in its ability to grant fine-grained access based on who the client claims to be.
Let’s see Traefik in action with mTLS. Imagine we have a simple web service running on port 8080, and we want to protect it.
# docker-compose.yml
version: '3.7'
services:
whoami:
image: traefik/whoami
ports:
- "8080:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
# Enable mTLS
- "traefik.http.routers.whoami.tls.options.name=mtls"
- "traefik.http.routers.whoami.tls.options.clientca=ca"
traefik:
image: traefik:v2.10
command:
- --api.insecure=true
- --providers.docker=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Define the CA certificate for client authentication
- --certificatesresolvers.myresolver.acme.tlschallenge=true
- --certificatesresolvers.myresolver.acme.email=your-email@example.com
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
- --providers.certificatesresolvers.myresolver.acme.tlschallenge=true
- --tls.stores.default.defaultCertificate.certFile=/ssl/traefik.crt
- --tls.stores.default.defaultCertificate.keyFile=/ssl/traefik.key
- --providers.file.directory=/ssl/cas
- --providers.file.watch=true
ports:
- "80:80"
- "443:443"
- "8080:8080" # Expose whoami directly for testing if needed
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- ./ssl/traefik.crt:/ssl/traefik.crt
- ./ssl/traefik.key:/ssl/traefik.key
- ./ssl/cas:/ssl/cas
First, we need a Certificate Authority (CA) that will sign our client certificates. Let’s create one:
# Generate CA private key
openssl genrsa -out ca.key 2048
# Generate CA certificate
openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=MyTestCA"
Now, we need to create a client certificate signed by this CA.
# Generate client private key
openssl genrsa -out client.key 2048
# Generate client CSR
openssl req -new -key client.key -out client.csr -subj "/CN=testclient"
# Sign client CSR with CA
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256
We need to tell Traefik about our CA. Create a directory ssl/cas and place ca.crt inside it. Traefik will automatically pick it up.
mkdir -p ssl/cas
cp ca.crt ssl/cas/ca.crt
The key part is configuring Traefik to use this CA for client certificate verification. We define a tlsOptions object, name it mtls, and point clientca to the CA certificate file. Then, we associate this tlsOptions with our router.
# In Traefik's static configuration (or via command line args as shown above)
# This is conceptually what happens, Traefik picks up the file.
tls:
stores:
default: {}
certificates:
- certFile: /ssl/traefik.crt # Your server certificate
keyFile: /ssl/traefik.key
options:
mtls:
clientCA: /ssl/cas/ca.crt
And in the router’s labels:
- "traefik.http.routers.whoami.tls.options.name=mtls"
- "traefik.http.routers.whoami.tls.options.clientca=ca" # 'ca' refers to the CA certificate file name in the configured directory.
Now, when a client tries to access https://whoami.localhost, Traefik will not only present its own server certificate but also request a client certificate. If the client presents a certificate signed by ca.crt, Traefik will verify it. If valid, the request proceeds to the whoami service. If not, Traefik will return a 400 Bad Request or 401 Unauthorized (depending on exact configuration and client behavior).
To test this, you can use curl:
# This will fail (no client cert)
curl -v https://whoami.localhost
# This should succeed
curl -v --cert client.crt --key client.key https://whoami.localhost
The most surprising aspect of Traefik’s mTLS is how it seamlessly integrates certificate management with routing rules, allowing you to define access policies that are cryptographically enforced at the edge. It’s not just about encryption; it’s about identity.
When Traefik receives a TLS handshake, it performs several steps. First, it presents its server certificate. Then, it requests a client certificate. If the client provides one, Traefik checks if the certificate’s issuer is trusted (i.e., present in its clientCA configuration) and if the certificate is still valid. If these checks pass, Traefik extracts information like the Common Name (CN) from the client certificate and can use it in subsequent middleware or logging.
The next step you’ll likely encounter is using the validated client certificate information, such as the CN, to authorize access using Traefik’s ForwardAuth middleware or by dynamically adjusting routing rules.