Improving Traefik SSL security

This post expands on Distilling Traefik lessons learned with some more stuff I have since found to improve things like the SSL Labs SSL Server Test score for a site served via a Traefik container.

The main source I found for this useful stuff was at this excellent page, I just translated this into my environment.

The basic difficulty is that Traefik by default allows TLS 1.0 and 1.1 connections, along with their rather unfortunate weak ciphers. In addition, there are a bunch of security related HTTP headers that we can get Traefik to add for any services it serves to the world.

TLS versions

It’s a Very Bad Idea to support TLS v1.0 and a Bad Idea to support TLS v1.1, both of these are no longer secure. HTTPS sites should always serve TLS v1.2 as the lowest supported version. In some cases you may even want to limit a more sensitive site to a minimum of 1.3.

To set the default minimum TLS version to 1.2, you can set the default TLS options in the dynamic config file (traefik_dynamic.toml) as follows:

[tls.options.default]
  minVersion = "VersionTLS12"
  cipherSuites = [
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"
  ]

This sets the default minimum TLS version to 1.2 and limits the encryption cipher suites to the secure ones supported by TLS 1.2 and 1.3. If you have a need to limit some services to TLS v1.3, you can also add:

[tls.options.tlsv13only]
  minVersion = "VersionTLS13"

With that in place, you can add traefik.http.routers.web1.tls.options=tlsv13only to the labels related to the service you want to secure with TLS v1.3.

HTTP headers

The TLS version stuff above will make SSL Labs happier already, but it’s a good idea to add a bunch of headers that tell browsers and any potential proxy servers between the browser and Traefik how to behave.

The reasoning behind using these headers is beyond the scope of this post, there’s a lot of great info out there explaining them. Here I just show how to get Traefik to add these headers in case your application doesn’t.

In your traefic_dynamic.toml file, you can define a middleware as follows:

[http.middlewares.secure-headers.headers]
  sslRedirect = true
  frameDeny = true
  stsIncludeSubdomains = true
  stsPreload = true
  stsSeconds = 63072000
  contentTypeNosniff = true
  accessControlAllowMethods = [ "GET", "POST" ]
  accessControlAllowOriginList = [ "https://web1.mydomain", "https://web2.mydomain", "https://web3.mydomain" ]
  accessControlMaxAge = 100
  addVaryheader = true
  contentSecurityPolicy = "script-src 'self'"
  referrerPolicy = "origin-when-cross-origin"

You will want to edit the accessControlAllowOriginList line to show your origin servers.

Once that’s defined, you can add labels to your containers or file defined routers. In my previous post, I defined three routers (two via container labels and one in traefik_dynamic.toml). For the two defined in docker labels, just add these two labels:

  - traefik.http.routers.web1.middlewares=secure-headers@file
  - traefik.http.routers.web2.middlewares=secure-headers@file

For web3, which is defined in traefik_dynamic.toml, you can add the line in bold shown below:

[http.routers.web3]
  rule = "Host(`web3.mydomain`)"
  entrypoints = ["web","websecure"]
  service = "web3@file"
  middleware="secure-headers@file"
  [http.routers.web3.tls]
    certResolver = "lets-encrypt"

Posted

in

by

Tags: