Traefik Proxy 2.x and TLS 101

Traefik Proxy 2.x and TLS 101

The challenge that I'll explore today is that you have an HTTP service exposed through Traefik Proxy and you want to deal with the HTTPS burden (TLS termination), leaving your pristine service unspoiled by mundane technical details.

Originally published on Traefik blog
There are hundreds of reasons why I love being a developer (besides memories of sleepless nights trying to fix a video game that nobody except myself would ever play).
Being a developer gives you superpowers — you can solve any problem. Yes, especially if they don't involve real-life, practical situations.
But these superpowers are sometimes hindered by tedious configuration work that expects you to master yet another arcane language assembled with heaps of words you've never seen before. Such a barrier can be encountered when dealing with HTTPS and its certificates.
Luckily for us — and for you, of course — Traefik Proxy lowers this kind of hurdle and makes sure that there are easy ways to connect your projects to the outside world securely.

The goal for today

The challenge that I'll explore today is that you have an HTTP service exposed through Traefik Proxy and you want Traefik Proxy to deal with the HTTPS burden (TLS termination), leaving your pristine service unspoiled by mundane technical details.
I'm assuming you have a basic understanding of Traefik Proxy on Docker and that you're familiar with its configuration. If not, it's time to read Traefik 2 & Docker 101.
For the purpose of this article, I'll be using my pet demo docker-compose file. It enables the Docker provider and launches a my-app application that allows me to test any request.
version: '3.9'
services:
  traefik:
    image: traefik:v2.6
    command:
      - --entrypoints.web.address=:80
      - --providers.docker=true
    ports:
      - '80:80'
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock:ro'

  my-app:
    image: traefik/whoami:v1.7.1

Getting things ready

First things first, let's make sure my setup can handle HTTPS traffic on the default port (:443). Let's also be certain Traefik Proxy listens to this port thanks to an entrypoint I'll name web-secure.
version: '3.9'
services:
  traefik:
    image: traefik:v2.6
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.web-secure.address=:443 # Declares the web-secure entrypoint in Traefik
      - --providers.docker=true
    ports:
      - '80:80'
      - '443:443' # Docker sends requests on port 443 to Traefik on port 443
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock:ro'

  my-app:
    image: traefik/whoami:v1.7.1
To avoid confusion, let's state the obvious — I haven't yet configured anything but enabled requests on 443 to be handled by Traefik Proxy. So, no certificate management yet!

General concepts

In Traefik Proxy, you configure HTTPS at the router level. While defining routes, you decide whether they are HTTP or HTTPS routes (by default, they are HTTP routes).
First, let's expose the my-app service on HTTP so that it handles requests on the domain example.com.
version: '3.9'
services:
  # ...
  my-app:
    image: traefik/whoami:v1.7.1
    labels:
      - 'traefik.http.routers.my-app.rule=Host(`example.com`)'
And now, see what it takes to make this route HTTPS only.
version: '3.9'
services:
  # ...
  my-app:
    image: traefik/whoami:v1.7.1
    labels:
      - 'traefik.http.routers.my-app.rule=Host(`example.com`)'
      - 'traefik.http.routers.my-app.tls=true'
There you have it! By adding the tls option to the route, you've made the route HTTPS. The TLS configuration could be done at the entrypoint level to make sure all routers tied to this entrypoint are using HTTPS by default. See the Traefik Proxy documentation to learn more.
The only unanswered question left is, where does Traefik Proxy get its certificates from? And the answer is, either from a collection of certificates you own and have configured or from a fully automatic mechanism that gets them for you.
Let's see these solutions in action.

Option 1 - certificates you own

The least magical of the two options involves creating a configuration file.
Say you already own a certificate for a domain — or a collection of certificates for different domains — and that you are then the proud holder of files to claim your ownership of the said domain.
To have Traefik Proxy make a claim on your behalf, you'll have to give it access to the certificate files. Let's do this.

Add a configuration file for certificates

version: '3.9'
services:
  traefik:
    image: traefik:v2.6
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.web-secure.address=:443
      - --providers.docker=true
      - --providers.file.directory=/configuration/
      - --providers.file.watch=true
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock:ro'
      - '/home/username/traefik/configuration/:/configuration/'
Traefik Proxy runs with many providers beyond Docker (i.e., Kubernetes, Rancher, Marathon). Here I chose to add plain old configuration files (--providers.file) to the configuration/ directory and I automatically reload changes with --providers.file.watch=true. I'm using a configuration file to declare our certificates.

Add the certificates to the configuration file

