How to handle https with docker-compose and mkcert for local development
Published on
May 21, 2021
Docker and docker-compose are now widely used in the world of web development. Since our first Hackathon in 2015, Docker became a standard on all projects:
It helps a lot to maintain our stacks and having specific configurations for local , staging or production environments.
Nevertheless, Antoine has seen a lot of Docker configurations for development environment that are quite far from the staging or production environments. That's why he wrote this article in order to help you understanding, why you need your different environments to be close enough to each other. It's the best way to fail fast on your local environment in order to avoid shipping bugs on production.
Most of the time, local Docker configuration provides access to services directly on a specific port. The holy grail is using a reverse-proxy (such as Traefik) even on development. But today I would like to focus on a very specific topic: the TLS encryption on local environment.
Prerequisites
This article was created using these versions of Docker and mkcert
docker
: 20.10.15docker-compose
: 1.28.5mkcert
: 1.4.3-1
The following configuration might work for lower versions but I didn't check.
Traefik
Before we start, we need to talk a bit about Traefik in case you never heard about it.
Basically, Traefik is a reverse proxy, which means it is the door to your platform.
It intercepts and routes every incoming request: it knows all the logic and every rule that determines which services handle which requests
(based on the path, the host, headers, and so on ...).
The cool thing with Traefik is that you don't have much configuration to write.
With only few docker-compose labels on services, you have a powerful reverse-proxy that handles the traffic of your stack.
mkcert
mkcert
is a tool to generate locally-trusted development certificates that can be shared with Traefik so it can perform a TLS encryption.
Mkcert can only be used in local environment. Public domain will require you to provide a certificate that is trusted by an external authority.
You can follow the installation guide on the mkcert documentation.
Getting started
We will use the containous/whoami
Docker image to demonstrate the usage of a TLS certificate on a docker-compose service.
mkcert certificates
First we have to create and store the TLS certificates with mkcert
in the ./certs
folder.
These certificates are locally-trusted which mean they are valid only on your machine. Therefore you should gitignore
them!
Every developer needs to generate their own certificate when they build the project.
mkdir -p certs
mkcert -cert-file certs/local-cert.pem -key-file certs/local-key.pem "app.localhost" "*.app.localhost" "domain.local" "*.domain.local"
Traefik configuration
Then create the folders: ./docker/traefik
mkdir -p docker/traefik
Create the ./docker/traefik/dynamic_conf.yaml
that allows to configure the available subdomains served by Traefik.
It also declares the path of the TLS certifcates (generated with mkcert) that will be shared with Traefik through a Docker volume.
http:
routers:
traefik:
rule: "Host(`traefik.app.localhost`)"
service: "api@internal"
tls:
domains:
- main: "app.localhost"
sans:
- "*.app.localhost"
- main: "domain.local"
sans:
- "*.domain.local"
tls:
certificates:
- certFile: "/etc/certs/local-cert.pem"
keyFile: "/etc/certs/local-key.pem"
Then you have to create the ./docker/traefik/traefik.yaml
configuration file.
global:
sendAnonymousUsage: false
api:
dashboard: true
insecure: true
providers:
docker:
endpoint: unix:///var/run/docker.sock
watch: true
exposedbydefault: false
file:
filename: /etc/traefik/dynamic_conf.yaml
watch: true
log:
level: DEBUG
format: common
entryPoints:
web:
address: ':80'
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ':443'
I am not going to explain every section of this file but here are some details about the providers
and entryPoints
sections:
providers
This section tells Traefik what and how services are accessible from the outside. It is basically the routing configuration.
Here we have 2 different providers:
docker
: Use containers labels to retrieve routing configuration (more information on the next section).file
: Load a configuration file. Here thedynamic_conf.yaml
we created earlier.
entryPoints
This section is quite simple and allows to redirect every incoming request on http
(port 80) to https
(port 443).
Now you can be sure that Traefik will not let http
requests go further to your services.
docker-compose file
In the docker-compose.dev.yml
file, put:
version: '3.8'
services:
reverse-proxy:
image: traefik:v2.4.7
container_name: traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik=true"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
ports:
- 80:80
- 443:443
restart: unless-stopped
security_opt:
- no-new-privileges:true
volumes:
- ./docker/traefik/dynamic_conf.yaml:/etc/traefik/dynamic_conf.yaml:ro
- ./docker/traefik/traefik.yaml:/etc/traefik/traefik.yaml:ro
- ./certs:/etc/certs:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
whoami:
image: containous/whoami:latest
container_name: whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.app.localhost`)"
- "traefik.http.routers.whoami.tls=true"
restart: unless-stopped
networks:
proxy:
external: true
Here you will find the declarations of services in your stack. Here are some more details about this configuration:
reverse-proxy
- ports: Binds both ports 80 and 443 to the host. Notice that the
whoami
service does not expose any port. All incoming requests to your stack will be handled by Traefik. - volumes: We share configuration files, certificates and the Docker socket (Traefik requires access to the Docker socket to get its dynamic configuration).
whoami
We define 3 labels:
traefik.enable=true
: Explicitly tells Traefik to expose this container.traefik.http.routers.whoami.rule=Host('whoami.app.localhost')
: The domain the service will respond to.traefik.http.routers.whoami.tls=true
: Enable HTTPS on this route.
Run the stack
docker-compose -f docker-compose.dev.yml up -p
And voilà! You should now be able to go to https://whoami.app.localhost.
Any questions or feedback? Ping us on Twitter ! https://twitter.com/KNPLabs
Comments