Contexte et besoins
Dans le cadre de mon alternance, l’équipe a besoin d’un gestionnaire de mots de passe interne pour partager les identifiants techniques (comptes service, accès matériel, mots de passe d’admin) sans passer par un SaaS externe et sans perdre les bénéfices d’un client Bitwarden classique (extension navigateur, application mobile, partage par dossier).
Je déploie Vaultwarden : une réimplémentation libre et légère du serveur Bitwarden, écrite en Rust. Vaultwarden parle nativement le protocole Bitwarden et fonctionne donc avec tous les clients officiels, mais avec une empreinte mémoire et CPU bien plus faible — adapté à une cible hébergée sur le lab plutôt que sur du gros serveur.
Le besoin tient en quatre points : un Vaultwarden en conteneur Docker sur la VM VAULT du lab, un ADMIN_TOKEN haché en Argon2id (pas en clair), les inscriptions désactivées par défaut (on autorise uniquement les comptes de l’équipe créés depuis l’interface admin), et un volume persistant sur disque. La publication HTTPS derrière un reverse proxy fera l’objet d’une intervention ultérieure.
Rappel théorique sur Vaultwarden
Vaultwarden vs Bitwarden Server
| Aspect | Bitwarden Server officiel | Vaultwarden |
|---|---|---|
| Langage | C# (.NET) | Rust |
| Empreinte RAM | ~1 Go | ~50 Mo |
| Compatibilité clients | Tous les clients Bitwarden | Tous les clients Bitwarden (mêmes API REST) |
| Licence | AGPL v3 | AGPL v3 |
| Maintenance | Bitwarden, Inc. | Communauté + dani-garcia (auteur principal) |
Vaultwarden expose les mêmes endpoints HTTP que Bitwarden Server, donc l’extension navigateur, l’application mobile et le client desktop fonctionnent tels quels en pointant simplement vers l’URL de notre instance.
Architecture interne
Vaultwarden est un binaire unique embarquant :
- un serveur HTTP Rocket (le framework Web Rust) ;
- une base SQLite (par défaut) ou MySQL/PostgreSQL ;
- une interface Web (le Bitwarden Web Vault, version officielle, embarquée dans l’image) ;
- un endpoint d’admin protégé par un
ADMIN_TOKEN.
Côté stockage, tout vit dans /data à l’intérieur du conteneur : la base, les pièces jointes, les icônes mises en cache. C’est le seul volume à persister.
Le rôle de l’ADMIN_TOKEN
L’ADMIN_TOKEN protège la page /admin qui permet de :
- créer manuellement des comptes (utile quand
SIGNUPS_ALLOWED=false) ; - voir les utilisateurs, leurs organisations, leurs sessions ;
- modifier la configuration sans redémarrer le conteneur.
Prudence
L’ADMIN_TOKEN doit être haché en Argon2id. Vaultwarden accepte un token en clair mais affiche un NOTICE explicite dans les logs au démarrage : « You are using a plain text ADMIN_TOKEN which is insecure ». Sur une instance qui sera publiée derrière un reverse proxy, le hash est obligatoire.
Topologie
| Équipement | Rôle | Adresse |
|---|---|---|
| Passerelle du lab | Routage / DNS interne | 192.168.10.254/24 |
VM VAULT (Debian 12) | Hôte Docker pour Vaultwarden | 192.168.10.150/24 |
| Mon poste d’admin | Navigateur, SSH | DHCP sur LAN |
Prérequis
- VM Debian 12 (
VAULT) avec Docker Engine et le plugin Docker Compose installés (cf. intervention « Découverte de Docker »). - IP statique sur le segment du lab.
- Compte administrateur sudo pour piloter Docker.
Préparation du projet
Je crée l’arborescence de déploiement sous /opt/docker-compose/vaultwarden. Cette convention vient de la doc Vaultwarden et permet de retrouver chaque pile Compose au même endroit sur tous les hôtes Docker du lab.
sudo mkdir -p /opt/docker-compose/vaultwarden
cd /opt/docker-compose/vaultwardenGénération du hash ADMIN_TOKEN
L’image officielle vaultwarden/server embarque un sous-commande hash qui prompt deux fois pour le mot de passe et renvoie une chaîne Argon2id PHC prête à être collée dans la variable d’environnement.
docker run --rm -it vaultwarden/server /vaultwarden hash
Explications ligne par ligne :
docker run --rm -it vaultwarden/server: démarre un conteneur temporaire (--rm) en mode interactif (-it) basé sur l’image officielle./vaultwarden hash: invoque le binaire interne avec le sous-commandehash, qui utilise le presetbitwardend’Argon2id :m=65540(≈64 Mo de mémoire),t=3(3 itérations),p=4(4 threads en parallèle).- La chaîne renvoyée commence par
$argon2id$v=19$m=65540,t=3,p=4$...et inclut le sel et le hash. C’est la valeur à coller dansADMIN_TOKENdu fichier.env.
Important
Sur le shell, le caractère $ doit être échappé en $$ quand on copie la chaîne dans docker-compose.yml (Compose interprète $ comme une variable). Si on passe par un fichier .env, pas d’échappement : on colle la chaîne brute.
Écriture du fichier docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
ports:
- "8000:80"
volumes:
- ./data:/data
env_file:
- .envExplications ligne par ligne :
image: vaultwarden/server:latest: image officielle maintenue par dani-garcia. Sur une infra de production on pin la version (par exemple:1.35.4) pour ne pas se faire surprendre par une migration majeure ; pour cette intervention je laisselatestet je documente la version installée.restart: unless-stopped: le conteneur redémarre automatiquement au reboot de la VM, sauf si je l’ai explicitement arrêté.ports: "8000:80": Vaultwarden écoute sur le port 80 du conteneur. Je l’expose sur 8000 côté hôte pour ne pas marcher sur les pieds d’un futur reverse proxy en 80/443.volumes: ./data:/data: la base SQLite et les pièces jointes vivent dans./datacôté hôte. C’est le seul volume à sauvegarder.env_file: .env: Vaultwarden tire toute sa configuration de variables d’environnement, isolées dans.envpour ne pas polluer ledocker-compose.ymlavec des secrets.
Fichier .env
# Désactive l'auto-inscription : seuls les comptes créés via /admin sont valides
SIGNUPS_ALLOWED=false
# Désactive la création d'invitations en autonomie
INVITATIONS_ALLOWED=false
# Active la page d'administration et la protège par le hash Argon2id
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$3muZyP+yU4ve4cn0RQhNMtB0m9L2UXPXtVSeqJHfV5c$ZPvhJxT7lOuLQeqKaNl/A2qjbYmIIS4kv8pZ/Lntt10'
# URL publique de l'instance (à mettre à jour quand le reverse proxy sera en place)
DOMAIN=http://192.168.10.150:8000Explications ligne par ligne :
SIGNUPS_ALLOWED=false: aucun visiteur ne peut créer de compte depuis la page de login. Les comptes sont créés à la main par l’admin.INVITATIONS_ALLOWED=false: empêche les utilisateurs existants d’inviter d’autres personnes par mail. On garde la création de compte centralisée.ADMIN_TOKEN: le hash généré juste avant. La page/adminest accessible à quiconque possède la chaîne en clair ; Vaultwarden compare le mot de passe saisi avec le hash stocké.DOMAIN: l’URL publique de l’instance. Importante pour les liens dans les e-mails et pour le calcul des CSP côté navigateur. À remplacer parhttps://vault.lab.localune fois le reverse proxy en place.
Démarrage du conteneur
cd /opt/docker-compose/vaultwarden
docker compose up -d
docker compose logs --follow vaultwarden
Sur la première tentative, j’avais collé l’ADMIN_TOKEN en clair pour aller vite. Vaultwarden l’accepte mais salue d’un NOTICE explicite dans les logs :
[NOTICE] You are using a plain text `ADMIN_TOKEN` which is insecure.
Please generate a secure Argon2 PHC string by using `vaultwarden hash` or `argon2`.Je relance avec le hash Argon2id, puis je vérifie la sortie après redémarrage : le NOTICE a disparu, on a uniquement la bannière de démarrage et le Rocket has launched from http://0.0.0.0:80.