# Dynamic configuration
# in configuration/certificates.yaml
tls:
  certificates:
		# first certificate
    - certFile: /path/to/example-com.cert
      keyFile: /path/to/example-com.key

		# second certificate
    - certFile: /path/to/other.cert
      keyFile: /path/to/other.key

# and so on
Now that I have my YAML configuration file available (thanks to the enabled file provider), I can fill in certificates in the tls.certificates section.
This is all there is to do. When dealing with an HTTPS route, Traefik Proxy goes through your default certificate store to find a matching certificate.

Specifying a default certificate

If no valid certificate is found, Traefik Proxy serves a default auto-signed certificate. But if needed, you can customize the default certificate like so:
# Dynamic configuration
tls:
  stores:
    default:
      defaultCertificate:
        certFile: path/to/cert.cert
        keyFile: path/to/cert.key
Even though the configuration is straightforward, it is your responsibility, as the administrator, to configure/renew your certificates when they expire. If you don't like such constraints, keep reading!

Option 2 — dynamic / automatic certificates

Having to manage (buy/install/renew) your certificates is a process you might not enjoy — I know I don't! If so, you'll be interested in the automatic certificate generation embedded in Traefik Proxy, thanks to Let's Encrypt.
Long story short, you can start Traefik Proxy with no other configuration than your Let's Encrypt account, and Traefik Proxy automatically negotiates (get/renew/configure) certificates for you. No extra step is required.

Certificate resolvers

As I showed earlier, you can configure a router to use TLS with --traefik.http.routers.router-name.tls=true. As a result, Traefik Proxy goes through your certificate list to find a suitable match for the domain at hand — if not, it uses a default certificate.
For the automatic generation of certificates, you can add a certificate resolver to your TLS options. A certificate resolver is responsible for retrieving certificates.
Here, let's define a certificate resolver that works with your Let's Encrypt account.
version: '3.9'
services:
  traefik:
    image: traefik:v2.6
    command:
      - --entrypoints.websecure.address=:443
      # ...
      - --certificatesresolvers.le.acme.email=my@email.com
      - --certificatesresolvers.le.acme.storage=/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true
      # ...
As you can see, I defined a certificate resolver named le of type acme. Then, I provided an email (your Let's Encrypt account), the storage file (for certificates it retrieves), and the challenge for certificate negotiation (here tlschallenge, just because it's the most concise configuration option for the sake of the example).
From now on, Traefik Proxy is fully equipped to generate certificates for you.

Using the certificate resolver

Earlier, I enabled TLS on my router like so:
version: "3.9"
services:
	# ...
	my-app:
    image: traefik/whoami:v1.7.1
    labels:
      - "traefik.http.routers.my-app.rule=Host(`example.com`)"
      - "traefik.http.routers.my-app.tls=true"
Now, to enable the certificate resolver and have it automatically generate certificates when needed, I add it to the TLS configuration:
version: "3.9"
services:
	# ...
	my-app:
    image: traefik/whoami:v1.7.1
    labels:
      - "traefik.http.routers.my-app.rule=Host(`example.com`)"
      - "traefik.http.routers.my-app.tls.certresolver=le"
Now, if your certificate store doesn't yet have a valid certificate for example.com, the le certificate resolver will transparently negotiate one for you. Yes, it's that simple!
And as stated above, you can configure this certificate resolver right at the entrypoint level. Doing so applies the configuration to every router attached to the entrypoint (refer to the documentation to learn more).

Multiple certificate resolvers

With certificate resolvers, you can configure different challenges.
Below is an example that shows how to configure two certificate resolvers that leverage Let's Encrypt, one using the dnsChallenge and the other using the tlsChallenge.
# Static configuration
certificatesResolvers:
	resolver-digital-ocean:
		acme:
			# ...
		  dnsChallenge:
		    provider: "digitalocean"
		    delayBeforeCheck: 0

	tls-challenge-resolver:
		acme:
			# ...
		  tlsChallenge: {}
Later on, you'll be able to use one or the other on your routers.
# Dynamic configuration
http:
	routers:
	  https-route:
	    rule: "Host(`my.domain`)"
      tls:
	      certResolver: "resolver-digital-ocean"

		https-route-2:
	    rule: "Host(`other.domain`)"
	    tls:
	      certResolver: "tls-challenge-resolver"
In the above example that uses the file provider, I asked Traefik Proxy to generate certificates for my.domain using the dnsChallenge with DigitalOcean and to generate certificates for other.domain using the tlsChallenge.
And you've guessed it already — Traefik Proxy supports DNS challenges for different DNS providers at the same time!

