feat(vault): migre vault.opytex.org vers stacks/
Première stack migrée vers le nouveau modèle. Le nom du dossier est
conservé à l'identique : il détermine le nom du projet Compose
(vaultopytexorg) et donc le préfixe des volumes, ce qui permet de
reprendre les données existantes.
- env_file : les 22 variables non sensibles, en clair et diffables
- secrets.env.yaml : ADMIN_TOKEN et SSO_CLIENT_SECRET, chiffrés
- env_file: .env supprimé ; les deux secrets passent par ${VAR:?} pour
échouer franchement si `sops exec-env` est oublié
Corrections au passage :
- priority=10 sur le router principal. Sans elle Traefik la déduisait de
la longueur de la règle (23), contre 26 sur le router /admin : trois
caractères de marge avant que /admin ne perde le forward auth.
- backup.enable sur le volume, qui n'était couvert par aucune sauvegarde.
À compléter dans MANUAL_VOLUMES : un label n'agit pas sur un volume
déjà créé.
- version d'image figée (1.35.7) au lieu de latest, détectable par Renovate
- security_opt: no-new-privileges
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit was merged in pull request #1.
This commit is contained in:
86
stacks/vault.opytex.org/docker-compose.yml
Normal file
86
stacks/vault.opytex.org/docker-compose.yml
Normal file
@@ -0,0 +1,86 @@
|
||||
# ============================================================
|
||||
# Vaultwarden — vault.opytex.org
|
||||
# ============================================================
|
||||
# Déploiement :
|
||||
# sops exec-env secrets.env.yaml 'docker compose up -d'
|
||||
#
|
||||
# Le nom du dossier détermine le nom du projet Compose (`vaultopytexorg`,
|
||||
# les points étant retirés) et donc le préfixe des volumes. Le conserver
|
||||
# à l'identique est ce qui permet de reprendre les données existantes.
|
||||
#
|
||||
# Configuration non sensible : env_file (versionné, en clair).
|
||||
# Secrets : secrets.env.yaml (chiffré), injectés par `sops exec-env`.
|
||||
|
||||
services:
|
||||
vaultwarden:
|
||||
# Version en dur, jamais via une variable : c'est ce qui permet à Renovate
|
||||
# de détecter les montées de version et d'ouvrir une PR.
|
||||
image: vaultwarden/server:1.35.7
|
||||
container_name: vaultwarden
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
volumes:
|
||||
- vaultwarden_data:/data
|
||||
env_file:
|
||||
- env_file
|
||||
environment:
|
||||
# Les deux seules valeurs sensibles. `${VAR:?}` fait échouer le
|
||||
# démarrage si `sops exec-env` a été oublié, au lieu de recréer le
|
||||
# conteneur avec des valeurs vides.
|
||||
#
|
||||
# Régénérer le token admin en hash argon2 :
|
||||
# docker run --rm -it vaultwarden/server:1.35.7 /vaultwarden hash --preset owasp
|
||||
ADMIN_TOKEN: ${ADMIN_TOKEN:?}
|
||||
SSO_CLIENT_SECRET: ${SSO_CLIENT_SECRET:?}
|
||||
networks:
|
||||
- traefik-proxy
|
||||
- smtp
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network=traefik-proxy
|
||||
|
||||
# --- Router principal (UI + API clients Bitwarden) ---
|
||||
# `priority` explicite : sans elle, Traefik la déduit de la longueur de
|
||||
# la règle — 23 pour Host(`vault.opytex.org`), contre 26 forcés sur le
|
||||
# router /admin. Trois caractères de marge seulement : un domaine plus
|
||||
# long ferait passer ce router devant, et /admin perdrait le forward auth.
|
||||
- traefik.http.routers.vaultwarden.entrypoints=web-secure
|
||||
- traefik.http.routers.vaultwarden.rule=Host(`vault.opytex.org`)
|
||||
- traefik.http.routers.vaultwarden.priority=10
|
||||
- traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt
|
||||
- traefik.http.routers.vaultwarden.service=vaultwarden
|
||||
- traefik.http.routers.vaultwarden.middlewares=crowdsec@file
|
||||
|
||||
# --- /admin : CrowdSec + forward auth Authentik ---
|
||||
# Porte de secours si le SSO tombe (SSO_ONLY=true côté env_file).
|
||||
- traefik.http.routers.vaultwarden-admin.entrypoints=web-secure
|
||||
- traefik.http.routers.vaultwarden-admin.rule=Host(`vault.opytex.org`) && PathPrefix(`/admin`)
|
||||
- traefik.http.routers.vaultwarden-admin.priority=26
|
||||
- traefik.http.routers.vaultwarden-admin.tls.certresolver=letsencrypt
|
||||
- traefik.http.routers.vaultwarden-admin.service=vaultwarden
|
||||
- traefik.http.routers.vaultwarden-admin.middlewares=crowdsec@file,authentik@file
|
||||
|
||||
# --- Callback Authentik (outpost) ---
|
||||
- traefik.http.routers.vaultwarden-outpost.entrypoints=web-secure
|
||||
- traefik.http.routers.vaultwarden-outpost.rule=Host(`vault.opytex.org`) && PathPrefix(`/outpost.goauthentik.io/`)
|
||||
- traefik.http.routers.vaultwarden-outpost.priority=25
|
||||
- traefik.http.routers.vaultwarden-outpost.tls.certresolver=letsencrypt
|
||||
- traefik.http.routers.vaultwarden-outpost.service=vaultwarden-outpost-svc
|
||||
|
||||
- traefik.http.services.vaultwarden.loadbalancer.server.port=80
|
||||
- traefik.http.services.vaultwarden-outpost-svc.loadbalancer.server.url=http://authentik:9000
|
||||
|
||||
volumes:
|
||||
# ATTENTION : les labels de volume ne s'appliquent qu'à la CRÉATION. Le volume
|
||||
# `vaultopytexorg_vaultwarden_data` existe déjà et n'héritera PAS de ce label :
|
||||
# il faut l'ajouter à MANUAL_VOLUMES dans backup.conf.
|
||||
vaultwarden_data:
|
||||
labels:
|
||||
backup.enable: "true"
|
||||
|
||||
networks:
|
||||
traefik-proxy:
|
||||
external: true
|
||||
smtp:
|
||||
external: true
|
||||
54
stacks/vault.opytex.org/env_file
Normal file
54
stacks/vault.opytex.org/env_file
Normal file
@@ -0,0 +1,54 @@
|
||||
# =============================================================================
|
||||
# VAULTWARDEN — configuration NON sensible
|
||||
# =============================================================================
|
||||
# Versionné en clair, volontairement : cette configuration doit rester
|
||||
# relisible et diffable sans déchiffrer quoi que ce soit.
|
||||
#
|
||||
# Les deux valeurs sensibles (ADMIN_TOKEN, SSO_CLIENT_SECRET) sont dans
|
||||
# secrets.env.yaml et injectées par `sops exec-env`.
|
||||
#
|
||||
# Ce fichier ne contient QUE ce que le conteneur consomme. Ce qui pilote le
|
||||
# compose lui-même — version d'image, règles Traefik — est écrit en dur dans
|
||||
# docker-compose.yml : `env_file:` n'alimente pas l'interpolation ${...}, et
|
||||
# une version en dur est directement détectée par Renovate.
|
||||
# =============================================================================
|
||||
|
||||
# --- Domaine ---
|
||||
DOMAIN=https://vault.opytex.org
|
||||
|
||||
# --- Inscriptions ---
|
||||
# false = seuls les comptes invités via admin peuvent s'inscrire
|
||||
SIGNUPS_ALLOWED=false
|
||||
SIGNUPS_VERIFY=true
|
||||
|
||||
# --- SSO OpenID Connect (Authentik) ---
|
||||
# SSO_ONLY=true : désactive le login natif Bitwarden, tout passe par Authentik
|
||||
SSO_ENABLED=true
|
||||
SSO_ONLY=true
|
||||
SSO_AUTHORITY=https://sso.opytex.org/application/o/vaultwarden/
|
||||
SSO_CLIENT_ID=KGGkButSscxfnbo6KDAH5aj7VHMOcvwtELSehGPl
|
||||
SSO_SCOPES=email profile offline_access
|
||||
SSO_PKCE=true
|
||||
|
||||
# Pour faire marcher avec le sso
|
||||
SSO_ORGANIZATIONS_INVITE=true
|
||||
SSO_ORGANIZATIONS_ALL_COLLECTIONS=true
|
||||
|
||||
# --- Base de données ---
|
||||
# SQLite dans le volume vaultwarden_data. Pas de service db, pas de dump :
|
||||
# la sauvegarde repose entièrement sur celle du volume.
|
||||
DATABASE_URL=/data/db.sqlite3
|
||||
|
||||
# --- SMTP (relais interne, réseau `smtp`) ---
|
||||
SMTP_HOST=postfix
|
||||
SMTP_FROM=noreply@opytex.org
|
||||
SMTP_FROM_NAME=Vaultwarden
|
||||
SMTP_SECURITY=off
|
||||
SMTP_PORT=25
|
||||
|
||||
# --- Logs & sécurité ---
|
||||
LOG_LEVEL=warn
|
||||
EXTENDED_LOGGING=true
|
||||
IP_HEADER=X-Forwarded-For
|
||||
LOGIN_RATELIMIT_MAX_BURST=5
|
||||
LOGIN_RATELIMIT_SECONDS=60
|
||||
26
stacks/vault.opytex.org/secrets.env.yaml
Normal file
26
stacks/vault.opytex.org/secrets.env.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
ADMIN_TOKEN: ENC[AES256_GCM,data:PohQt8NlNAlUeWMdvqDdkUmHKJoqkLFE5bF3gZPvvDzxtaLWvtIdnnSkf/3mYJR+CSWjiObM6HgjhZ/WjzeR4324/bWVy5xXwH+o1TGXzXLtCruMrLPZFZDC99tmORa571gZ0DluRPI5EOEcYYQx3AQqggTVow==,iv:o4K3ydAFhFEo/aGhZSYdAWnJCqRGgg9p9zcbhPxzvvM=,tag:zH2t/bxKYi+C/XXU/j8z6Q==,type:str]
|
||||
SSO_CLIENT_SECRET: ENC[AES256_GCM,data:elGebRAUOJemc0DGyCMS8f2+qU5CJZYW1Cu51936m7XNFy0FQZltJrtpSJm952UHtuoYvN9/TGKXFABEPs9R3vfb9EJ3jgV+Jw/Lvwa+HI2UqwQsxRPsJ11ihRAQuQRmIia+nzy0cH2/Kt8sNRxQhw/tR/Up70J10Y8MoXm5jCg=,iv:6D2YpUI0GlqsPDI5BeAu8hFv7LWJDAfJVDmXay14/38=,tag:fxxn9Qi3oJt7qBciG+Ct3g==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOS2lpaE5RNlZmcjNLQWFh
|
||||
YzRJalZaRC9TRVFnUlNGc21WdTJGVkpUMlFJCjNHVFp1V3N1b3Nla0Z2Q1NuaHZQ
|
||||
NXpHSnFTTFpvVVhhakh6bnBFTHBCUzgKLS0tIEQ4NzNha1NaV3cyNTZST2JwaXQw
|
||||
UWVCWWIxM2VkbkhTTWNGVERXV29yZTgKpkNFQGeW9XoS274YMeGEOUgLh1z6oT5k
|
||||
hwQMD/9EYv2npMKGEPQlrKwIUhJly8m+t2TOjUp/1YS37Bd8MbNbBA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age18m2zl3pwgw97djshzcf4wmhrqxksl4erqse6edkpxpkg5rcz0ujs8u4c7s
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxTmhYWloyd0dWZlFJdmpV
|
||||
KzI4WjhQTVBjS3R3NzRvL3B6Z2xyM09qOHlVCmlST0hOenJUb3lSN0lFMk9pOU11
|
||||
aGsvL0JWV0hJanZkN0t2VzFSWUFlT1UKLS0tIGdpY05pcmVqSllqTFk2MmJvZS9I
|
||||
cHRabXdZTjhMeUFObHRyNGN3TjFxZWsKliW1EpTLHAk1INr177ezaMgyYg1gLV8B
|
||||
X5QLAU85NtxNyb6GhYBHJprdM6Cw/RWXQoyfFig7FophJKzzXRHqdA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1fh70nny5hzz8a8g9077kgfca0lhlh3wsjf4fjrv4klx5ucdtpdmsazuqdj
|
||||
lastmodified: "2026-08-12T14:10:42Z"
|
||||
mac: ENC[AES256_GCM,data:URzoDTyKu6YtfV8fbKIK1hbyTBn1SlFOsGhZFekS3IAMS7zKAYJISFczpCvValQdDVcngYBfnPP+lgmXSxQHe6vVdo9TplCI0HIBNEtIqLJwPMXlc6hw9XTUvDnSpoB32mgIOtTNQeIlZ2T2EcuyCL44i202MmMuKFmafPUdEY4=,iv:OlaLkzxbN7isHawJ6iwFEG2L6uXU/WL6fzo9fDZ0qlI=,tag:CK2Dt2MME88Sz59tGzWi6w==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.3
|
||||
Reference in New Issue
Block a user