In my previous post, I detailed moving your Traefik configuration to a slightly more permanent format (YAML… I know, just go with it). You might have then been tempted to go to SSL Labs and do an SSL Test. What you would have discovered, in that tragic scenario, is a mere B grade!
“Why, oh why?” I hear you cry, “I did everything right!” Well, technically you did, so don’t feel too bad. It’s not a fault of yours that you’re letting people with filthy TLS 1.0 and 1.1 view your site. It is, however, the view of certain industry bodies that these technologies are deprecated and therefore have multiple nasty security vulnerabilities.
Caveats
- You’re running Traefik in a container, bonus points if you’re using Docker Compose
- You’ve backed up your current running config and volume contents
- You understand basic Docker concepts like bringing containers up and down, and what that will do to your currently running infrastructure
A Quick Recap
Now, if you’d gone through that previous post I mentioned above, you would have read about the difference between Static and Dynamic configuration in Traefik. A quick reminder:
- Static runs at startup
- Dynamic runs each time a new request comes in to Traefik
So with Static configuration, we can define the entrypoints upon which Traefik accepts traffic (port 80, 443, etc) but according to Traefik’s split of these rules, defining which TLS version is accepted is done Dynamically, i.e. checked every time a new connection comes in to the router. “But… but…” I hear you stammer, “I’ve only passed through a static configuration file! How do?”
It really is just as easy as the static configuration file, with one small addition.
Loading The File
To enable us to test this method as we go, we need to create an empty dynamic.yml
file in the same directory as the rest of our configuration. Once this is done, we need to add it to our traefik.yml
file (or whatever you’ve called your static config file) as a ‘Provider.’ You’ll probably also have something like Docker in here too, see the bottom of the post for my full file in context.
providers:
file:
filename: /etc/traefik/dynamic.yml
The final step is to add this to our docker-compose.yml
file so that the container has access to this file. Again, see the bottom of the post for my full file.
volumes:
- "./dynamic.yml:/etc/traefik/dynamic.yml"
Now, we’re ready to add some config to dynamic.yml
file!
The Dynamic File
It’s worth mentioning here that I haven’t yet seen a successful way to do this via the cmdline and trust me, I’ve looked… This was the impetus for me converting my static config to a YAML file in the first place, so just trust me on that one!
tls:
options:
default:
minVersion: VersionTLS13
Now, at first glance, this looks like this will solve all of your problems. But if you re-test at this point, you’ll notice that the ‘TLS’ section of the report shows a yellow ‘No’ by TLS 1.2. This is because TLS 1.2 isn’t deprecated yet, it’s still perfectly usable and certain individuals who might not have upgraded their browsers in quite some time (or Java 8…) might not have full TLS 1.3 support.
Surely that’s easy to fix, right? Just amend the ‘13’ to a ‘12’ so that your minimum version of TLS is 1.2 and clients can negotiate higher if they like.
tls:
options:
default:
minVersion: VersionTLS12
Now with this, you’ll still get an A grade, but the ‘Ciper Suites’ section of the report will shed some more light here:
Now, you don’t want to be seen by all of your webmaster friends to be supporting WEAK cipher suites, do you? That would just be embarrassing, every time you turn around and see 2 people whispering and pointing in your direction, you’d know exactly what they were talking about! Let’s solve that before it becomes a problem. Traefik has a ‘cipherSuites’ key that we can add to the default configuration which means that all of our sites will automatically only accept these and best of all, we only have to define it once! I’ve defined a few accepted ones here, but please DYOR (Do Your Own Research) to make sure that you’re happy to support these, or possibly to add more. Our final file, therefore, becomes the below:
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_AES_128_GCM_SHA256
- TLS_CHACHA20_POLY1305_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
And you’ll also notice the report now shows:
Congrats, you’ve made the grade! :confetti_ball:
In a future post, we’ll go over upgrading this grade to an A+!
Full dynamic.yml
File
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_AES_128_GCM_SHA256
- TLS_CHACHA20_POLY1305_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Full traefik.yml
(Static) File
entryPoints:
http:
address: ":80"
https:
address: ":443"
providers:
docker: {}
file:
filename: /etc/traefik/dynamic.yml
certificatesResolvers:
le:
acme:
email: name@domain.tld
storage: /letsencrypt/acme.json
tlsChallenge: {}
Full docker-compose.yml
File
version: "3.3"
services:
traefik:
container_name: traefik
image: traefik:v2.4.8
labels:
- "traefik.enable=true"
### Middleware Redirect
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
### Global HTTP -> HTTPS Redirect
- "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.redirs.entrypoints=http"
- "traefik.http.routers.redirs.middlewares=https-redirect"
- "traefik.http.routers.redirs.priority=1"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "certs:/letsencrypt"
- "./traefik.yml:/etc/traefik/traefik.yml"
- "./dynamic.yml:/etc/traefik/dynamic.yml"
restart: unless-stopped
networks:
- proxy