Wildcard and Let's Encrypt

Instead of generating a certificate for each subdomain, you can choose to generate wildcard certificates.
# Dynamic configuration
http:
	routers:
	  router-example:
	    rule: "Host(`something.my.domain`)"
	    tls:
        certResolver: "my-resolver"
        domains:
	        main: "my.domain"
	        sans: "*.my.domain"
In the above example, I configured Traefik Proxy to generate a wildcard certificate for *.my.domain.
If I had omitted the .tls.domains section, Traefik Proxy would have used the host (in this example, something.my.domain) defined in the Host rule to generate a certificate.

What about TCP and TLS?

If you want to configure TLS with TCP, then the good news is that nothing changes. You configure the same tls option, but this time on your tcp router.
version: "3.9"
services:
	# ...
	my-tcp-app:
    image: traefik/whoamitcp:v0.2.1
    labels:
      - "traefik.tcp.routers.my-tcp-app.rule=HostSNI(`tcp-example.com`)"
      - "traefik.tcp.routers.my-tcp-app.tls=true"

What about passthrough?

Sometimes your services handle TLS by themselves. In such cases, Traefik Proxy must not terminate the TLS connection but forward the request as is to these services. To configure this passthrough, you need to configure a TCP router, even if your service handles HTTPS.
version: "3.9"
services:
	# ...
	my-tcp-app:
    image: traefik/whoamitcp:v0.2.1
    labels:
      - "traefik.tcp.routers.my-tcp-app.rule=HostSNI(`tcp-example.com`)"
      - "traefik.tcp.routers.my-tcp-app.tls.passthrough=true"

What about mTLS?

Sometimes, especially when deploying following a Zero Trust security model, you want Traefik Proxy to verify that clients accessing the services are authorized beforehand, instead of having them authorized by default. This is when mutual TLS (mTLS) comes to the rescue.
To enforce mTLS in Traefik Proxy, the first thing you do is declare a TLS Option (in this example, require-mtls) forcing verification and pointing to the root CA of your choice.
# Dynamic configuration
tls:
  options:
    require-mtls:
      clientAuth:
        clientAuthType: RequireAndVerifyClientCert
        caFiles:
          - /certs/rootCA.crt
Now that this option is available, you can protect your routers with tls.options=require-mtls@file. Once done, every client trying to connect to your routers will have to present a certificate signed with the root certificate authorities configured in the caFiles list.
version: "3.9"
services:
	# ...
	my-app:
    image: traefik/whoami:v1.7.1
    labels:
      - "traefik.http.routers.my-app.rule=Host(`example.com`)"
      - "traefik.http.routers.my-app.tls.options=require-mtls@file"

TLS from Traefik Proxy to your service

Not only can you configure Traefik Proxy to enforce TLS between the client and itself, but you can configure in many ways how TLS is operated between Traefik Proxy and the proxied services.
Leveraging the serversTransport configuration, you can define the list of trusted certificate authorities, a custom server name, and, if mTLS is required, what certificate it should present to the service.
# Dynamic configuration
http:
  serversTransports:
    my-transport:
			serverName: "traefik.example.com"
      certificates:
        - certFile: path/to/cert.crt
          keyFile: path/to/cert.key
Later on, you can bind that serversTransport to your service:
version: "3.9"
services:
	# ...
	my-app:
    image: traefik/whoami:v1.7.1
    labels:
      - "traefik.http.routers.my-app.rule=Host(`example.com`)"
      - "traefik.http.services.my-app.loadbalancer.serverstransport=my-transport@file"

Mix and match

Traefik Proxy allows for many TLS options you can set on routers, entrypoints, and services (using server transport). Mixing and matching these options fits such a wide range of use cases that I'm sure it can tackle any advanced or straightforward setup you'll need.
  • Do you want to serve TLS with a self-signed certificate?
  • Are you're looking to get your certificates automatically based on the host matching rule
  • Would you rather terminate TLS on your services?
  • Do you require mTLS from the client?
  • Do you extend this mTLS requirement to the backend services?
Traefik Proxy covers that and more. And before you ask for different sets of certificates, let's be clear — the definitive answer is, absolutely!

Where to go next?

Hopefully, this article sheds light on how to configure Traefik Proxy 2.x with TLS. If there are missing use cases or you have any questions, please don't hesitate to let us know on Traefik community forum!
If you're interested in learning more about using Traefik Proxy as an ingress proxy and load balancer, watch our workshop "Advanced Load Balancing with Traefik Proxy".
In the meantime — happy networking!

Related Posts

;