Secure everything using Traefik, Let's Encrypt, and Docker
Install and configure a Traefik reverse proxy while also auto-generating a wildcard SSL certificate using Cloudflare and Let’s Encrypt.
Code here: https://github.com/nateleavitt/traefik-docker-ssl
Description
This is going to setup and use Traefik as a reverse proxy for all your other services (containers). SSL termination will happen on Traefik, which will then forward the traffic to the requested service. Certificates will be auto-generated with Let’s Encrypt, with Cloudfront as the DNS verification provider.
Prereqs
- You need a cloudflare account
- You need a registered domain which is also using the cloudflare nameservers
- (suggested) Internal DNS
Setup and Config
Cloudflare setup
First you need to get a token or api key from Cloudflare. I would suggest a token. Once logged in, click on your profile, and select My Profile
> API Tokens
> Create Token
. Make sure you give it the ability to Edit
the Dns zone, and also I add SSL & Certificates
. Once you get the token, make sure to copy it somewhere safe.
Env setup
I have set this project up to use a .env
file for secrets. You can change this, as it isn’t the most secure way of handling secrets. But to do this, rename the .env.example
file to .env
and add your token.
1
2
3
4
5
CF_DNS_API_TOKEN=your-token-here
# If you chose to use an email and api key comment the line above and
# uncomment the 2 lines below. I would suggest setting up a token.
# [email protected]
# CF_API_KEY=your-api-key-here
Traefik Dashboard User Setup
In order to access the traefik dashboard, you will need to setup a username and password. Make sure to replace <USER>
and <PASSWORD>
with your own made up values. This can be done using the following:
1
echo $(htpasswd -nb "<USER>" "<PASSWORD>") | sed -e s/\\$/\\$\\$/g
Copy the output into the docker-compose.yml
file in the section traefik.http.middlewares.traefik-auth.basicauth.users=your-user:your-hashed-password
. Replace your-user
and your-hashed-password
with the values you created above.
Traefik Config
This compose file starts traefik, it’s dashboard, and also configures your wildcard domain settings. Some things to note in this file; First, you’ll need to update the labels to include your domain. Notice I use .local
on all my domains. This is because I like to use the local subdomain of my domain for all internal services, which then makes it easy to refer to my internal services using DNS entries. Also, take note that I am creating the proxy
docker network to isolate it’s network.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
version: '3'
services:
traefik:
image: traefik:latest
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
env_file: .env
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./data/acme.json:/acme.json
- ./config:/config/:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.entrypoints=http"
- "traefik.http.routers.traefik.rule=Host(`dashboard.local.your-domain.com`)"
- "traefik.http.middlewares.traefik-auth.basicauth.users=your-user:your-hashed-password"
- "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
- "traefik.http.routers.traefik-secure.entrypoints=https"
- "traefik.http.routers.traefik-secure.rule=Host(`dashboard-host.local.your-domain.com`)"
- "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
- "traefik.http.routers.traefik-secure.tls=true"
- "traefik.http.routers.traefik-secure.tls.certresolver=cloudflare"
- "traefik.http.routers.traefik-secure.tls.domains[0].main=local.your-domain.com"
- "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.local.your-domain.com"
- "traefik.http.routers.traefik-secure.service=api@internal"
networks:
proxy:
external: true
In the data/
folder you will see some traefik config. You’ll need to update your cloudflare email below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# data/traefik.yml
api:
dashboard: true
debug: true
entryPoints:
http:
address: ":80"
http:
redirections:
entryPoint:
to: https
scheme: https
https:
address: ":443"
serversTransport:
insecureSkipVerify: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
directory: "/config/"
watch: true
certificatesResolvers:
cloudflare:
acme:
email: [email protected] # your cloudflare email
storage: acme.json
dnsChallenge:
provider: cloudflare
disablePropagationCheck: true
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
log:
level: DEBUG
Once you have configured and updated your settings, I would start traefik and watch the logs to see if the SSL certificates are being created correctly. Also pay attention to any logs/errors from Cloudflare. If you do get errors, make sure your token is providing all the necessary permissions for your DNS zone on Cloudflare.
1
docker compose up
Try logging into the dashboard by going to the url you used in the docker-compose.yml
label above. The label I use is dashboard.local.your-domain.com
. Also, you will use the username and password you configured in the User Setup section. If you can login, then things are going great up to this point, and you can move on to the next section.
Service Config
I have set this up to where you put each of your services/router denfinitions into the config
folder. Traefik will watch this folder and any time services (yml files) are added to that folder Traefik will automatically update. Below is an example service which will serve the https://hello-world.your-domain.com
address.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# hello-world.yml
http:
#routers
routers:
hello-world:
entryPoints:
- "https"
rule: "Host(`hello-world.your-domain.com`)"
middlewares:
- default-headers
- https-redirectscheme
tls: {}
service: hello-world
#endrouter
#service
services:
hello-world:
loadBalancer:
servers:
- url: "http://server-ip-address:port"
passHostHeader: true
#endservice
Make sure to included any middleware specific to your service. For example, in the default.yml
file I have included some commonly used middlewares; https-redirectscheme
, default-headers
, and default-whitelist
. In my example above you’ll see that default-headers
and https-redirectscheme
is being used.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# default.yml
http:
middlewares:
https-redirectscheme:
redirectScheme:
scheme: https
permanent: true
default-headers:
headers:
frameDeny: true
browserXssFilter: true
contentTypeNosniff: true
forceSTSHeader: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 15552000
customFrameOptionsValue: SAMEORIGIN
customRequestHeaders:
X-Forwarded-Proto: https
default-whitelist:
ipWhiteList:
sourceRange:
- "10.0.0.0/8"
- "192.168.0.0/16"
- "172.0.0.0/12"
secured:
chain:
middlewares:
- default-whitelist
- default-headers
From there feel free to add/remove services to the config folder, which will have an SSL certificate generated and then provisioned by Traefik.