🛡️ Traefik 2, custom TLS configuration
This post teach you how to configure Traefik with a custom TLS configuration.
This post tends to give you some custom configuration files to work with Traefik and TLS.
Version | Date | Comment |
---|---|---|
1 | 05/2022 | Post creation |
2 | 08/2023 | Change paths, add environment and context, update versions docker-compose file, remove "compress" middleware, add headers in the post to show parts |
Goal : Configure Traefik with custom TLS settings
Environment : Debian 12
, Docker 24.x
, docker compose (plugin) 2.20.x
, Traefik 2.10
.
Execution context :
jho@vmi866042:/opt/docker/dc$ tree
.
├── conf
│  ├── acme.json
│  ├── traefik.yml
│  ├── traefikdynamic
│  │  ├── general.yml
│  │  ├── routersservices.yml
├── docker-compose.yml
└── logs
├── traefikAccess.log
├── traefik.log
- path where are every folder and files :
/opt/docker/dc
- path of the principal configuration file for Traefik :
/opt/docker/dc/conf/traefik.yml
- folder where are every dynamic configuration files :
/opt/docker/dc/conf/traefikdynamic
- path of the file which is used to store SSL certificates for let's encrypt (or other provider) :
/opt/docker/dc/conf/acme.json
- folder to store logs :
/opt/docker/dc/logs/
docker-compose configuration file
Forward, you can't use the two types of configuration (static and dynamic) at the same time, it is only once. I prefer dynamic configuration, here's a quick reminder.
Let's prepare the docker-compose.yml
file:
---
services:
traefik:
image: traefik:saintmarcelin
container_name: traefik
restart: unless-stopped
ports:
- target : 80
published : 80
protocol: tcp
mode : host
### BEGIN dashboad
- target : 8080
published : 8080
protocol: tcp
mode : host
### END dashboard
- target : 443
published : 443
protocol: tcp
mode : host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./conf/traefikdynamic:/dynamic
- ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
- ./conf/acme.json:/etc/traefik/acme.json
- ./logs/traefik.log:/etc/traefik/applog.log
environment:
TZ: Europe/Paris
Traefik configuration
Every configuration files here are stored in the “conf” folder. You can change the path to where you want, but remember to update your docker-compose file for it.
Content of the file traefik.yml
:
---
global:
sendAnonymousUsage: false
checkNewVersion: false
api:
dashboard: true
log:
filePath: "/etc/traefik/applog.log"
format: json
level: "ERROR"
providers:
docker:
endpoint: unix:///var/run/docker.sock
exposedByDefault: false
watch: true
swarmMode: false
file:
directory: "/dynamic"
watch: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
# caServer: https://acme-v02.api.letsencrypt.org/directory
storage: "/etc/traefik/acme.json"
keyType: EC256
httpChallenge:
entryPoint: web
In this file, one important bloc is “provider” with the definition of a “dynamic” folder path. This folder is created locally to have the file “traefik_dynamic.yml”.
The line watch: true
allows Traefik to watch changes in the configuration files and applies them when an update is done.
Certificates are generated with Let's Encrypt ; indeed, Traefik uses its internal tool “lego”. When you are in build mode, you should use “staging” server, which have a less aggressive limiter than the “acme-v02”. In production environment, use the “acme-v02” server from Let's Encrypt.
Dynamic configuration file is the same type as the main configuration file (YAML), difference is the settings. Here's my dynamic configuration file “traefik_dynamic.yml”, in the folder “conf/traefikdynamic” :
---
tls:
options:
default:
minVersion: VersionTLS12
sniStrict: true
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
alpnProtocols:
- h2
- http/1.1
mintls13:
minVersion: VersionTLS13
http:
middlewares:
security:
headers:
accessControlAllowMethods:
- GET
- OPTIONS
- PUT
accessControlMaxAge: 100
addVaryHeader: true
browserXssFilter: true
contentTypeNosniff: true
customFrameOptionsValue: SAMEORIGIN
customResponseHeaders:
Access-Control-Allow-Origin: "*"
Sec-Fetch-Site: cross-site
X-Forwarded-Proto: https
forceSTSHeader: true
frameDeny: true
hostsProxyHeaders:
- "X-Forwarded-Host"
referrerPolicy: "strict-origin-when-cross-origin"
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
Some explanations :
- TLS block specify every needed options to have an optimal configuration. Minimal version of TLS is 1.2 with newer algorithms used by modern navigators ;
- HTTP bloc have 2 “under-blocs” :
- a middleware to compress some flux aside streaming
- a middleware for every security about HTTPS (headers)
Docker labels
Configuration files are ready, you now have to set up your services in the docker-compose.yml file. Here's what you need for every services behind Traefik :
services:
portainer:
...
labels:
traefik.enable: "true"
traefik.http.routers.service-https.entrypoints: "websecure"
traefik.http.routers.service-https.middlewares: "security@file"
traefik.http.routers.service-https.tls: "true"
traefik.http.routers.service-https.tls.certresolver: "letsencrypt"
Middlewares security@file
and compression@file
are drawn in the file traefik_dynamic.yml
.
When you set the docker labels, don't forget to re-up your containers (docker-compose up -d
).
Don't want to use docker labels?
If you don't use docker labels, you can add these blocs in the “traefikdynamic.yml” – information about “routers” and “services” next to the TLS configuration, in the bloc http
: (for example, I'm using Portainer)
services:
sc-portainer:
loadBalancer:
servers:
- url: "http://portainer:9000"
routers:
rt-portainer:
entryPoints:
- websecure
middlewares:
- security
service: sc-portainer
rule: Host(`portainer.domain.local`)
tls:
certResolver: letsencrypt
The "services" block defines the containers to be accessed within your infrastructure, along with the communication port. Each service must be defined to be operated through Traefik, as shown in the example above.
The "routers" block defines the route and middlewares for the services. In this case, I want the "sc-portainer" service to use both middlewares, making it accessible via "portainer.domain.local" (line rule:), and reachable via HTTPS from the outside (certificate generated using the letsencrypt service defined in the traefik.yml file).
This configuration is more convenient as it doesn't require changing your service's docker-compose.yml file. Traefik applies the changes on-the-fly from the dynamic configuration files.