feat(stacks): migrate traefik

This commit is contained in:
2026-08-14 06:21:01 +02:00
parent 9a2722def3
commit 10fff06464
8 changed files with 211 additions and 0 deletions

4
.gitignore vendored
View File

@@ -64,3 +64,7 @@ terraform.rc
*.agekey
age-key.txt
keys.txt
# Le stockage ACME de Traefik : clé de compte Let's Encrypt et clés privées de
# tous les certificats. Vit sur le serveur uniquement, jamais dans le dépôt.
acme.json

View File

@@ -0,0 +1,42 @@
services:
traefik:
image: traefik:v3.6
container_name: traefik2
restart: always
# Pas de `command:` : Traefik lit /etc/traefik/traefik.toml par défaut.
# Un `--log.level` ici écraserait silencieusement celui du toml.
ports:
- "80:80"
- "443:443"
volumes:
- ./traefik.toml:/etc/traefik/traefik.toml:ro
- ./dynamic:/etc/traefik/dynamic:ro
# Doit exister comme FICHIER avant le premier `up`, sinon Docker crée un
# répertoire à sa place et Traefik redemande tous les certificats.
- ./acme.json:/acme.json
- traefik_log:/var/log/traefik/
- /var/run/docker.sock:/var/run/docker.sock:ro
secrets:
- crowdsec_lapi_key
networks:
- traefik-proxy
labels:
- traefik.enable=true
- traefik.docker.network=traefik-proxy
- traefik.http.routers.traefik_secure.entrypoints=web-secure
- traefik.http.routers.traefik_secure.rule=Host(`chioggia.opytex.org`)
- traefik.http.routers.traefik_secure.tls.certresolver=letsencrypt
- traefik.http.routers.traefik_secure.service=api@internal
- traefik.http.routers.traefik_secure.middlewares=authentik@file
secrets:
crowdsec_lapi_key:
environment: CROWDSEC_LAPI_KEY
volumes:
traefik_log:
external: true
networks:
traefik-proxy:
external: true

View File

@@ -0,0 +1,18 @@
http:
middlewares:
authentik:
forwardAuth:
address: http://authentik:9000/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-email
- X-authentik-name
- X-authentik-uid
- X-authentik-jwt
- X-authentik-meta-jwks
- X-authentik-meta-outpost
- X-authentik-meta-provider
- X-authentik-meta-app
- X-authentik-meta-version

View File

@@ -0,0 +1,18 @@
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: true
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec:8080
# La clé elle-même est dans secrets.env.yaml. Le provider fichier de
# Traefik ne substitue aucune variable d'environnement : c'est ce
# `...File` qui permet de garder ce fichier versionnable en clair.
crowdsecLapiKeyFile: /run/secrets/crowdsec_lapi_key
logLevel: ERROR
# crowdsecAppsecEnabled: true
# crowdsecAppsecHost: crowdsec:7422
# crowdsecAppsecFailureBlock: false
# crowdsecAppsecUnreachableBlock: false

View File

@@ -0,0 +1,28 @@
[http]
[http.routers]
[http.routers.opytexorg]
entryPoints = ["web-secure"]
service = "opytexorg"
middlewares = ["opytexorgstripprefix@file", "opytexorgindex@file", "opytexerror@file"]
rule = "Host(`opytex.org`)"
[http.routers.opytexorg.tls]
certResolver = "letsencrypt"
[[http.routers.opytexorg.tls.domains]]
main = "opytex.org"
[http.services]
[http.services.opytexorg.loadBalancer]
[[http.services.opytexorg.loadBalancer.servers]]
url = "http://minio:9000"
[http.middlewares]
[http.middlewares.opytexorgindex.replacePathRegex]
regex = "^(.*)/$"
replacement = "$1/index.html"
[http.middlewares.opytexorgstripprefix.addPrefix]
prefix = "/opytex.org"
[http.middlewares.opytexerror.errors]
status = ["400-499", "500-599"]
service = "opytexorg@file"
query = "opytex.org/404.html"

View File

@@ -0,0 +1,22 @@
[http]
[http.routers]
[http.routers.raw]
entryPoints = ["web-secure"]
service = "raw"
middlewares = ["rawstripprefix@file"]
rule = "Host(`raw.opytex.org`)"
[http.routers.raw.tls]
certResolver = "letsencrypt"
[[http.routers.raw.tls.domains]]
main = "raw.opytex.org"
[http.services]
[http.services.raw.loadBalancer]
[[http.services.raw.loadBalancer.servers]]
url = "http://minio:9000"
[http.middlewares]
[http.middlewares.rawstripprefix.addPrefix]
prefix = "/raw"

