Skip to Content

Client certificate authentication with Traefik

Sometimes authentication with a username and password is not secure enough. A login form might contain vulnerabilities or give away important information, like what software you are running on the given domain (and what version of it). Client certificate authorization is just like using SSH: your client has a private and public key installed that it uses to let the server know it can be trusted (through mutual TLS). It’s not very user friendly, but it IS a great way to secure your web app.

Doing this with Traefik is fairly straightforward:

  1. Generate a certificate authority (CA)
  2. Add the CA certificate to Traefik
  3. Create an IngressRoute that uses the CA certificate
  4. Generate a client certificate
  5. Install the client certificate on your client(s)

1. Generate a certificate authority (CA)

Generate the CA certificate with the openssl command:

openssl genrsa -des3 -out 'ca.key' 4096
openssl req -x509 -new -nodes -key 'ca.key' -sha384 -days 1825 -out 'ca.crt'

Important: store the ca.key in a secure place.

Next, create a Secret in the same namespace where the IngressRoute will be in:

kubectl create secret generic 'client-auth-ca-cert' -n 'my-namespace' --from-file='ca.crt=ca.crt'

Make sure the key is either ca.crt or tls.ca. See the TLSOption documentation for more info.

2. Add the CA certificate to Traefik

Create a TLSOption in the same namespace as the Secret that we have created in the previous step:

apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
metadata:
  name: client-cert # Change this
  namespace: my-namespace # Change this
spec:
  minVersion: VersionTLS12
  maxVersion: VersionTLS13
  clientAuth:
    secretNames:
      - client-auth-ca-cert
    clientAuthType: RequireAndVerifyClientCert
  curvePreferences:
    - CurveP521
    - CurveP384
  cipherSuites:
    - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
    - TLS_AES_256_GCM_SHA384
    - TLS_CHACHA20_POLY1305_SHA256
  sniStrict: true

At this point the CA certificate is ready to use.

3. Create an IngressRoute that uses the CA certificate

Use the following snippet. Make changes where necessary.

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: example-com # Change this
  namespace: my-namespace # Change this
spec:
  entryPoints:
    - websecure # Change this?
  routes:
    - kind: Rule
      match: "Host(`example.com`)" # Change this
      services:
        # You can use this service for testing. It will respond with a '418 I'm a teapot'
        - kind: TraefikService
          name: noop@internal
  tls: # Not merged with static configuration
    # certResolver: letsencrypt # You can add this later
    options:
      # Change these. Must match the metadata from step 2
      name: client-cert
      namespace: my-namespace

Once created, try curl -vk https://example.com --resolve example.com:443:{server IP}. It should respond with a bad certificate error.

4. Generate a client certificate

Now that everything is set up on the server side, we can create a client key and certificate:

export DOMAIN=example.com # Same as Host in IngressRoute's match
export ORGANIZATION=Example
export COUNTRY=NL

openssl genrsa -des3 -out "$DOMAIN.key" 4096
openssl req -new -key "$DOMAIN.key" -out "$DOMAIN.csr" -subj "/CN=$DOMAIN/O=$ORGANIZATION/C=$COUNTRY"

# Use ca.crt and ca.key from step 1
openssl x509 -sha384 -req -CA 'ca.crt' -CAkey 'ca.key' -CAcreateserial -days 365 -in "$DOMAIN.csr" -out "$DOMAIN.crt"
openssl pkcs12 -export -out "$DOMAIN.pfx" -inkey "$DOMAIN.key" -in "$DOMAIN.crt"

Let’s try out the certificate:

curl -vk https://example.com --resolve example.com:443:{server IP} --cert "$DOMAIN.crt" --key "$DOMAIN.key"

The server should now respond with a 418 I'm a teapot.

5. Install the client certificate on your client(s)

Most clients support a way to add a client certificate. The best way to figure out how is to do a quick online search. Here are some examples:

When connecting to your domain for the first time, the client usually shows a popup to select the installed client certificate.

That’s it. You have now established a very secure connection with your server. Just make sure you don’t leak one of your private key files!