5 minutes
Traefik 2.0 in a docker swarm environment
Context
Traefik is an edge router, it distribute all my HTTP HTTPS and TCP request to the good docker container. This page dscribe how i setup a docker traefik instance in my personal docker swarm Environement : 2 managers and 2 workers. Hosted on 4 DigitalOcean’s droplets.
This article explain how i setup trafik and his dashborad.
Docker file
I want traefik to listen port :80 and :443 and i’ll publish the ports in host mode to be sure to have the real IP’s of the clients (I need them for security purpose, allow only my IP by example) :
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
For the :443 port, i want a valid SLL certificate, so i need to define a Cloudflare environement variable filled with my cloudflare mail and API key (this could be done in the static configuration file, but, i simply choose to write my credentals here):
environment:
- "CF_API_EMAIL=me@domain.com"
- "CF_API_KEY=cloudflare_super_secure_key"
Next step is to bind some needed volumes to my container :
volumes:
# Directory for letsencrypt files
- "/var/data/traefik2/letsencrypt:/letsencrypt"
# Mount the docker socket
- "/var/run/docker.sock:/var/run/docker.sock:ro"
# Mount the static traefik.toml file
- "/var/data/traefik2/conf/traefik.toml:/etc/traefik/traefik.toml"
# Directory for the logs
- "/var/data/traefik2/log/:/log"
# Directory for the config files
- "/var/data/traefik2/conf:/config"
# Set the same timezone as the host
- "/etc/timezone:/etc/timezone:ro"
- "/etc/localtime:/etc/localtime:ro"
You can view the full docker-compose.yml file here
Configuration files
traefik.toml
Now that the compose file is ready It’s time to setup traefik.toml static configuration file.
First let’s send Anonymous usage to help the Traefik team
[global]
sendAnonymousUsage = true
I then need to tell traefik witch port to use (aka the same ports i exposed in the docker-file.yml)
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.https]
address = ":443"
The next step is to define the providers
[providers]
# Docker
[providers.docker]
# Traefik will watch for docker container change
watch = true
# The endpoint to look at
endpoint = "unix:///var/run/docker.sock"
# My containers will never be exposed if i don't configure them
exposedByDefault = false
# Docker is in swarm mode
swarmMode = true
# The default docker network to look at
network = "lan_nosecure"
# File (aka traefik.toml and/or others dynamic configuration files)
[providers.file]
# My configuratioin files will be located in thiis directory
directory = "/config"
# Traefik will watch for file change
watch = true
I want the API to be rechable (i’ll define later in the dynamic.toml file how to access it)
[api]
insecure = false
dashboard = true
debug = true
Send metrics to my influxDB
[metrics]
[metrics.influxDB]
address = "http://IP:8086"
protocol = "http"
pushInterval = "10s"
database = "traefik"
retentionPolicy = ""
addEntryPointsLabels = true
addServicesLabels = true
Define the log and the access files
[log]
level = "error"
filePath = "/log/traefik.log"
[accessLog]
filePath = "/log/access.log"
I’ll next set the SSL certificate configuration
[certificatesResolvers]
[certificatesResolvers.cloudflare]
[certificatesResolvers.cloudflare.acme]
# Where to save my certificates
storage = "/letsencrypt/acme.json"
# When you're good to go you can remove this line (it's the letsencrypt testing server)
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
# I choose to use the DNS challenge and wait 10 seconds before check
[certificatesResolvers.cloudflare.acme.dnsChallenge]
provider = "cloudflare"
delayBeforeCheck = 10
You can view the full traefik.toml file here
dynamic.toml
The hard part !
I consider this part as the “tricky” part of the setup as it’s more complex to write. It’s require patience and no typo !
I first want all my HTTP traffic to be SSL encrypted so i define a global redirect. All the HTTP requests will be routed to the HTTPS router :
[http.routers]
# Define a router and name it redirecttohttps
[http.routers.redirecttohttps]
# This router will use this entrypoint
entryPoints = ["http"]
# Definition of what to catch (this regex means all)
rule = "HostRegexp(`{host:.+}`)"
# The traffic that enter trought this entrypoint is send to this middleware
middlewares = ["httpsredirect"]
# Each middleware must have a service
service = "noop"
I also have to define this service (to avoid an error, even if this one will never be called).
[http.services]
[http.services.noop.loadBalancer]
[[http.services.noop.loadBalancer.servers]]
url = "http://pony.club"
Time to define the httpsredirect middleware.
[http.middlewares]
[http.middlewares.httpsredirect.redirectScheme]
scheme = "https"
permanent = true
This will tell traefik to listen all the HTTP requests (:80 as defined in traefik.toml) and redirect them to the HTTPS entrypoint (:443 as defined in traefik.toml)
I also want the traefik dashboard to be rechable from home, so i defined the router named traefik and apply the home-ipwhitelist middleware to the requests.
[http.routers.traefik]
# on witch entrypoint this router will listen
entryPoints = ["https"]
# the rule definition
rule = "Host(`traefik.ch1.ninja`)"
# the requests have to go trought theses middlewares before the service
middlewares = ["home-ipwhitelist@file", "security-headers@file"]
# Witch service this router will serve
service = "api@internal"
# this define the resolver this router will use to generate the SSL certiificate
[http.routers.traefik.tls]
certResolver = "cloudflare"
Lets define the middlewares used by the traefik router.
This one is for the headers, you can see on securityheaders.com that this configuration is graded “A”. It include, CORS headers, security headers and some policy. A good ressource for undestanding headers : developer.mozilla.org
[http.middlewares.security-headers.headers]
AccessControlAllowMethods = ["GET", "OPTIONS", "PUT"]
AccessControlAllowOrigin = "origin-list-or-null"
AccessControlMaxAge = 100
#AddVaryHeader = true
BrowserXssFilter = true
ContentTypeNosniff = true
ForceSTSHeader = true
FrameDeny = true
SSLRedirect = true
#STSIncludeSubdomains = true
STSPreload = true
CustomFrameOptionsValue = "SAMEORIGIN"
ReferrerPolicy = "same-origin"
FeaturePolicy = "geolocation 'self'"
STSSeconds = 315360000
Now the home IP middleware.
[http.middlewares.home-ipwhitelist.ipWhiteList]
sourceRange = ["homeIP/32"]
You can view the full traefik.toml file here
tls.toml
I have separated this configuration in a dedicated file, you can see on www.ssllabs.com that this configuration is secured.
[tls.options]
[tls.options.default]
sniStrict = true
cipherSuites = [
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_FALLBACK_SCSV"
]
Find the tls.toml file here
Here we are !, the configuration is done, and, as you can see (you navigate to this site trought traefik right now) it’s workinig like a charm !
I really thanks the traefik team for this fast, secure and marvelous product ❤️.
Thanks for reading.