Traefik 2 with docker autodiscovery + automatic https + http to https redirection + secure api + basic auth + influxdb metrics
20/04/2020Minimal version
http server with docker autodiscovery
Create a docker-compose.yml with this content
version: "3.3"
services:
traefik:
image: "traefik:v2.0.0"
command:
- --entrypoints.web.address=:80
- --providers.docker=true
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
myapp:
image: containous/whoami:v1.3.0
labels:
- traefik.http.routers.myapp.rule=Host(`test.raphaelpiccolo.com`)
ports:
- "8989:80"Then
docker-compose up -d curl http://test.raphaelpiccolo.com curl http://test.raphaelpiccolo.com:8989
Both url work, to block second url, bind to localhost port :
- "127.0.0.1:8989:80"
It's also possible to remove ports specification completely if the image exposes the port, because traefik will find it.
complete version :
http server with docker autodiscovery
automatically redirect http to https if using websecure entrypoint
automatically generate https when needed
This file will store https certificates : /traefik/acme.json
wait 10 secs for letsencrypt to generate certificates. In the meantime the site is working but there is a certificate alert.
traefik api is now available https://traefik.raphaelpiccolo.com
Create a docker-compose.yml
version: "3.3"
services:
traefik:
image: "traefik:v2.0.0"
command:
# listen port 443 and name it websecure
- --entrypoints.websecure.address=:443
# listen port 80 and name it web
- --entrypoints.web.address=:80
# you need to enable traefik on each container
- --providers.docker.exposedByDefault=false
# listen for docker changes
- --providers.docker=true
# activate traefik api
- --api
# activate traefik ping service
- --ping
- --ping.manualrouting=true
- --ping.entryPoint=websecure
# use letencrypt to generate certificates
- --certificatesresolvers.le.acme.email=rafi.piccolo@gmail.com
- --certificatesresolvers.le.acme.storage=/traefik/acme.json
- --certificatesresolvers.le.acme.tlschallenge=true
# set logging level
- --log.level=INFO
# activate accesslog in a separate file
- --accesslog=true
- --accesslog.filepath=/traefik/access.log
# activate metrics exporting to influxdb
- --metrics.influxdb=true
- --metrics.influxdb.protocol=http
- --metrics.influxdb.address=https://influxdb.raphaelpiccolo.com
- --metrics.influxdb.database=mydb
- --metrics.influxdb.username=admin
- --metrics.influxdb.password=${PASSWORD}
# config file for more settings : tls ciphers for grade A on ssllabs
- --providers.file.filename=/traefik/traefik.toml
- --providers.file.watch=true
# add a custom uniqid on every request
- --tracing.jaeger=true
- --tracing.jaeger.samplingParam=0
- --tracing.jaeger.traceContextHeaderName=X-Request-ID
labels:
- traefik.enable=true
# catch all traefik errors and send them to the errorpage service
- "traefik.http.routers.globalerrorpage.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.globalerrorpage.tls.certresolver=le"
- "traefik.http.routers.globalerrorpage.entrypoints=websecure"
- "traefik.http.routers.globalerrorpage.service=errorpage"
- "traefik.http.routers.globalerrorpage.priority=1"
# create a middleware to replace 404 errors by a standard page
- "traefik.http.middlewares.errorpage.errors.status=404"
- "traefik.http.middlewares.errorpage.errors.service=errorpage"
- "traefik.http.middlewares.errorpage.errors.query=/{status}"
# create a middleware named redirect-to-https, to redirect http to https
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"
# create a middleware to redirect to www
- traefik.http.middlewares.redirect-to-www.redirectregex.regex=(https|http)://(?:www.)?(.*)
- traefik.http.middlewares.redirect-to-www.redirectregex.replacement=https://www.$${2}
# create a middleware to redirect to non-www
- traefik.http.middlewares.redirect-to-nonwww.redirectregex.regex=(https|http)://(www\.(.*))
- traefik.http.middlewares.redirect-to-nonwww.redirectregex.replacement=https://$${3}
# apply middleware "redirect-to-https", to all hosts connected to "web" entrypoint
- "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.redirs.entrypoints=web"
- "traefik.http.routers.redirs.middlewares=redirect-to-https"
# create a middleware named auth, to request a basic authentification of users
# you can generate a user:password pair with this command :
# echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
- traefik.http.middlewares.auth.basicauth.users=user:$$apr1$$WH5iVjXW$$bda85jX7RQGGcCs3hnP8b0
# create a middleware named admin, to request a basic authentification for admin
- traefik.http.middlewares.admin.basicauth.users=admin:$$apr1$$SIIXcmeY$$GAkMymzdfhwACgQvM9PNy1
# create a middleware named securityheaders to automatically set security headers to pass : https://securityheaders.com/
- "traefik.http.middlewares.securityheaders.headers.framedeny=true"
- "traefik.http.middlewares.securityheaders.headers.customFrameOptionsValue=sameorigin"
- "traefik.http.middlewares.securityheaders.headers.referrerPolicy=same-origin"
- "traefik.http.middlewares.securityheaders.headers.BrowserXssFilter=true"
- "traefik.http.middlewares.securityheaders.headers.ContentTypeNosniff=true"
- "traefik.http.middlewares.securityheaders.headers.ForceSTSHeader=true"
- "traefik.http.middlewares.securityheaders.headers.SSLRedirect=true"
- "traefik.http.middlewares.securityheaders.headers.STSIncludeSubdomains=true"
- "traefik.http.middlewares.securityheaders.headers.STSPreload=true"
- "traefik.http.middlewares.securityheaders.headers.STSSeconds=315360000"
# api secure
- "traefik.http.routers.traefik.rule=Host(`traefik.raphaelpiccolo.com`)"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=securityheaders,admin"
- "traefik.http.routers.traefik.tls.certresolver=le"
- "traefik.http.routers.traefik.entrypoints=websecure"
# ping secure
- "traefik.http.routers.ping.rule=Host(`traefik.${DOMAIN}`) && PathPrefix(`/ping`)"
- "traefik.http.routers.ping.service=ping@internal"
- "traefik.http.routers.ping.middlewares=securityheaders"
- "traefik.http.routers.ping.tls.certresolver=le"
- "traefik.http.routers.ping.entrypoints=websecure"
ports:
# web entrypoint
- "80:80"
# websecure entrypoint
- "443:443"
volumes:
# to listen docker changes
- "/var/run/docker.sock:/var/run/docker.sock:ro"
# to save https certificates
- "./traefik:/traefik"
myapp:
image: containous/whoami:v1.3.0
labels:
# domain name for this container : you can specify multiple domains
- traefik.http.routers.myapp.rule=Host(`raphaelpiccolo.com`, `www.raphaelpiccolo.com`)
# set a basic auth on a container + securityheaders
- traefik.http.routers.myapp.middlewares=auth,securityheaders,redirect-to-www
# use https
- traefik.http.routers.myapp.tls.certresolver=le
- traefik.http.routers.myapp.entrypoints=websecure
ports:
- "127.0.0.1:9090:80"
# proxy to host (172.17.0.1) port (19999)
# get docker host ip (usable from inside a container) : ip addr show docker0
# displays : 172.17.0.1
netdata:
image: alpine/socat
container_name: netdata
restart: always
command: tcp-listen:80,fork,reuseaddr tcp-connect:172.17.0.1:19999
labels:
- traefik.http.routers.netdata.rule=Host(`netdata.flatbay.fr`)
# when the container has many ports you can indicate to traefik which one he should connect to the domain
- traefik.http.services.netdata.loadbalancer.server.port=80
# if you want to specify tls options (see traefik.toml)
- traefik.http.routers.netdata.tls.options=notsafe@file
- traefik.http.routers.netdata.tls.certresolver=le
- traefik.http.routers.netdata.entrypoints=websecure
- traefik.http.routers.netdata.middlewares=securityheaders,admin
portainer:
image: portainer/portainer
container_name: portainer
restart: always
ports:
- "127.0.0.1:19000:9000"
# no auth because we add the admin basic auth with traefik
command: --no-auth
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer:/data
labels:
- traefik.http.routers.portainer.rule=Host(`portainer.flatbay.fr`)
- traefik.http.routers.portainer.tls.certresolver=le
- traefik.http.routers.portainer.entrypoints=websecure
- traefik.http.routers.portainer.middlewares=securityheaders,admincreate traefik.toml
only usefull if grade A on ssllabs
[tls.options]
[tls.options.default]
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_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"
]
sniStrict = true
[tls.options.notsafe]other technique to create a service to redirect to host
in traefik.toml
[http]
[http.routers]
[http.routers.netdata]
rule = "Host(`netdata.flatbay.fr`)"
service = "netdata"
[http.services]
[http.services.netdata.loadBalancer]
[[http.services.netdata.loadBalancer.servers]]
url = "http://172.17.0.1:19999/"create an standard error page :
create a new service
errorpage:
build: ./errorpage
restart: always
container_name: errorpage
volumes:
- ./errorpage:/usr/app/
working_dir: /usr/app
command: forever -w server.js
labels:
- traefik.enable=true
- traefik.http.services.errorpage.loadbalancer.server.port=3000
- traefik.http.routers.errorpage.rule=Host(`error.raphaelpiccolo.com`)
- traefik.http.routers.errorpage.tls.certresolver=le
- traefik.http.routers.errorpage.entrypoints=websecure
- traefik.http.routers.errorpage.middlewares=securityheaders
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']add this in a service in which you want to replace the 404 page with the one from the errorpage service
- traefik.http.routers.d2cpartners.middlewares=securityheaders,redirect-to-nonwww,errorpage
custom header uuid
add this in command
--tracing.jaeger=true --tracing.jaeger.samplingParam=0 --tracing.jaeger.traceContextHeaderName=X-Request-ID
TCP :
in this exemple we relay the mysql connection through traefik in plain unencrypted tcp/IP.
ps: we need one entrypoint for every tcp connection because we can't switch services by hostname like we do on http (unless we use tls)
traefik:
...
command:
- --entrypoints.mysql.address=:3306
ports:
- "3306:3306"
mysql:
...
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.mysql.rule=HostSNI(`*`)"
- "traefik.tcp.services.mysql.loadbalancer.server.port=3306"
- "traefik.tcp.routers.mysql.entrypoints=mysql"TCP with tls :
ps: tls doesnt work because of a bug in mysql, but this is how it would work :
add this label on mysql
- "traefik.tcp.routers.mysql.tls=true"
or this one to let mysql deal with tls itself
- "traefik.tcp.routers.mysql.tls.passthrough=true"
trusted ips and forwarded headers
when you make on request on traefik, it will add a header when it will call your service.
X-Forwarded-For, with the ip of the client initiating connection.
for exemple:
X-Forwarded-For: X.X.X.X
if you add this in commands :
- --entryPoints.web.forwardedHeaders.insecure - --entryPoints.websecure.forwardedHeaders.insecure
and you set a custom X-forwarded-For before calling traefik, then X-forwarded-For will be the ip you specified, plus the real ip :
$> curl -H 'X-Forwarded-For: 1.1.1.1' https://xxx.com/ X-Forwarded-For: 1.1.1.1, X.X.X.X
you could also allow trust only your ip : only your ip (X.X.X.X) can set a X-Forwarded-For header.
--entryPoints.web.forwardedHeaders.trustedIPs=127.0.0.1/32,X.X.X.X
full exemple :
$> curl -H 'X-Forwarded-For: 1.1.1.1' https://xxx.com/ Hostname: 5c1b8960e103 IP: 127.0.0.1 IP: 172.21.0.46 RemoteAddr: 172.21.0.43:60138 GET / HTTP/1.1 Host: xxx.com User-Agent: curl/7.70.0 Accept: */* Accept-Encoding: gzip X-Forwarded-For: 1.1.1.1, X.X.X.X X-Forwarded-Host: xxx.com X-Forwarded-Port: 443 X-Forwarded-Proto: https X-Forwarded-Server: 00625e8b8443 X-Real-Ip: X.X.X.X X-Request-Id: 276a624864bd6164:56bfa7b03c583401:4f64762abc3cc711:0
insecure ping service
add this
command:
- --ping
- --entrypoints.traefik.address=:8080
ports:
- "8080:8080"call this to check
curl http://X.X.X.X:8080/ping
https ping service
add this
command:
- --ping
- --ping.manualrouting=true
- --ping.entryPoint=websecure
labels:
- "traefik.http.routers.ping.rule=Host(`traefik.${DOMAIN}`) && PathPrefix(`/ping`)"
- "traefik.http.routers.ping.service=ping@internal"
- "traefik.http.routers.ping.middlewares=securityheaders"
- "traefik.http.routers.ping.tls.certresolver=le"
- "traefik.http.routers.ping.entrypoints=websecure"call this to check
curl https://traefik.raphaelpiccolo.com/ping