Vaultwarden : self-hôte ton gestionnaire de mots de passe
Tes mots de passe sont la clé de ton château. Les confier à un service tiers, c’est faire confiance à quelqu’un d’autre pour garder la clé. Vaultwarden te permet d’héberger un gestionnaire de mots de passe compatible Bitwarden, léger, en Docker, sur ton propre serveur. Voici comment faire, proprement.
Pourquoi self-héberger son gestionnaire de mots de passe ?
Le fiasco LastPass a montré que même les spécialistes du mot de passe se font défoncer. Self-héberger, c’est :
- Contrôle total : tes données ne quittent jamais ton infrastructure
- Pas de dépendance : si le service public est down ou compromis, tu n’es pas impacté
- Confidentialité : aucun tiers ne peut accéder à tes vaults, même en théorie
- Customisation : intégration avec ton réseau, ton reverse proxy, ta stack de monitoring
Vaultwarden, c’est quoi ?
Vaultwarden (anciennement Bitwarden_rs) est une implémentation communautaire du serveur Bitwarden, écrite en Rust. Avantages :
- Léger : ~50 Mo de RAM vs ~2 Go pour le serveur Bitwarden officiel
- Compatible : même clients (browser extension, mobile, desktop)
- Fonctionnalités premium gratuites : 2FA, attachments, organisations
- Une seule binaire, pas besoin de MSSQL + les 10 conteneurs officiels
Déploiement Docker Compose
Structure des fichiers
1
2
3
4
5
/opt/vaultwarden/
├── docker-compose.yml
├── .env
└── data/
└── (persistent data)
docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
version: "3.8"
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
env_file: .env
volumes:
- ./data:/data
ports:
- "127.0.0.1:8080:80" # Écoute en local uniquement
networks:
- vault-net
networks:
vault-net:
driver: bridge
Variables d’environnement (.env)
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
# /opt/vaultwarden/.env
# Domaine (OBLIGATOIRE — doit correspondre au certificat TLS)
DOMAIN=https://vault.tondomaine.com
# Désactiver l'inscription publique (important !)
SIGNUPS_ALLOWED=false
# Désactiver l'invitation publique
INVITATIONS_ALLOWED=true
# 2FA obligatoire pour les connexions
REQUIRE_DEVICE_EMAIL=true
# Limites anti-brute force
FAILED_LOGIN_ATTEMPTS_RATE=5
DISABLE_ADMIN_TOKEN=false
# Token admin (changer immédiatement — générer avec : openssl rand -base64 48)
ADMIN_TOKEN=<ton-token-ici>
# SMTP pour les notifications et 2FA par email
SMTP_HOST=smtp.tondomaine.com
SMTP_FROM=vault@tondomaine.com
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USERNAME=vault@tondomaine.com
SMTP_PASSWORD=<mot-de-passe-smtp>
# Logging
LOG_LEVEL=warn
EXTENDED_LOGGING=false
# WebSocket pour les sync temps réel
WEBSOCKET_ENABLED=true
Reverse proxy Nginx
Vaultwarden ne doit jamais être exposé directement. Utilise un reverse proxy avec TLS.
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
# /etc/nginx/sites-available/vault.tondomaine.com
server {
listen 443 ssl http2;
server_name vault.tondomaine.com;
ssl_certificate /etc/letsencrypt/live/vault.tondomaine.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.tondomaine.com/privkey.pem;
# Headers de sécurité
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
client_max_body_size 50M; # Pour les attachments
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket pour les notifications temps réel
location /notifications/hub {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# Redirection HTTP → HTTPS
server {
listen 80;
server_name vault.tondomaine.com;
return 301 https://$host$request_uri;
}
Pour la configuration TLS complète, voir le guide Reverse Proxy Nginx & TLS.
Accès distant sécurisé
Exposer Vaultwarden sur Internet, c’est un risque. Options par ordre de préférence :
- VPN uniquement : accessible uniquement via WireGuard — le plus sûr
- Cloudflare Tunnel : pas besoin d’ouvrir de port, protection WAF incluse
- Exposition directe via reverse proxy — fonctionnel mais plus exposé
1
2
3
# Si accès via WireGuard uniquement :
# Modifier le nginx pour écouter sur l'IP WireGuard
listen 10.0.0.1:443 ssl http2;
Activation du 2FA
Vaultwarden supporte plusieurs méthodes 2FA :
| Méthode | Sécurité | Commodité |
|---|---|---|
| TOTP (Google Authenticator, Aegis) | Élevée | Bonne |
| WebAuthn / FIDO2 (YubiKey) | Très élevée | Excellente |
| Moyenne | Bonne | |
| Duo Security | Élevée | Bonne |
1
2
3
4
5
# Activer WebAuthn dans .env
WEBSOCKET_ENABLED=true
# Les utilisateurs activent le 2FA depuis :
# Settings → Security → Two-step login
Pour aller plus loin sur l’authentification moderne, voir Passkeys & FIDO2 : la mort du mot de passe.
Stratégie de backup
Un gestionnaire de mots de passe sans backup, c’est une bombe à retardement.
Backup automatique avec BorgBackup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# /opt/vaultwarden/backup.sh
BACKUP_DIR="/backups/vaultwarden"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
VW_DIR="/opt/vaultwarden"
# Backup SQLite + attachments + config
mkdir -p "$BACKUP_DIR"
tar czf "$BACKUP_DIR/vaultwarden_${TIMESTAMP}.tar.gz" \
-C "$VW_DIR" data/ .env docker-compose.yml
# Rotation : garder 30 jours
find "$BACKUP_DIR" -name "vaultwarden_*.tar.gz" -mtime +30 -delete
echo "[$(date)] Backup Vaultwarden terminé"
1
2
# Cron quotidien à 3h du matin
echo "0 3 * * * root /opt/vaultwarden/backup.sh" >> /etc/crontab
Pour une stratégie de backup complète et chiffrée, voir BorgBackup : stratégie de backup.
Maintenance
1
2
3
4
5
6
7
8
9
10
11
12
13
# Mise à jour
cd /opt/vaultwarden
docker compose pull
docker compose up -d
# Vérifier les logs
docker compose logs -f --tail=50
# Backup avant mise à jour
./backup.sh
# Healthcheck
curl -f http://127.0.0.1:8080/alive || echo "VAULTWARDEN DOWN"
Endurcissement supplémentaire
- Rate limiting Nginx sur
/api/identity/connect/token(anti brute force) - Fail2ban/CrowdSec : jail personnalisée pour les échecs Vaultwarden
- Monitoring : exporter les métriques vers Prometheus & Grafana
- SSH durci sur le serveur hôte : Hardening SSH
- Inscription désactivée après le setup initial
1
2
3
4
5
6
7
# Rate limiting Nginx pour Vaultwarden
limit_req_zone $binary_remote_addr zone=vault_auth:10m rate=5r/m;
location /api/identity/connect/token {
limit_req zone=vault_auth burst=3 nodelay;
proxy_pass http://127.0.0.1:8080;
}
Checklist
- Docker Compose déployé et fonctionnel
SIGNUPS_ALLOWED=falseaprès création des comptes- Reverse proxy TLS configuré
- 2FA activé pour tous les utilisateurs
- Backup automatique en place avec rotation
- Accès restreint (VPN ou Cloudflare Tunnel recommandé)
- Rate limiting sur les endpoints d’authentification
- Monitoring de la santé du service
ADMIN_TOKENchangé et stocké hors serveur- Stratégie de récupération d’urgence documentée
Références :
Tes mots de passe sont sur un Google Sheet ? Contacte-moi pour migrer proprement.