Traefik with Let’s Encrypt and Cloudflare (pt 2)

In this article we are gonna get into setting up Traefik to request dynamic certs from Lets Encrypt. I had a few issues getting this up and running and the documentation is a little fuzzy. In my case I decided to go with the DNS challenge route. Really the only reason I went with this option is because I was having issues with the TLS and HTTP challenges. Well as it turns out my issues didn’t have as much to do with my configuration as they did with my router.

Sometime in the past I had set up some special rules on my router to force all clients on my network to send DNS requests through a self hosted DNS server. I did this to keep some of my “smart” devices from misbehaving by blocking there access to the outside world. As it turns out some devices will ignore the DNS servers that you hand out via DHCP and will use their own instead. That is of course unless you force DNS redirection but that is another post for another day.

Let’s revisit our current configuration:

version: '3'

services:
  reverse-proxy:
    # The official v2 Traefik docker image
    image: traefik:v2.11
    # Enables the web UI and tells Traefik to listen to docker
    command:
      - --api.insecure=true
      - --providers.docker=true
      - --providers.file.filename=/config.yml
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      # Set up LetsEncrypt
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
      - --certificatesresolvers.letsencrypt.acme.email=mikeconrad@onmail.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --entryPoints.web.http.redirections.entryPoint.to=websecure
      - --entryPoints.web.http.redirections.entryPoint.scheme=https
      - --entryPoints.web.http.redirections.entrypoint.permanent=true
      - --log=true
      - --log.level=INFO
#      - '--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory'

    environment:
      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
    ports:
      # The HTTP port
      - "80:80"
      - "443:443"
      # The Web UI (enabled by --api.insecure=true)
      - "8080:8080"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
      - ./volumes/traefik/logs:/logs
      - ./traefik/config.yml:/config.yml:ro
    networks:
      - traefik
  ots:
    image: luzifer/ots
    container_name: ots
    restart: always
    environment:
      # Optional, see "Customization" in README
      #CUSTOMIZE: '/etc/ots/customize.yaml'
      # See README for details
      REDIS_URL: redis://redis:6379/0
      # 168h = 1w
      SECRET_EXPIRY: "604800"
      # "mem" or "redis" (See README)
      STORAGE_TYPE: redis
    depends_on:
      - redis
    labels:
      - traefik.enable=true
      - traefik.http.routers.ots.rule=Host(`ots.hackanooga.com`)
      - traefik.http.routers.ots.entrypoints=websecure
      - traefik.http.routers.ots.tls=true
      - traefik.http.routers.ots.tls.certresolver=letsencrypt
    networks:
      - traefik
  redis:
    image: redis:alpine
    restart: always
    volumes:
      - ./redis-data:/data
    networks:
      - traefik
networks:
  traefik:
    external: true

Now that we have all of this in place there are a couple more things we need to do on the Cloudflare side:

Step 1: Setup wildcard DNS entry

This is pretty straightforward. Follow the Cloudflare documentation if you aren’t familiar with setting this up.

Step 2: Create API Token

This is where the Traefik documentation is a little lacking. I had some issues getting this set up initially but ultimately found this documentation which pointed me in the right direction. In your Cloudflare account you will need to create an API token. Navigate to the dashboard, go to your profile -> API Tokens and create new token. It should have the following permissions:

Zone.Zone.Read
Zone.DNS.Edit

Also be sure to give it permission to access all zones in your account. Now simply provide that token when starting up the stack and you should be good to go:

CF_DNS_API_TOKEN=[redacted] docker compose up -d