Traefik with Docker

Traefik with Docker

My initial configuration for web proxy of my self hosted websites consisted of an installation of NGINX and Certbot locally on the Docker server (not in containers) along with manual file updates or manual certificate requests/updates anytime I wanted to stand up another web container for self hosting. This worked just fine but I wanted to make this process fully automated. Recently, I stumbled onto Traefik when I was researching alternative solutions. Here is a brief description from their website:

Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them.
What sets Traefik apart, besides its many features, is that it automatically discovers the right configuration for your services.

The magic happens when Traefik inspects your infrastructure, where it finds relevant information and discovers which service serves which request.

Traefik is natively compliant with every major cluster technology, such as Kubernetes, Docker, Docker Swarm, AWS, Mesos, Marathon, and the list goes on; and can handle many at the same time. (It even works for legacy software running on bare metal.)

With Traefik, there is no need to maintain and synchronize a separate configuration file: everything happens automatically, in real time (no restarts, no connection interruptions). With Traefik, you spend time developing and deploying new features to your system, not on configuring and maintaining its working state..

The reason why I chose Traefik over other proxy services was due to the fact that, once properly configured, I would only need to add some flags to any docker container I currently had (or new ones to be created) for Traefik to automatically update itself - without exposing said containers externally or updating/restarting files and services. I highly recommend this setup if you have a domain (or multiple) that you want to use with containers on your network, securely. This configuration will be used throughout nearly all of my guides so I wanted to make sure there was a page dedicated to my Traefik configurations for easy reference.

Before starting this installation, you'll probably need to forward ports 80 (http) and 443 (https) to the host running your docker installation to ensure that Traefik receives all web traffic for proper routing. (Refer to your router's vendor documentation for this step)

For this setup, we will be creating a dedicated directory to store all of our docker-compose files as well as creating the files for editing

mkdir -p traefik/data traefik/data/configurations; cd traefik
touch docker-compose.yml
touch data/traefik.yml
touch data/acme.json
touch data/configurations/dynamic.yml
chmod 600 data/acme.json

Then create the "proxy" network for Traefik to communicate externally as well as use within any other docker-compose.yml files to automatically be detected by Traefik

docker network create proxy

Don't forget to alter these files with your email/domain!

docker-compose file

version: '3.7'

services:
  traefik:
    image: "traefik:latest"
    container_name: traefik
    restart: always
    security_opt:
      - no-new-privileges:true
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
      # Add folder with dynamic configuration yml
      - ./data/configurations:/configurations
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.traefik-secure.entrypoints=websecure"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.mycooldomain.net`)"
      - "traefik.http.routers.traefik-secure.middlewares=user-auth@file"
      - "traefik.http.routers.traefik-secure.service=api@internal"

networks:
  proxy:
    external: true

traefik file

log:
  level: DEBUG

api:
  dashboard: true

entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure

  websecure:
    address: :443
    http:
      middlewares:
        - secureHeaders@file
        - nofloc@file
      tls:
        certResolver: letsencrypt

pilot:
  dashboard: false

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: /configurations/dynamic.yml

certificatesResolvers:
  letsencrypt:
    acme:
      email: email@example.com
      storage: acme.json
      keyType: EC384
      httpChallenge:
        entryPoint: web

  buypass:
    acme:
      email: email@example.com
      storage: acme.json
      caServer: https://api.buypass.com/acme/directory
      keyType: EC256
      httpChallenge:
        entryPoint: web

dynamic configuration file

# Dynamic configuration
http:
 middlewares:
   nofloc:
     headers:
       customResponseHeaders:
         Permissions-Policy: "interest-cohort=()"
   
   secureHeaders:
     headers:
       frameDeny: true
       sslRedirect: true
       browserXssFilter: true
       contentTypeNosniff: true
       forceSTSHeader: true
       stsIncludeSubdomains: true
       stsPreload: true
       stsSeconds: 31536000

   bw-stripPrefix:
     stripPrefix:
       prefixes:
         - "/notifications/hub"
       forceSlash: false

   user-auth:
     basicAuth:
       users:
         - "admin:$apr1$oTAsPbab$XasZ6DIlRHf.KTMzbd.Ig1"

tls:
 options:
   default:
     cipherSuites:
       - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
       - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
       - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
       - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
       - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
       - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
     minVersion: VersionTLS12

Now we're ready to start Traefik

docker-compose up -d

You'll need to allow some time for Traefik to start up, as well as generate your domain's SSL certificate. When it's ready, just go to https://traefik.yourdomain and login with the username and password you created. For our example, we'll navigate to https://traefik.mycooldomain.net

traefik-screenshot

Notes

  • This dynamic.yml configuration contains some settings I use to host my own BitWarden container (check out the guide here) so please remove or comment out the below section if you don't plan to use BitWarden
#bw-stripPrefix:
#          stripPrefix:
#            prefixes:
#              - "/notifications/hub"
#            forceSlash: false

  • For the "user-auth" section of the dynamic.yml file you'll want to generate your own hashed login credentials so you don't store your admin portal login in plain text within the file
htpasswd -nb admin <username> <password>

Sample run:

$ htpasswd -nb admin thisisthepassword
admin:$apr1$oTAsPbab$XasZ6DIlRHf.KTMzbd.Ig1

Now that our traefik container is created, we can create containers with traefik flags to automatically have traefik detect and route to dynamically. For a full list of available features, check out traefik's documentation page.