Achieving an A Grade with Traefik

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
I was reminded of how incredible Linkin Park are whilst writing this post so here's my favourite song from the masterpiece that is Hybrid Theory