Tanker ship in a blue green ocean

Wildcard certificates make it easy to secure lots of subdomains under a single domain. For example, you can secure web.example.com and mail.example.com with a single certificate for *.example.com. Fortunately, LetsEncrypt allows you to get wildcard certificates via a DNS ownership check (often called a DNS-01 challenge).

Fortunately, Traefik can request a certificate from LetsEncrypt automatically and complete the challenge for you. It can publish DNS records to multiple providers, but my favorite is Cloudflare. They will host your DNS zones and records for free. They also have a robust API for managing DNS records (also free).

In this post, we will cover the basics of getting TLS working with Traefik. We can add a wildcard certificate on top and then re-use that same certificate for other containers running behind Traefik.

Basic setup

First, we need a running instance of Traefik. The Traefik documentation explains this entire process in detail and I highly recommend reading the basics on configuration discovery, routers, and TLS settings.

We will use docker-compose to make this easier to manage. If you’re on Fedora, install docker-compose:

dnf install docker-compose

Now we need a docker-compose.yml file:

---
version: "3"
services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    command:
      # Tell Traefik to discover containers using the Docker API
      - --providers.docker=true
      # Enable the Trafik dashboard
      - --api.dashboard=true
      # Set up LetsEncrypt
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
      - --certificatesresolvers.letsencrypt.acme.email=EMAIL_ADDRESS
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      # Set up an insecure listener that redirects all traffic to TLS
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      # Set up the TLS configuration for our websecure listener
      - --entrypoints.websecure.http.tls=true
      - --entrypoints.websecure.http.tls.certResolver=letsencrypt
      - --entrypoints.websecure.http.tls.domains[0].main=home.example.com
      - --entrypoints.websecure.http.tls.domains[0].sans=*.home.example.com
    environment:
      - CLOUDFLARE_EMAIL=CLOUDFLARE_ACCOUNT_EMAIL_ADDRESS
      - CLOUDFLARE_DNS_API_TOKEN=CLOUDFLARE_TOKEN_GOES_HERE
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - certs:/letsencrypt
    labels:
      - "traefik.enable=true"
      - 'traefik.http.routers.traefik.rule=Host(`home.example.com`)'
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.service=api@internal"
      - 'traefik.http.routers.traefik.middlewares=strip'
      - 'traefik.http.middlewares.strip.stripprefix.prefixes=/traefik'

In this example, we tell Traefik about our desired setup in the command section, including our listeners. Our insecure listener on port 80 redirects to secure connections on port 443 and we tell Traefik that we plan to use LetsEncrypt to get the certificates.

We provide the username and Cloudflare API key in the environment section. Follow Cloudflare’s guides for managing API tokens and keys carefully to generate a token.

The labels section sets up a rule where traffic destined for home.example.com goes to the Traefik dashboard. this is helpful in case you make mistakes or you can’t figure out why something is working. You can go to the dashboard to show all of the existing services, listeners, and other configurations.

☝🏻 Before applying this docker-compose file, change a few things:

  • Set your LetsEncrypt email address in the line with --certificatesresolvers.letsencrypt.acme.email
  • Set your Cloudflare account email address for the CLOUDFLARE_EMAIL environment variable
  • Set your Cloudflare DNS API token for the CLOUDFLARE_DNS_API_TOKEN environment variable
  • Change the Host() rules from example.com to match your domain name

Run docker-compose up -d and then docker-compose logs -f traefik to see if Traefik came up successfully with certificates. If you run into any problems, double check that your Cloudflare email and token are accurate. Also verify that your Cloudflare token has the correct permissions to adjust the dns zone.

Adding a container

At this point, we can add another container and it can use the same TLS certificate we requested from LetsEncrypt already!

The librespeed project provides a self-hosted network speed test that you can run on any network. It also runs perfectly inside a container. The linuxserver.io librespeed container is well maintained and easy to run.

Add this to your docker-compose.yml right under the Traefik configuration:

  librespeed:
    image: ghcr.io/linuxserver/librespeed
    container_name: librespeed
    restart: unless-stopped
    ports:
      - 80
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.librespeed.rule=Host(`librespeed.home.example.com`)"
      - "traefik.http.routers.librespeed.entrypoints=websecure"
      - "traefik.http.routers.librespeed.tls.certresolver=letsencrypt"

Check the labels section. We first enable Traefik so it will route requests to the container. Then we set a host rule so that traffic for librespeed.home.example.com comes to this container. We only listen for TLS traffic (remember our redirect for insecure traffic earlier).

Finally, we tell Traefik to use the same certresolver as before. Traefik is smart enough to know that *.home.example.com covers the librespeed.home.example.com subdomain just fine.

Run docker-compose up -d once more and now librespeed has a secure connection using the original wildcard certificate.

Renewals

LetsEncrypt certificates are valid for only 90 days. That’s why automation plays such an important role in handling renewals. You certainly don’t want to set calendar reminders to log into your server and run a script every 90 days. 😱

Traefik automatically knows when the expiration date approaches. When the certificate has less than 30 days left until the expiration date, Traefik automatically renews the certificate.

💣 Be careful with your DNS zone and with your DNS API keys! If you accidentally delete the API key or make big changes to your DNS zone, there’s a chance that Traefik may not be able to renew the certificate.

Photo credit: Veron Wessels on Unsplash