Security & Authentication

Implementing Mutual TLS (mTLS) for API Security

How to implement mutual TLS: certificate authority setup, client certificate generation, server configuration, and debugging common mTLS errors.

What Is Mutual TLS?

In standard TLS, only the server presents a certificate to prove its identity to the client. In Mutual TLS (mTLS), both sides present certificates — the client proves its identity to the server as well.

mTLS is the gold standard for service-to-service authentication in zero-trust architectures. It is used by Kubernetes service meshes (Istio, Linkerd), financial APIs, and anywhere cryptographic client identity assurance is required.

One-Way vs Mutual TLS

One-Way TLSMutual TLS
Server authenticatesYesYes
Client authenticatesNoYes
Client needs certificateNoYes
Use caseHTTPS websitesService-to-service APIs

Certificate Authority Setup

For mTLS, you typically run your own private CA to issue client certificates. Use a CA trusted by your servers but not public CAs:

# Generate a private CA key and self-signed certificate
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key \
  -subj '/CN=My Internal CA/O=Example Corp' \
  -out ca.crt

Client Certificate Generation

Each client service gets its own key pair and certificate signed by the CA:

# 1. Generate client key
openssl genrsa -out client.key 2048

# 2. Create a Certificate Signing Request (CSR)
openssl req -new -key client.key \
  -subj '/CN=payment-service/O=Example Corp' \
  -out client.csr

# 3. Sign with the CA
openssl x509 -req -days 365 -in client.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out client.crt

The CN (Common Name) becomes the client's identity on the server side — use it to implement fine-grained authorization (payment-service may call /payments/* but not /admin/*).

Server Configuration

Nginx:

server {
    listen 443 ssl;
    ssl_certificate     /etc/ssl/server.crt;
    ssl_certificate_key /etc/ssl/server.key;

    # mTLS: require client certificate signed by our CA
    ssl_client_certificate /etc/ssl/ca.crt;
    ssl_verify_client on;           # require valid client cert
    ssl_verify_depth 2;

    # Pass client CN to application
    proxy_set_header X-Client-Cert-CN $ssl_client_s_dn_cn;
}

Testing with curl

# Test without client certificate (should fail with 400)
curl https://api.example.com/secure

# Test with client certificate
curl --cert client.crt --key client.key \
  --cacert ca.crt \
  https://api.example.com/secure

# Inspect server certificate chain
openssl s_client -connect api.example.com:443 \
  -cert client.crt -key client.key -CAfile ca.crt

mTLS in Kubernetes

Service meshes automate mTLS certificate management:

  • Istio: enables mTLS between all pods in a mesh with a PeerAuthentication policy. Certificates are rotated automatically every 24 hours using SPIFFE/SPIRE.
  • Linkerd: strict mTLS by default with automatic certificate rotation from a trust anchor.
# Istio: enforce strict mTLS in a namespace
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: STRICT

Common Errors

ErrorCauseFix
`400 No required SSL certificate was sent`Missing client certPass `--cert` to curl
`SSL certificate verify failed`CA mismatchUse the same CA for client cert signing
`Certificate has expired`Cert past `notAfter`Renew and reissue
`ssl_verify_client` returns 400Wrong CA configuredCheck `ssl_client_certificate` path

Protocolos relacionados

Termos do glossário relacionados

Mais em Security & Authentication