Premier accès Web
Depuis mon poste d’admin, j’ouvre http://192.168.10.150:8000 dans le navigateur. Je tombe sur la page de login Vaultwarden.

Le bandeau du bas indique la version embarquée du Web Vault (2026.1.1), avec la mention « A modified version of the Bitwarden Web Vault for Vaultwarden ». Le lien Créez un compte est encore présent visuellement, mais comme SIGNUPS_ALLOWED=false, toute tentative côté serveur sera rejetée.
Création du compte admin
Je passe par la page d’administration http://192.168.10.150:8000/admin :
- Je saisis l’
ADMIN_TOKENen clair (celui que j’ai utilisé pour générer le hash). - Vaultwarden m’authentifie comme admin.
- Dans Users → Invite User, j’ajoute mon adresse e-mail. Comme la couche SMTP n’est pas encore configurée, l’invitation reste en attente.
- Sur la page de login publique, je clique sur Créez un compte ; cette fois Vaultwarden l’accepte parce que mon adresse a été pré-déclarée par l’admin.
Validation du login
Je me connecte avec le compte fraîchement créé. Je tombe sur le coffre vide.

Le coffre est vide, c’est attendu. L’avatar LU confirme l’authentification de mon compte. Le panneau d’onboarding « Commencer » propose les actions classiques : importer des données, installer l’extension navigateur. Je referme l’onboarding et je passe au test de cycle complet.
Vérifications
Conteneur en up
docker compose psSortie attendue :
NAME IMAGE STATUS PORTS
vaultwarden vaultwarden/server:latest Up X minutes 0.0.0.0:8000->80/tcpPersistance du volume
ls -la ./dataOn doit voir notamment :
db.sqlite3(la base) ;attachments/,sends/,tmp/: les pièces jointes et fichiers temporaires.
# Test : on stoppe et redémarre le conteneur, le coffre doit retrouver le compte
docker compose down
docker compose up -dTest de cycle complet
- Créer un identifiant : Nouvel élément → Identifiant, je mets un test (
https://test.lab.local,user-test, mot de passe généré). - Sortir du coffre, redémarrer le conteneur.
- Se reconnecter : l’identifiant est toujours là. Le volume persiste correctement.
Problèmes rencontrés et solutions
| Symptôme | Cause | Correction |
|---|---|---|
[NOTICE] You are using a plain text ADMIN_TOKEN | Hash Argon2id non généré | Lancer docker run --rm -it vaultwarden/server /vaultwarden hash, coller la chaîne dans .env |
Impossible d’accéder à /admin (401) | ADMIN_TOKEN vide ou mal formé | Vérifier le .env, redémarrer le conteneur (les variables ne sont lues qu’au démarrage) |
| Le client Bitwarden refuse de se connecter | Mauvaise URL ou certificat invalide | Vérifier le champ DOMAIN, ouvrir l’URL dans un navigateur pour voir si le serveur répond |
Internal server error au login | Permissions du volume ./data | chown -R 1000:1000 ./data (UID par défaut côté conteneur), redémarrer |
| Création de compte refusée alors que l’admin a invité | Pas de SMTP configuré et l’utilisateur n’a pas validé son adresse | Désactiver temporairement la vérification : SIGNUPS_VERIFY=false dans le .env |
Compétences du bloc 1 mobilisées
| Compétence officielle | Mobilisation concrète |
|---|---|
| Mettre à disposition aux utilisateurs un service informatique | Vaultwarden accessible aux comptes admin du lab via http://192.168.10.150:8000. |
| Installer et configurer des éléments d’infrastructure | Stack Docker Compose, volume persistant, configuration par variables d’environnement. |
| Mettre en place et vérifier les niveaux d’habilitation associés à un service | SIGNUPS_ALLOWED=false, comptes créés depuis /admin, ADMIN_TOKEN haché Argon2id. |
| Réaliser les tests d’intégration et d’acceptation d’un service | Validation du cycle (start, login admin, création utilisateur, login utilisateur, persistance). |
Bilan
Vaultwarden 1.35.4 est en service sur la VM VAULT du lab, avec un Web Vault 2026.1.1, une base SQLite persistante dans ./data, un ADMIN_TOKEN protégé par Argon2id et l’auto-inscription désactivée. Les comptes de l’équipe peuvent être créés à la main via la page /admin puis utilisés depuis n’importe quel client Bitwarden officiel.
Trois enseignements :
- la réutilisation des clients Bitwarden officiels rend Vaultwarden très facile à adopter : l’utilisateur final ne sait pas qu’il parle à autre chose qu’à un Bitwarden ;
- l’ADMIN_TOKEN haché n’est pas un détail : Vaultwarden accepte un token en clair mais le NOTICE dans les logs est explicite sur le risque, et le hash Argon2id se génère en une commande ;
- découpler
docker-compose.ymlet.envrend l’instance portable : la même pile peut être rejouée sur n’importe quel hôte Docker en regénérant juste le hash et en adaptantDOMAIN.
Pour la suite, il faudra publier l’instance en HTTPS derrière un reverse proxy avec un certificat valide, brancher un SMTP pour les invitations et la 2FA par mail, et automatiser la sauvegarde du dossier data/ (la base SQLite et les pièces jointes).
Sources
- Vaultwarden Wiki — Using Docker Compose , Vaultwarden Project.
- Vaultwarden Wiki — Enabling admin page (Argon2 ADMIN_TOKEN) , Vaultwarden Project.
- Vaultwarden Wiki — Disable registration of new users , Vaultwarden Project.
- Bitwarden Help Center — Self-hosting Bitwarden , Bitwarden, Inc.
- Docker Docs — Compose file specification , Docker Inc.
- ANSSI — Recommandations relatives à l’authentification multifacteur et aux mots de passe , ANSSI.