Introduction
We are going to install traefik proxy in a docker container. The traefik proxy is going to use Let’s encrypt to get certificates(using cloudflare for the domain verifications). We are also going to setup DNS records to point to our traefik proxy which will inturn re-route the traffic to its respective domain.
With this setup, I will be able to get free wild card certificates for all our self hosted services while using cloudflare as our local DNS to route to our services that are going to protected with let’s encrypts secure certificates!!!
The following is the file structure that I have used, we can modify the structure, but doing so, we would also need to specify the said changes in the config and the docker compose file where we are mounting these changes.
1
2
3
4
5
6
7
./traefik
├── data
│ ├── acme.json
│ ├── config.yml
│ └── traefik.yml
└── cf_api_token.txt
└── docker-compose.yml
Setting up my environment.
Started by creating the folders above for the docker compose and the files that needs to be mounted to the docker container.
1
2
3
4
5
6
7
8
9
10
11
## For the docker compose. I have all the docker container files in a docker-volumes folder.
mkdir docker_volumes
cd docker_volumes
## To hole the traefik files
mkdir traefik
cd traefik
## Create a compose file for creating the container
touch docker-compose.yaml
nano docker-compose.yaml
My docker compose files looks something like (traefik using .yml syntax for its config files)
docker-compose.yml
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
43
44
45
46
47
48
49
50
51
52
53
54
version: "3.8"
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
# - 443:443/tcp # Uncomment if you want HTTP3
# - 443:443/udp # Uncomment if you want HTTP3
environment:
CF_DNS_API_TOKEN_FILE: /run/secrets/cf_api_token # note using _FILE for docker secrets
# CF_DNS_API_TOKEN: ${CF_DNS_API_TOKEN} # if using .env
TRAEFIK_DASHBOARD_CREDENTIALS: ${TRAEFIK_DASHBOARD_CREDENTIALS}
secrets:
- cf_api_token
env_file: .env # use .env
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro # traefik config file
- ./data/acme.json:/acme.json # this is needed with write permissions to update or write the certificate
# - ./data/config.yml:/config.yml:ro # This is to use traefik for any exiting services we have already deployed. Detailed info below.
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.entrypoints=http"
- "traefik.http.routers.traefik.rule=Host(`mysubdomain.domain.example.com`)"
- "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_DASHBOARD_CREDENTIALS}"
- "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(`mysubdomain.domain.example.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=subdomain.example.com"
- "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.subdomain.example.com"
- "traefik.http.routers.traefik-secure.service=api@internal"
secrets:
cf_api_token:
file: ./cf_api_token.txt
networks:
proxy:
external: true
Now, I am going to create an acme.json file where traffic can write or update the certificates. Had to make sure that the acme file is editable.
1
2
3
4
5
6
7
mkdir data
cd data
touch acme.json
chmod 600 acme.json
## Create a traefik file for its configuration/. By default, traefik will look for this file.
nano traefik.yml
The above created traefik file contains the configuration for installing and configuring traefik, Which in my case looks something like,
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
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: ## Commented this out initially to first have the treafik up and running. Once the traefik instance is up, we can add more services to it using this config.
# filename: /config.yml
certificatesResolvers:
cloudflare: ## I am using cloudflare as my local DNS and for DNS challange and verification,
acme:
email: <example.email.com>
storage: acme.json
caServer: https://acme-v02.api.letsencrypt.org/directory # prod (default) has a rate limit. So, for testing and intial setup, i have used the below staging cert.
# caServer: https://acme-staging-v02.api.letsencrypt.org/directory # staging
dnsChallenge:
provider: cloudflare
#disablePropagationCheck: true # uncomment this if you have issues pulling certificates through cloudflare, By setting this flag to true disables the need to wait for the propagation of the TXT record to all authoritative name servers.
#delayBeforeCheck: 60s # uncomment along with disablePropagationCheck if needed to ensure the TXT record is ready before verification is attempted
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
We are also going to create a network called proxy for the container to use, as we have indicated so in the compose file.
1
docker create network proxy
Now, to store the cloudflare API Token, which will be used to verify our domain, I have created a folder cf_api_token.txt
Traefik Login Credentials
To generate the traefik login credentials, I have utilized, htpasswd
, which can be installed by installing apache2-utils
. To generate a password using this htpasswd
1
echo $(htpasswd -nB <userName>) | sed -e s/\\$/\\$\\$/g
Now, I have copied the password along with the username in a .env
file, which will also be mounted along with all the other files to the docker container. While mounting, the credentials should resolve to TRAEFIK_DASHBOARD_CREDENTIALS
variable, which is what we have specified in the compose file.
Now that I have all the configurations, I have created the docker container using docker compose
1
docker compose up -d ## For the initial creation of the container
Troubleshooting.
I have faced some issues, initially or after changing the cert url of cloudflare from staging to production. The follwoing steps are some steps I used to resolve the issues,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker ps ## To check if the container has been spinned up properly without any issues.
docker logs <container_name> ## Usually, if traefik is running successfully, we should not see any logs. if you have any logs, then something went wrong
docker exec -it <container_name> /bin/sh ## ssh into the container to check if all the files and secrets are mounter correctly
## In the docker container
top ## To see the services running
ls ## Check for the higher level files mounted
cat acme.json ## Should have a certificate same as in the server
cat traefik.yml ## Should have the same configuration
cat /run/secrets/cf_api_token ## Should echo out cloudflare's api token we created
echo env | grep -i TRAEFIK
## While changing out the staging certificate to production, clear out the acme file
rm acme.json
touch acme.json
chmod 600 acme.json
## Recreating docker container
docker compose up -d --force-recreate
DNS Records
I have created a central A record that is pointing to my docker container and using this A record, I have created CNAME records for traefik that are mentioned in the compose file. This allows to change the IP of traefik whithout changing all the IPs of services that are associated to traefik.
Once the changes have been made, I have verified the name resolution
1
nslookup <subdomain.domain.example.com>
Adding External Routes
As mentioned above, I already have a few service up and running that i want to resolve using traefik. For this, I have to uncommented the config file in compose and traefik file. Now, I have also create this config file which looks something like
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
43
44
45
46
47
48
49
50
51
http:
#region routers
routers:
proxmox:
entryPoints:
- "https"
rule: "Host(`proxmox.subdomain.example.com`)"
middlewares:
- default-headers
- https-redirectscheme
tls: {}
service: proxmox
pihole:
#endregion
#region services
services:
proxmox:
loadBalancer:
servers:
- url: "<proxmox_url>"
passHostHeader: true
#endregion
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:
ipAllowList:
sourceRange: # IPs that we want to allow
- "10.0.0.0/8"
secured:
chain:
middlewares:
- default-whitelist
- default-headers
recreated the container again, one last time.