Access Control with Traefik and Client Certificates

January 20, 2022

written by:

Assume you have a personal Wiki which you host yourself as a Docker container. You may want to restrict access to the wiki, so that strangers cannot access it. One, very obvious way, is of course enabling username and password checking in the software itself. Another way would be to limit network access to the wiki. In this article, we will demonstrate how to set up client certificate checking in Traefik, in order to allow access only users with a valid client certificate.

flavor wheel

This article is going to demonstrate how to set up a Traefik provider for client certificate checking. Then, we will show how to enable this provider in a docker-compose file. For sake of simplicty, we are not digging into the details of creating client certificates, however this web-article may help How to Create a Self-signed Client Certificate with OpenSSL.

In this article we are using a recent Traefik Proxy Version 2.5.6.

Prerequisites

We are assuming, Traefik is up and running as a Docker container. If this is not the case and / or you are new to this topic, check out the following blog article to get started: Continuous Deployment Revisited - Making Docker Containers Accessible with Traefik 2.x.

Configuring a Provider

To configure providers in Traefik, we actually need to do a couple of things:

  • Enable providers in the Traefik configuration
  • Create a provider configuration and add the certificate authority certificates (ca-certificates)
  • Update volume mounts for Traefik, so Traefik can read ca-certificates and provider configurations
  • Update the application docker-compose files to use the provider and enable client-certificate checking

Enabling Providers

There are different types of providers, and if you have read our other blog articles about Traefik, you have come accross the Docker provider already. This is the one, which also checks whether a Docker container is started or stopped.

For client certificate checking we will configure file based providers. Enabling them is a rather quick story. Edit your traefik.toml file and add the following lines:

[providers]
  [providers.file]
    directory = "/providers"

If you want Traefik to monitor the directory and update provider configurations automatically upon change, you may add a watch: true to the [providers.file] section (with the same identation as directory). Also, if you want only one file to be used, you may swap directory with filename and providing a filename of your configuration. Please see the file provider documentation for further information.

Have in mind that directory and filename refer to your Docker Container internal file system structure. We have created a directory providers where we will collect all of our provider configurations to keep everything neat and clean.

Creating the Provider Configuration

Now creating the provider is as simple as creating a file. In this case, client.toml.

The name of the provider will be client (see lines 2 and 3, provider Namespaces are explained in the provider namespace documentation). The file name is not relevant!

[tls.options]
  [tls.options.client]
    [tls.options.client.clientAuth]
      caFiles = ["/certs/colamda-ca.crt"]
      clientAuthType = "RequireAndVerifyClientCert"

Please note line 4, where caFiles is assigned a list of files:

  • The files given refer to the Traefik Docker-Containers internal file system structure. Thus, we need to map volume mounts accordingly in our docker-compose.yml file.
  • caFiles is assigned a list of files. If your ca-certificate expires, sometime in the future, you may add another ca-certificate ahead of time to prepare for a transition and then remove the old ca-certificate afterwards.

There are different kinds of checks you may enable in clientAuthType . Please check the client authentication documentation for further details. RequireAndVerifyClientCert does pretty much what is advertised: It requires a client certificate and also checks whether a given client-certificate is valid, which is the strictest setting available.

Updating the Traefik Docker-Compose.yml File

All in all, the below docker-compose file does not vary much from the ones in our other examples. Notice lines 19-20, where we have added volume mounts for the provider and certificate directory. In order to keep the directory structure clean, we have moved the traefik.toml and acme.json files to a separate directory, too (see line 17 and 18).

version: '3'

services:
  traefik:
    image: traefik:v2.5.6
    container_name: traefik
    hostname: traefik
    restart: always
    ports:
      - "80:80"
      - "443:443"
    networks:
      - traefik_proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./conf/traefik/traefik.toml:/traefik.toml
      - ./conf/traefik/acme.json:/acme.json
      - ./conf/providers:/providers
      - ./conf/certs:/certs

networks:
  traefiknet:
    driver: bridge
  traefik_proxy:
    external:
      name: traefik_proxy

Enabling Client-Certificate Checking for an Application

The following docker-compose file is a vastly simplified example. Most of the labels used were discussed in the blog article on Continuous Deployment Revisited - Making Docker Containers Accessible with Traefik 2.x.

The only thing worth mentioning here is line 20: traefik.http.routers.wiki-web-secure.tls.options: "client@file", where we are adding a file provider with the name client as a router. This is it: One line.

version: '3'
services:

  wiki:
    image: <SOME APPLICATION, e.g. WIKI, DOCKER IMAGE>
    labels:
      traefik.enable: "true"
      traefik.docker.network: "traefik_proxy"
      traefik.http.routers.wiki-web.rule: "Host(`<YOUR DOMAIN>`)"
      traefik.http.routers.wiki-web.entrypoints: "web"
      traefik.http.routers.wiki-web.middlewares: "wiki-https"
      traefik.http.middlewares.wiki-https.redirectscheme.scheme: "https"
      traefik.http.routers.wiki-web-secure.rule: "Host(`<YOUR DOMAIN>`)"
      traefik.http.routers.wiki-web-secure.entrypoints: "web-secure"
      traefik.http.routers.wiki-web-secure.tls.certresolver: "letsencrypt"
      traefik.http.routers.wiki-web-secure.middlewares: "wiki-gzip"
      traefik.http.routers.wiki-web-secure.service: "wiki-web"
      traefik.http.middlewares.wiki-gzip.compress: "true"
      traefik.http.services.wiki-web.loadbalancer.server.port: "8080"
      traefik.http.routers.wiki-web-secure.tls.options: "client@file"
    networks:
      - traefik_proxy

networks:
  traefik_proxy:
    external:
      name: traefik_proxy

Okay, now all the pieces are in place! If you try and access your application, access should be denied unless you install a client certificate which matches one of the certificate authority certificates, installed in your Traefik instance. Typically client certificates are installed in your application (e.g. browser) or operating system as a system-wide setting.

Further Reading