View File

@@ -0,0 +1,25 @@
CROWDSEC_LAPI_KEY: ENC[AES256_GCM,data:4gTTw3pdbkmpssdRWroB8sEzibQKE3i+STir1gAh1N5W/f58gi6K8lpF8w==,iv:MUEjf0tTf5S+rg8Yb+7PlxgNqGqOGbLHwc8Gd5UAwpM=,tag:rI9tOy/Qzb3XzJgbZXOa6Q==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkeDlpb3N3amFuTTcwamFy
a0pSTDhTRExLUUxjM1ZIcmU2Rko3enM1dkZNCm9OZHdxcTBxQnB2RzFEbkNOTEJ6
WUdTR3NBeXV3KzlnM2N1YkIvbDJJQUUKLS0tIE9ZQzVKbnZES2tRdkhpUjZjQTdP
Qm1EdGR6TitNaTFVS1pyTGhGK08wbGMK9ZwsDE2ih9EI87MNqFuA65XFZBAL2akF
4ftZM3DDQsvL62DWPYi6t466zg1kH2R2zx/wz9Jil4LKY13vJgz+bQ==
-----END AGE ENCRYPTED FILE-----
recipient: age18m2zl3pwgw97djshzcf4wmhrqxksl4erqse6edkpxpkg5rcz0ujs8u4c7s
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlUS9sZXVCLzIwcE4xdXdx
K2R5eTJnTWs3NHRlbEVsMEFhSnJIdGdpS0ZzCmNmVnVqYnlUS09pK25hcFJySnRn
SS81MHpJYksyaFNUeXFEWWFPQXU2R1EKLS0tIHBnZU1OcGZsSnRCM1hIYWlIZ3hK
UGZhSjVRRldXOUI2d3lZNlM1WE9lb2cK1ByWPZ8taCHED0jQiUkldAehZ7KQYPJx
E20o7wnn45LHx1Km+R91CeNTj7NFIXCpLZrhcXo89knvL4qFNCkT+Q==
-----END AGE ENCRYPTED FILE-----
recipient: age1fh70nny5hzz8a8g9077kgfca0lhlh3wsjf4fjrv4klx5ucdtpdmsazuqdj
lastmodified: "2026-08-14T04:19:11Z"
mac: ENC[AES256_GCM,data:VVPI//syL8fm81WyyIgxLlRdNYdQr9IZT2s5Kf9TygBqFwhqooaVHiYu1uaka3gEpFf0fZkfz3iB7i/vkT+/c6MoXKSUkVrUmT2FaDEZM2WITv3C7+Nh0hrcrksWGBXXsUSBW0ZnFm7WPG/SlYoyqcF+W9oHbA/fa4nCHJY4pBk=,iv:KsPgBCPwgbYf9opXpUgd6DHqf+M2Zuu0oIBdQAA1+p4=,tag:0e9wO7H6LQU5YHjjJsClNQ==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3

View File

@@ -0,0 +1,54 @@
[global]
checkNewVersion = true
sendAnonymousUsage = false
[log]
level = "INFO"
filePath = "/var/log/traefik/traefik.log"
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web.http.redirections.entryPoint]
to = "web-secure"
scheme = "https"
[entryPoints.web-secure]
address = ":443"
[accessLog]
filePath = "/var/log/traefik/access.log"
bufferingSize = 100
[providers]
[providers.file]
directory = "/etc/traefik/dynamic"
[providers.docker]
endpoint = "unix:///var/run/docker.sock"
watch = true
exposedbydefault = false
[api]
dashboard = true
debug = false
# Sans ce bloc, `plugin.bouncer` référencé par dynamic/crowdsec.yml n'existe
# pas : le middleware crowdsec@file devient invalide et TOUS les routeurs qui
# l'utilisent tombent en erreur (404). Traefik ne lit cette configuration qu'au
# démarrage — un conteneur déjà lancé survit à sa disparition, la panne
# n'apparaît qu'à la recréation suivante. C'est arrivé le 2026-08-13.
[experimental]
[experimental.plugins.bouncer]
moduleName = "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
version = "v1.4.5"
[certificatesResolvers.letsencrypt.acme]
email = "benjamin.bertrand@opytex.org"
# Chemin ABSOLU, et monté depuis l'hôte. En relatif, le fichier atterrissait
# dans le répertoire de travail du conteneur : perdu à chaque recréation,
# avec réémission complète des certificats et plafond Let's Encrypt au bout.
storage = "/acme.json"
tlschallenge = true
[certificatesResolvers.letsencrypt.acme.httpChallenge]
entryPoint = "web"