-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create two stack examples to show setup with multiple instances and related configuration.
- Loading branch information
Showing
1 changed file
with
43 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,106 +1,64 @@ | ||
# swarm-certbot-traefik | ||
|
||
[Traefik Proxy](https://doc.traefik.io/traefik/v2.11/) community edition does not really support Let's Encrypt in a serious way for **docker swarm**. If you have multiple instances of traefik with [letsencrypt](https://doc.traefik.io/traefik/https/acme/) support enabled, they would all start to generate same certificates, overwriting `acme.json` storage and exhausting the limits very quickly. | ||
[Traefik Proxy](https://doc.traefik.io/traefik/v2.11/) community edition does not really support Let's Encrypt in a serious way for **docker swarm**. If you have multiple instances of traefik with [letsencrypt](https://doc.traefik.io/traefik/https/acme/) support enabled, they would all start to generate same certificates, overwriting `acme.json` storage and exhausting the limits very quickly if things go wrong. | ||
|
||
## Functionality | ||
This project handles certificates using separate service (here called `certbot`), which exports file with certificates in format expected by `traefik`. It uses auto-discovery by searching for `certbot.domain` labels. Please check following examples, which show both `traefik` and `certbot` labels: | ||
|
||
```yml | ||
vector: | ||
image: timberio/vector:0.36.1-alpine | ||
networks: | ||
- web | ||
deploy: | ||
labels: | ||
- "traefik.enable=true" | ||
- "traefik.docker.network=web" | ||
- "traefik.http.routers.myproject-vector.entrypoints=websecure" | ||
- "traefik.http.routers.myproject-vector.rule=Host(`sink.example.com`)" | ||
- "traefik.http.services.myproject-vector.loadbalancer.server.port=8687" | ||
- "certbot.domain=sink.example.com" | ||
``` | ||
The provided example of `manager-stack.yml` provides following functionality: | ||
## Example stacks | ||
- Traefik dashboard protected by basic authentication - replace `REPLACE-ME-USE` with your password generated by `htpasswd -nb`. | ||
- Dashboard itself is covered by created certificate - replace `traefik.example.com` with your dashboard domain. | ||
- Generic redirect from 80 to 433 with exception of ACME challenge. | ||
This project shows two examples of docker swarm stacks: | ||
- [manager_single_stack.yml](examples/manager_single_stack.yml) - for single traefik instance, | ||
- [manager_multiple_stack.yml](examples/manager_multiple_stack.yml) - with two main traefik instances (to support rolling update) running on the same host, in this case there is an `edge` traefic instance added which acts as a TCP proxy, in this setup, it is expected, that there is only one public IP without load balancing. | ||
|
||
## Functionality | ||
|
||
- Traefik dashboard is protected by basic authentication. | ||
- Generic redirect from 80 to 433 is provided (with exception of ACME challenge). | ||
- Dynamic loading of generated certificates - Treafik actually requires TLS to be in a dynamically loaded file. | ||
- Challenge webroot gets automatically routed by traefik, but only gets opened when needed. | ||
- Automatic discovery of domains, that require cerificate - use `certbot.domain` label - you can separate multiple domains with commas. | ||
- Challenge requests get automatically routed by traefik - the server serving webroot directory is only started when needed. | ||
- Renewal is performed once in a day, when date change is detected. You can force from outside using: | ||
```sh | ||
SERVICE_NAME=manager_certbot; docker exec --tty $(docker ps --format json | jq -r 'select(.Names | startswith("'$SERVICE_NAME'")) | .ID') ./renew.sh | ||
``` | ||
|
||
> [!IMPORTANT] | ||
> - When certbot fails to generate certificate it would store log into `/etc/letsencrypt/failed/$DOMAIN` - you have to delete it to get another attempt. | ||
> - The setup expects that multiple copies of traefik run on the same node (manager) to handle rolling update of traefik service. | ||
## Configuration | ||
|
||
Use `certbot.domain` label - you can separate multiple domains with commas. No attempt to reuse traefik labels was done, there might be scenarios where traefik uses wildcards, but certbot needs to know the names: | ||
|
||
```yml | ||
version: '3.8' | ||
|
||
services: | ||
|
||
traefik: | ||
image: traefik:v3.0 | ||
tty: true | ||
networks: | ||
- web | ||
ports: | ||
- "80:80" | ||
- "443:443" | ||
deploy: | ||
replicas: 2 | ||
placement: | ||
constraints: | ||
- node.role == manager | ||
update_config: | ||
delay: 10s | ||
labels: | ||
labels: | ||
- "traefik.enable=true" | ||
- "traefik.docker.network=web" | ||
- "traefik.http.middlewares.auth.basicauth.users=admin:REPLACE-ME-USE" | ||
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" | ||
- "traefik.http.routers.dashboard.entrypoints=websecure" | ||
- "traefik.http.routers.dashboard.middlewares=auth" | ||
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)" | ||
- "traefik.http.routers.dashboard.service=api@internal" | ||
- "traefik.http.routers.http-catchall.entrypoints=web" | ||
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https" | ||
- "traefik.http.routers.http-catchall.rule=HostRegexp(`.*`)" | ||
- "traefik.http.routers.http-catchall.priority=1000" | ||
- "traefik.http.services.dashboard.loadbalancer.server.port=8080" | ||
- "certbot.domain=traefik.example.com" | ||
command: | ||
- "--log.level=DEBUG" | ||
- "--api.dashboard=true" | ||
- "--providers.swarm.endpoint=unix:///var/run/docker.sock" | ||
- "--providers.swarm.exposedByDefault=false" | ||
- "--entrypoints.web.address=:80" | ||
- "--entrypoints.websecure.address=:443" | ||
- "--entrypoints.websecure.http.tls=true" | ||
- "--accesslog=true" | ||
- "--accesslog.format=json" | ||
- "--providers.file.filename=/etc/letsencrypt/traefik.yml" | ||
- "--providers.file.watch=true" | ||
volumes: | ||
- /var/run/docker.sock:/var/run/docker.sock | ||
- /etc/letsencrypt:/etc/letsencrypt | ||
- "traefik.http.routers.myproject-nginx.entrypoints=websecure" | ||
- "traefik.http.routers.myproject-nginx.rule=HostRegexp(`(.*).example.com`)" | ||
- "traefik.http.services.myproject-nginx.loadbalancer.server.port=80" | ||
- "certbot.domain=admin.example.com,www.example.com" | ||
``` | ||
certbot: | ||
image: brablc/swarm-certbot-traefik | ||
tty: true | ||
networks: | ||
- web | ||
deploy: | ||
replicas: 1 | ||
placement: | ||
constraints: | ||
- node.role == manager | ||
labels: | ||
- "traefik.enable=true" | ||
- "traefik.docker.network=web" | ||
- "traefik.http.routers.certbot.entrypoints=web" | ||
- "traefik.http.routers.certbot.rule=PathPrefix(`/.well-known/acme-challenge`)" | ||
- "traefik.http.routers.certbot.priority=1010" | ||
- "traefik.http.services.certbot.loadbalancer.server.port=80" | ||
environment: | ||
LOOP_SLEEP: 60s | ||
CERTBOT_EMAIL: [email protected] | ||
volumes: | ||
- /var/run/docker.sock:/var/run/docker.sock | ||
- /etc/letsencrypt:/etc/letsencrypt | ||
When using provided stacks please change following: | ||
networks: | ||
web: | ||
external: true | ||
``` | ||
- Replace `REPLACE-PASSWORD` with your password generated by `htpasswd -nb`. | ||
- Dashboard itself is covered by created certificate - replace `traefik.example.com` with your dashboard domain. | ||
- Replace `[email protected]` environment variable with your email for Let's Encrypt registration. | ||
|
||
> [!IMPORTANT] | ||
> - When certbot fails to generate certificate it would store log into `/etc/letsencrypt/failed/$DOMAIN` - you have to delete it to get another attempt. | ||
> - The setup expects that multiple copies of traefik run on the same node (manager) to handle rolling update of traefik service. | ||
|
||
## Credits | ||
|
||
|