Traefik Proxy 2.x and TLS 101
The challenge that I'll explore today is that you have an HTTP service exposed through Traefik Proxy and you want to deal with the HTTPS burden (TLS termination), leaving your pristine service unspoiled by mundane technical details.
The goal for today
docker-compose
file. It enables the Docker provider and launches a my-app
application that allows me to test any request.version: '3.9'
services:
traefik:
image: traefik:v2.6
command:
- --entrypoints.web.address=:80
- --providers.docker=true
ports:
- '80:80'
volumes:
- '/var/run/docker.sock:/var/run/docker.sock:ro'
my-app:
image: traefik/whoami:v1.7.1
Getting things ready
:443
). Let's also be certain Traefik Proxy listens to this port thanks to an entrypoint I'll name web-secure
.version: '3.9'
services:
traefik:
image: traefik:v2.6
command:
- --entrypoints.web.address=:80
- --entrypoints.web-secure.address=:443 # Declares the web-secure entrypoint in Traefik
- --providers.docker=true
ports:
- '80:80'
- '443:443' # Docker sends requests on port 443 to Traefik on port 443
volumes:
- '/var/run/docker.sock:/var/run/docker.sock:ro'
my-app:
image: traefik/whoami:v1.7.1
General concepts
my-app
service on HTTP so that it handles requests on the domain example.com
.version: '3.9'
services:
# ...
my-app:
image: traefik/whoami:v1.7.1
labels:
- 'traefik.http.routers.my-app.rule=Host(`example.com`)'
version: '3.9'
services:
# ...
my-app:
image: traefik/whoami:v1.7.1
labels:
- 'traefik.http.routers.my-app.rule=Host(`example.com`)'
- 'traefik.http.routers.my-app.tls=true'
tls
option to the route, you've made the route HTTPS. The TLS configuration could be done at the entrypoint level to make sure all routers tied to this entrypoint are using HTTPS by default. See the Traefik Proxy documentation to learn more.Option 1 - certificates you own
Add a configuration file for certificates
version: '3.9'
services:
traefik:
image: traefik:v2.6
command:
- --entrypoints.web.address=:80
- --entrypoints.web-secure.address=:443
- --providers.docker=true
- --providers.file.directory=/configuration/
- --providers.file.watch=true
ports:
- '80:80'
- '443:443'
volumes:
- '/var/run/docker.sock:/var/run/docker.sock:ro'
- '/home/username/traefik/configuration/:/configuration/'
--providers.file
) to the configuration/
directory and I automatically reload changes with --providers.file.watch=true
. I'm using a configuration file to declare our certificates.Add the certificates to the configuration file
# Dynamic configuration
# in configuration/certificates.yaml
tls:
certificates:
# first certificate
- certFile: /path/to/example-com.cert
keyFile: /path/to/example-com.key
# second certificate
- certFile: /path/to/other.cert
keyFile: /path/to/other.key
# and so on
tls.certificates
section.Specifying a default certificate
# Dynamic configuration
tls:
stores:
default:
defaultCertificate:
certFile: path/to/cert.cert
keyFile: path/to/cert.key
Option 2 — dynamic / automatic certificates
Certificate resolvers
--traefik.http.routers.router-name.tls=true
. As a result, Traefik Proxy goes through your certificate list to find a suitable match for the domain at hand — if not, it uses a default certificate.version: '3.9'
services:
traefik:
image: traefik:v2.6
command:
- --entrypoints.websecure.address=:443
# ...
- --certificatesresolvers.le.acme.email=my@email.com
- --certificatesresolvers.le.acme.storage=/acme.json
- --certificatesresolvers.le.acme.tlschallenge=true
# ...
le
of type acme
. Then, I provided an email (your Let's Encrypt account), the storage file (for certificates it retrieves), and the challenge for certificate negotiation (here tlschallenge
, just because it's the most concise configuration option for the sake of the example).Using the certificate resolver
version: "3.9"
services:
# ...
my-app:
image: traefik/whoami:v1.7.1
labels:
- "traefik.http.routers.my-app.rule=Host(`example.com`)"
- "traefik.http.routers.my-app.tls=true"
version: "3.9"
services:
# ...
my-app:
image: traefik/whoami:v1.7.1
labels:
- "traefik.http.routers.my-app.rule=Host(`example.com`)"
- "traefik.http.routers.my-app.tls.certresolver=le"
example.com
, the le
certificate resolver will transparently negotiate one for you. Yes, it's that simple!Multiple certificate resolvers
# Static configuration
certificatesResolvers:
resolver-digital-ocean:
acme:
# ...
dnsChallenge:
provider: "digitalocean"
delayBeforeCheck: 0
tls-challenge-resolver:
acme:
# ...
tlsChallenge: {}
# Dynamic configuration
http:
routers:
https-route:
rule: "Host(`my.domain`)"
tls:
certResolver: "resolver-digital-ocean"
https-route-2:
rule: "Host(`other.domain`)"
tls:
certResolver: "tls-challenge-resolver"
my.domain
using the dnsChallenge
with DigitalOcean and to generate certificates for other.domain
using the tlsChallenge
.Wildcard and Let's Encrypt
# Dynamic configuration
http:
routers:
router-example:
rule: "Host(`something.my.domain`)"
tls:
certResolver: "my-resolver"
domains:
main: "my.domain"
sans: "*.my.domain"
*.my.domain
..tls.domains
section, Traefik Proxy would have used the host (in this example, something.my.domain
) defined in the Host
rule to generate a certificate.What about TCP and TLS?
tls
option, but this time on your tcp
router.version: "3.9"
services:
# ...
my-tcp-app:
image: traefik/whoamitcp:v0.2.1
labels:
- "traefik.tcp.routers.my-tcp-app.rule=HostSNI(`tcp-example.com`)"
- "traefik.tcp.routers.my-tcp-app.tls=true"
What about passthrough?
version: "3.9"
services:
# ...
my-tcp-app:
image: traefik/whoamitcp:v0.2.1
labels:
- "traefik.tcp.routers.my-tcp-app.rule=HostSNI(`tcp-example.com`)"
- "traefik.tcp.routers.my-tcp-app.tls.passthrough=true"
What about mTLS?
require-mtls
) forcing verification and pointing to the root CA of your choice.# Dynamic configuration
tls:
options:
require-mtls:
clientAuth:
clientAuthType: RequireAndVerifyClientCert
caFiles:
- /certs/rootCA.crt
ls.options=require-mtls@file
. Once done, every client trying to connect to your routers will have to present a certificate signed with the root certificate authorities configured in the caFiles
list.version: "3.9"
services:
# ...
my-app:
image: traefik/whoami:v1.7.1
labels:
- "traefik.http.routers.my-app.rule=Host(`example.com`)"
- "traefik.http.routers.my-app.tls.options=require-mtls@file"
TLS from Traefik Proxy to your service
# Dynamic configuration
http:
serversTransports:
my-transport:
serverName: "traefik.example.com"
certificates:
- certFile: path/to/cert.crt
keyFile: path/to/cert.key
version: "3.9"
services:
# ...
my-app:
image: traefik/whoami:v1.7.1
labels:
- "traefik.http.routers.my-app.rule=Host(`example.com`)"
- "traefik.http.services.my-app.loadbalancer.serverstransport=my-transport@file"
Mix and match
- Do you want to serve TLS with a self-signed certificate?
- Are you're looking to get your certificates automatically based on the host matching rule
- Would you rather terminate TLS on your services?
- Do you require mTLS from the client?
- Do you extend this mTLS requirement to the backend services?