Improve Traefik's HTTPS Encryption with Qualys SSL Labs and testssl.sh
tldr; Encryption (and HTTPS) is a complicated beast, but we have to do our best to make sure that our sites run securely. With the help of tools like Qualys SSL Labs [1] or the open-source testssl.sh [2] I update my production Traefik installations to run with the most secure configurations as possible.
Disclaimer: I am not an encryption expert and will be the first to admit that there is a bit of "cargo culting." I am making the changes necessary to my configurations such that the SSL labs gives an "A" grade or better, and that I see no RED flags from testssl.sh. I provide no advice here but rather share the configuration and outcomes.
Before we begin
Before you start disabling old encryption protocial it is a good idea to know who your users are and what software stacks they use to connect with you. Older stacks such as IE11 on older Windows or Safari on older Macs may break. See all the red? Yeah...
My current config
Below you see the "B" rating for the SSL configuration for simplecto.com. This is in part because the current configuration allows for older, less secure versions of TLS.
My current config that earned the "B" rating
This is my current config. There are no options set for encryption settings, cyphers, protocols, etc. I simply take the defaults from Traefik. This may or may not be sufficient for you to pass a security or compliance audit.
I use a single docker-compose.yml
file to deploy which keeps complexity to a minimum.
version: '3'
services:
traefik:
container_name: traefik
image: traefik:v2.1
command:
- "--providers.docker=true"
- "--entryPoints.web.address=:80"
- "--entryPoints.websecure.address=:443"
- "--certificatesResolvers.le.acme.email=secret_email@domain.com"
- "--certificatesResolvers.le.acme.storage=acme.json"
- "--certificatesResolvers.le.acme.tlsChallenge=true"
- "--certificatesResolvers.le.acme.httpChallenge=true"
- "--certificatesResolvers.le.acme.httpChallenge.entryPoint=web"
restart: always
ports:
- 80:80
- 443:443
- 8080:8080
networks:
- web
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./acme.json:/acme.json
labels:
# Redirect all HTTP to HTTPS permanently
- traefik.http.routers.http_catchall.rule=HostRegexp(`{any:.+}`)
- traefik.http.routers.http_catchall.entrypoints=web
- traefik.http.routers.http_catchall.middlewares=https_redirect
- traefik.http.middlewares.https_redirect.redirectscheme.scheme=https
- traefik.http.middlewares.https_redirect.redirectscheme.permanent=true
networks:
web:
external: true
What needs to change to get the "A"?
I went searching for answers and quickly found myself on the Containous community forums [3]. It was a post there that held the answers.
I must tell Traefik that I want TLS 1.3 (still experimental) and that the minimum version to support is TLSv1.2. Oh, and remember that pristine single-file config? Yeah, well, now I need to bind-mount some static files into the container. No biggie, but slightly more complex.
The new traefik_conf.yaml
This yaml
file tells Traefik to:
- Make the minimum supported version of TLS 1.2 and to enable 1.3.
- Only accept connections where the hostname is specified (SNI)
- Support a specific list of Cyphers (the cargo-cult part for me)
tls:
options:
default:
minVersion: VersionTLS12
sniStrict : true
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
mintls13:
minVersion: VersionTLS13
Now that we have this, we need to get it into the container and tell Traefik to use it.
There are two lines we need to add to our docker-compose.yml
file:
"--providers.file.filename=/traefik_conf.yaml"
undercommand:
./traefik_conf.yaml:/traefik_conf.yaml
undervolumes:
Now all we need to do is docker-compose up -d
and re-test!
Wohoo! – Ok, A-grade achieved.
Test locally with testssl.sh
As an alternative to SSL Labs there is a nice open-source tool called testssl.sh. You can run this locally and see way more information that you might know what to do with. You can test on any docker-enabled machine with:
docker run --rm -ti drwetter/testssl.sh example.com
There is quite a bit of output, but suffice to say you will get more than you are looking for :-)
Conclusion
With a small configuration change to Traefik we were able to move our SSL Labs overall rating from B to A. We had to add support for TLS1.3 and remove support for TLS 1.0 and 1.1.
Reference
- https://www.ssllabs.com/ssltest/analyze.html
- https://github.com/drwetter/testssl.sh
- https://community.containo.us/t/poor-rating-on-ssl-labs/2400/7
- https://docs.traefik.io/v2.0/providers/file/#filename
Update February 19, 2020
Reddit user /u/Fredouye left a helpful comment that would push the grading even higher. Indeed, we did improve our grade on key exchange to 100%.
traefik_conf.yaml
tls:
options:
default:
minVersion: VersionTLS12
curvePreferences:
- secp521r1
- secp384r1
sniStrict: true
traefik's docker-compose.yml
traefik.http.middlewares.securedheaders.headers.forcestsheader: "true"
traefik.http.middlewares.securedheaders.headers.sslRedirect: "true"
traefik.http.middlewares.securedheaders.headers.STSPreload: "true"
traefik.http.middlewares.securedheaders.headers.ContentTypeNosniff: "true"
traefik.http.middlewares.securedheaders.headers.BrowserXssFilter: "true"
traefik.http.middlewares.securedheaders.headers.STSIncludeSubdomains: "true"
traefik.http.middlewares.securedheaders.headers.STSSeconds: "315360000"
Thanks!