Traefik is a great “cloud” router that is perfect for use in a development environment to route traffic to different Docker hosts, but when I came to try and add some self-signed certificates to it so that my development environment more realistically mirrored the staging and production environments I ran into some problems and the Traefik documentation, whilst good, unfortunately, is a little vague around the subject of certificates in a general, so it took some Googling and putting various different things together to come up with a final solution that works well.
I already had several development sites set up using Traefik as the router to send traffic to the appropriate site based on the domain name, and after reading the docs, I thought it should be as easy as:
- Creating some self-signed certificates using
- Adding and trusting the certificates to the certificate store on my Mac.
- Telling Traefik to use the certificates where appropriate.
With this, I was almost right, but I discovered a few “gotchas” along with way.
Whilst it is easy enough to generate a self-signed certificate using
openssl, it seems it is more difficult to get the browser to trust that certificate than it was previously, and in the case of Chrome, nearly impossible. There is, however, a very nice, free open source solution to this by the way of mkcert, which is also cross-platform, so can be used on Windows, Mac and Linux. This handy little tool will add itself as a certificate authority to your computers certificate store and then allow you to generated certificates, signed by its own “authority”, which means your browser will believe it is a “true” and “trusted” certificate.
Simply follow the installation instructions and note there is an extra step for Firefox, and once you have it installed you can generate certificates using the command:
mkcert [list of domains]
So you could do:
mkcert www.mydomain.local www.mydomain2.local test.mydomain.local
This command will generate one certificate file that contains a certificate with these three domains listed as valid for that certificate.
It also supports wildcard domains as well, so you could make a certificate like this:
Then, you could use this certificate for any subdomain of
tasks.mydomain.local and so on.
So, now we have certificates that the browser will recognise and trust, we need to add them to Traefik, and this is where the documentation let me down.
Adding Certificate to Traefik
The way I have Traefik configured is with a very simple YAML configuration file that is bound to the Traefik docker container via a bind on startup, so the
traefik.yml on my Mac is seen by the Traefik docker container as being
/etc/traefik/traefik.yml on the container.
From reading the documentation, I was under the impression all I needed to do was:
- Bind the directory containing my certificates to a directory on the Traefik docker container.
- Add a
tls:section to my
traefik.ymlfile to declare the certificate files to Traefik on the path they were bound to in step 1.
- Add a couple of labels to the docker containers that would be using the certificate to turn on TLS and tell it which domains would be on TLS.
After doing all this, I could connect to the site on HTTPS but it kept giving me the “default certificate”, e.g. one Traefik generates automatically and not a certificate the browser sees as trusted. After much searching around, however, I found the solution. The
tls: configuration actually needs to be in a separate file, that Traefik refers to as a “dynamic configuration”. To do this, you need to tell Traefik about this “dynamic configuration” file in your main
traefik.yml file and then bind this new file into the Docker container at the point you have specified in your
traefik.yml file. So I ended up with this:
## traefik.yml ## Static configuration entryPoints: web: address: ":80" websecure: address: ":443" # Docker configuration backend providers: docker: network: "my-bridge-network" exposedByDefault: false watch: true file: filename: /etc/traefik/dynamic_conf.yml watch: true # API and dashboard configuration api: insecure: true
# Dynamic configuration tls: certificates: - certFile: "/etc/traefik/certs/www.domain1.andrew.pem" keyFile: "/etc/traefik/certs/www.domain1.andrew-key.pem" - certFile: "/etc/traefik/certs/_wildcard.domain2.andrew.pem" keyFile: "/etc/traefik/certs/_wildcard.domain2.andrew-key.pem" - certFile: "/etc/traefik/certs/www.domain3.andrew+2.pem" keyFile: "/etc/traefik/certs/www.onlytease.andrew+2-key.pem"
With the following binds:
Then, on each of the web site containers I added the following labels:
So, for example, it might look like this if the
mydomain and the site was served on the domains
After this is done and everything has been restarted, you can then access your development sites on HTTPS and with no browser warning. If you need to add more in future, it is as simple as generating another certificate, adding it to the
dynamic_conf.yml file and adding the labels to the container.