Déploiement de Vaultwarden avec Docker

Déploiement de Vaultwarden avec Docker

Mise en service d'un Vaultwarden sur le lab interne ANSSI : préparation de la stack Docker, écriture du docker-compose.yml, génération du hash Argon2id de l'ADMIN_TOKEN, démarrage du conteneur et premier login dans l'interface Bitwarden.
Publié le
Statut
Terminé

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

AspectBitwarden Server officielVaultwarden
LangageC# (.NET)Rust
Empreinte RAM~1 Go~50 Mo
Compatibilité clientsTous les clients BitwardenTous les clients Bitwarden (mêmes API REST)
LicenceAGPL v3AGPL v3
MaintenanceBitwarden, 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.

Topologie

ÉquipementRôleAdresse
Passerelle du labRoutage / DNS interne192.168.10.254/24
VM VAULT (Debian 12)Hôte Docker pour Vaultwarden192.168.10.150/24
Mon poste d’adminNavigateur, SSHDHCP 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.

BASH
sudo mkdir -p /opt/docker-compose/vaultwarden
cd /opt/docker-compose/vaultwarden
Cliquez pour développer et voir plus

Gé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.

BASH
docker run --rm -it vaultwarden/server /vaultwarden hash
Cliquez pour développer et voir plus

Génération du hash Argon2id pour l’ADMIN_TOKEN : prompts Password / Confirm Password puis sortie ADMIN_TOKEN=’$argon2id$v=19$m=65540,t=3,p=4$…$…’

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-commande hash, qui utilise le preset bitwarden d’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 dans ADMIN_TOKEN du fichier .env.

Écriture du fichier docker-compose.yml

YAML
services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./data:/data
    env_file:
      - .env
Cliquez pour développer et voir plus

Explications 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 laisse latest et 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 ./data côté hôte. C’est le seul volume à sauvegarder.
  • env_file: .env : Vaultwarden tire toute sa configuration de variables d’environnement, isolées dans .env pour ne pas polluer le docker-compose.yml avec des secrets.

Fichier .env

BASH
# 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:8000
Cliquez pour développer et voir plus

Explications 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 /admin est 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 par https://vault.lab.local une fois le reverse proxy en place.

Démarrage du conteneur

BASH
cd /opt/docker-compose/vaultwarden
docker compose up -d
docker compose logs --follow vaultwarden
Cliquez pour développer et voir plus

Démarrage Vaultwarden : Container vaultwarden Recreated, version 1.35.4, NOTICE on plain text ADMIN_TOKEN, Rocket has launched from http://0.0.0.0:80

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 :

TEXT
[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`.
Cliquez pour développer et voir plus

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.

Vaultwarden après correction de l’ADMIN_TOKEN : démarrage propre sans NOTICE, 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.

Page de login Vaultwarden Web 2026.1.1 : champ « Adresse électronique », bouton Continuer, lien « Créez un compte »

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 :

  1. Je saisis l’ADMIN_TOKEN en clair (celui que j’ai utilisé pour générer le hash).
  2. Vaultwarden m’authentifie comme admin.
  3. 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.
  4. 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.

Vaultwarden après login : « Tous les coffres », avatar LU en haut à droite, onboarding 1/3 complété, « Aucun élément dans le coffre »

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

BASH
docker compose ps
Cliquez pour développer et voir plus

Sortie attendue :

TEXT
NAME          IMAGE                       STATUS         PORTS
vaultwarden   vaultwarden/server:latest   Up X minutes   0.0.0.0:8000->80/tcp
Cliquez pour développer et voir plus

Persistance du volume

BASH
ls -la ./data
Cliquez pour développer et voir plus

On doit voir notamment :

  • db.sqlite3 (la base) ;
  • attachments/, sends/, tmp/ : les pièces jointes et fichiers temporaires.
BASH
# Test : on stoppe et redémarre le conteneur, le coffre doit retrouver le compte
docker compose down
docker compose up -d
Cliquez pour développer et voir plus

Test de cycle complet

  1. Créer un identifiant : Nouvel élément → Identifiant, je mets un test (https://test.lab.local, user-test, mot de passe généré).
  2. Sortir du coffre, redémarrer le conteneur.
  3. Se reconnecter : l’identifiant est toujours là. Le volume persiste correctement.

Problèmes rencontrés et solutions

SymptômeCauseCorrection
[NOTICE] You are using a plain text ADMIN_TOKENHash 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 connecterMauvaise URL ou certificat invalideVérifier le champ DOMAIN, ouvrir l’URL dans un navigateur pour voir si le serveur répond
Internal server error au loginPermissions du volume ./datachown -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 adresseDésactiver temporairement la vérification : SIGNUPS_VERIFY=false dans le .env

Compétences du bloc 1 mobilisées

Compétence officielleMobilisation concrète
Mettre à disposition aux utilisateurs un service informatiqueVaultwarden accessible aux comptes admin du lab via http://192.168.10.150:8000.
Installer et configurer des éléments d’infrastructureStack Docker Compose, volume persistant, configuration par variables d’environnement.
Mettre en place et vérifier les niveaux d’habilitation associés à un serviceSIGNUPS_ALLOWED=false, comptes créés depuis /admin, ADMIN_TOKEN haché Argon2id.
Réaliser les tests d’intégration et d’acceptation d’un serviceValidation 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.yml et .env rend 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 adaptant DOMAIN.

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

Commencer la recherche

Saisissez des mots-clés pour rechercher des articles

↑↓
ESC
⌘K Raccourci