Intermediaire 8 min de lecture · 1 687 mots

Vaultwarden : gestionnaire de mots de passe Bitwarden auto-hébergé pour toute la famille

Estimated reading time: 8 minutes

J’utilisais Bitwarden cloud depuis deux ans sur le compte gratuit. Ça fonctionnait bien, les clients officiels sont excellents, et je n’avais aucune raison urgente de migrer – jusqu’au jour où j’ai réalisé que toutes mes entrées de mots de passe, y compris les accès bancaires et les codes 2FA de mes comptes sensibles, reposaient entièrement sur un service tiers que je ne contrôle pas. Avec un homelab opérationnel depuis plusieurs mois, continuer à externaliser ça ne tenait plus vraiment.

Vaultwarden, pas Bitwarden Server

Bitwarden publie son serveur en open source, mais il est conçu pour du multi-tenant à grande échelle : il tourne avec des dépendances .NET et MSSQL Server, consomme plusieurs GB de RAM, et se configure avec une douzaine de conteneurs. Pour un homelab familial, c’est disproportionné.

Vaultwarden (anciennement Bitwarden_rs) est une réimplémentation de l’API Bitwarden en Rust, compatible à 100 % avec tous les clients officiels – extensions Chrome et Firefox, apps iOS, apps Android, client desktop. Un seul conteneur, une base SQLite (ou PostgreSQL si on préfère), moins de 100 MB de RAM au repos.

La distinction importante : Vaultwarden n’est pas un fork de Bitwarden. C’est une implémentation indépendante du protocole. Les clients officiels Bitwarden ne savent pas qu’ils parlent à Vaultwarden – ils voient juste une API Bitwarden conforme.

Infrastructure et prérequis

Le déploiement se fait sur LXC 103, qui héberge déjà Traefik et sert de point d’entrée HTTPS pour la plupart des services du homelab. Debian 12, Docker Compose, 2 vCPU, 2 GB RAM.

Le prérequis le plus contraignant : HTTPS obligatoire. Les extensions Bitwarden dans Chrome et Firefox refusent catégoriquement de se connecter à un serveur HTTP – même en local, même sur un réseau privé. Ce n’est pas un avertissement contournable, c’est un blocage au niveau du client. Traefik avec Let’s Encrypt gère le certificat via le challenge DNS – le domaine vault.arewel.com pointe vers l’IP publique de la box, avec un enregistrement A.

Docker Compose

version: "3.9"

services:
  vaultwarden:
    image: vaultwarden/server:latest
    restart: unless-stopped
    volumes:
      - /opt/vaultwarden/data:/data
    environment:
      DOMAIN: "https://vault.arewel.com"
      SIGNUPS_ALLOWED: "false"
      INVITATIONS_ALLOWED: "true"
      ADMIN_TOKEN_FILE: /run/secrets/vw_admin_token
      TWO_FACTOR_ENABLED: "true"
      SMTP_HOST: "smtp.gmail.com"
      SMTP_FROM: "vault@arewel.com"
      SMTP_PORT: "587"
      SMTP_SECURITY: "starttls"
      SMTP_USERNAME_FILE: /run/secrets/smtp_user
      SMTP_PASSWORD_FILE: /run/secrets/smtp_pass
    secrets:
      - vw_admin_token
      - smtp_user
      - smtp_pass
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.rule=Host(vault.arewel.com)"
      - "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
      - "traefik.http.routers.vaultwarden.entrypoints=websecure"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"

secrets:
  vw_admin_token:
    file: ./secrets/admin_token.txt
  smtp_user:
    file: ./secrets/smtp_user.txt
  smtp_pass:
    file: ./secrets/smtp_pass.txt

SIGNUPS_ALLOWED: "false" ferme les inscriptions publiques – indispensable dès que le service est exposé sur Internet. Les nouveaux comptes familiaux passent par des invitations envoyées depuis le compte administrateur. INVITATIONS_ALLOWED: "true" permet précisément ça.

ADMIN_TOKEN_FILE pointe vers un secret Docker plutôt que d’inclure le token en clair dans le fichier Compose. Le token est généré avec openssl rand -base64 48.

Sans SIGNUPS_ALLOWED: "false", n’importe qui peut créer un compte sur ton instance Vaultwarden si elle est exposée sur Internet. Couper les inscriptions avant d’ouvrir le port.

Export depuis Bitwarden cloud

Depuis le client web Bitwarden (vault.bitwarden.com) : Settings > Export vault > Format : JSON (chiffré avec mot de passe).

Le format chiffré est important : le JSON non chiffré contient les mots de passe en clair. Le format chiffré protège le fichier avec un mot de passe symétrique qu’on choisit au moment de l’export – il faudra le saisir à l’import.

Le fichier exporté contient :

  • Toutes les entrées du coffre personnel (logins, notes sécurisées, cartes, identités)
  • Les dossiers et leur hiérarchie
  • Ce qu’il ne contient pas : les codes TOTP (2FA), les pièces jointes, et les entrées des organisations auxquelles on appartient (il faut les exporter séparément)
  • Import dans Vaultwarden

    Depuis le client web Vaultwarden (https://vault.arewel.com) après avoir créé le premier compte : Tools > Import data > Format : Bitwarden (JSON, chiffré).

    L’import des 247 entrées prend quelques secondes. Les dossiers sont recréés tels quels. Les URL associées à chaque entrée sont préservées.

    Vérification après import : comparer le nombre d’entrées dans Bitwarden cloud (Settings > My Account > Purge vault affiche le compteur sans effacer) avec le nombre dans Vaultwarden. S’ils correspondent, la migration est complète pour les entrées de base.

    Organisation familiale et collections

    Vaultwarden supporte les organisations Bitwarden – un espace partagé avec gestion de droits et collections séparées. J’ai créé l’organisation « Famille Calounx » depuis le panneau admin (https://vault.arewel.com/admin) plutôt que depuis le client, ce qui permet de l’associer directement au compte admin.

    Structure des collections dans l’organisation :

  • Famille commune : Netflix, Amazon, abonnements partagés, accès box opérateur, WiFi
  • Pascal : accès professionnels, comptes perso non partagés
  • Accès banque (lecture seule pour les membres non admin)
  • Les membres de la famille reçoivent une invitation par email, créent leur compte, et sont ajoutés à l’organisation avec les permissions adaptées. Chaque membre a son coffre personnel sur le même serveur, invisible aux autres – sauf les collections partagées.

    Trois comptes actifs à ce stade : le mien et deux membres de la famille qui avaient des comptes Bitwarden cloud indépendants. Chacun a exporté son coffre personnel depuis Bitwarden cloud et importé dans Vaultwarden.

    Configuration 2FA sur Vaultwarden

    Le 2FA TOTP côté Vaultwarden lui-même (pour se connecter à l’instance) est activé via la variable TWO_FACTOR_ENABLED: "true" et configuré par chaque utilisateur dans Security > Two-step login > Authenticator app.

    L’app de référence reste le client Bitwarden officiel, qui peut également stocker les codes TOTP pour les sites tiers – c’est une fonctionnalité premium dans Bitwarden cloud, mais elle est disponible gratuitement dans Vaultwarden.

    Ce qui a coincé : les TOTP des sites tiers

    C’est la partie que j’avais sous-estimée. Le fichier JSON exporté depuis Bitwarden cloud ne contient pas les codes TOTP configurés pour les sites tiers (GitHub, Google, etc.). Ces codes sont stockés chiffrés différemment dans le backend Bitwarden, et l’export JSON ne les inclut pas – que ce soit le format chiffré ou non chiffré.

    Résultat concret : j’avais environ 30 sites avec du 2FA TOTP configuré dans Bitwarden cloud. Après migration, toutes ces entrées sont présentes dans Vaultwarden mais sans le secret TOTP. Il a fallu les reconfigurer un par un, site par site, en allant dans les paramètres de sécurité de chaque service, désactivant l’ancien 2FA et en reconfigurant un nouveau secret TOTP depuis Vaultwarden.

    Le temps réel passé : un peu moins d’une heure pour les 30 sites, en procédant par ordre de criticité (banques et emails en premier, le reste ensuite). Certains sites compliquent volontairement la reconfiguration du 2FA (ils demandent le code actuel avant de permettre la désactivation, ce qui nécessite d’avoir encore accès à l’ancien secret – ce que j’avais depuis l’app Authenticator sur mon téléphone).

    Avant de migrer, liste tous tes sites avec TOTP actif dans Bitwarden. Prévois au minimum 1h pour la reconfiguration, plus longtemps si certains comptes ont des procédures de récupération complexes. Ne migre pas à la veille d’un week-end chargé.

    Applications clientes : pointer vers le bon serveur

    Sur chaque appareil, il faut indiquer l’URL de l’instance Vaultwarden avant de se connecter.

    Extension Chrome/Firefox : cliquer sur l’icône Bitwarden, puis sur « Se connecter ». Avant de saisir les identifiants, cliquer sur l’engrenage (ou « Settings » selon la version) et définir Self-hosted environment URL = https://vault.arewel.com. Sauvegarder, puis procéder à la connexion.

    App iOS et Android : même logique – sur l’écran de connexion, un lien ou un bouton « Region » ou « Self-hosted » permet de saisir l’URL du serveur avant de s’authentifier.

    L’expérience utilisateur une fois connecté est identique à Bitwarden cloud – remplissage automatique, génération de mots de passe, synchronisation instantanée entre appareils.

    Sauvegarde

    Le fichier critique est /opt/vaultwarden/data/db.sqlite3. Il contient l’intégralité du coffre – entrées, organisations, paramètres utilisateurs. Une corruption ou une perte de ce fichier sans backup, c’est la perte de tous les mots de passe de la famille.

    Stratégie de sauvegarde en deux couches :

    Couche 1 : Proxmox Backup Server

    Le LXC 103 est sauvegardé par PBS chaque nuit (snapshot cohérent du conteneur complet, y compris /opt/vaultwarden/data/). En cas de crash, restauration en moins de 5 minutes.

    Couche 2 : copie hebdomadaire sur NAS

    Un script cron copie le fichier SQLite sur le DS414 chaque dimanche à 2h :

#!/bin/bash
TIMESTAMP=$(date +%Y%m%d-%H%M)
DEST="/mnt/synology/backups/vaultwarden/"
mkdir -p "$DEST"

# SQLite hot backup via l'API de copie cohérente
sqlite3 /opt/vaultwarden/data/db.sqlite3 ".backup '${DEST}/db-${TIMESTAMP}.sqlite3'"

# Nettoyage : garder les 8 dernières sauvegardes
ls -t "${DEST}"/db-*.sqlite3 | tail -n +9 | xargs -r rm

La commande .backup de sqlite3 effectue une copie cohérente sans verrouiller la base – plus fiable qu’un simple cp sur un fichier SQLite potentiellement en cours d’écriture.

Un coffre sans backup est une promesse de catastrophe. Le fichier SQLite de Vaultwarden est petit (<10 MB pour 247 entrées) et rapide à sauvegarder - il n'y a aucune excuse pour ne pas le faire.[/warning]

Résultats

Après la migration :

  • 247 entrées migrées, vérifiées une à une sur les sites les plus critiques
  • 3 comptes familiaux actifs, chacun avec son coffre personnel + accès aux collections partagées
  • 30 secrets TOTP reconfigurés manuellement (~50 minutes)
  • Zéro donnée chez Bitwarden – les comptes cloud ont été supprimés après validation de la migration
  • RAM consommée par le conteneur Vaultwarden : 45 MB au repos
  • Sauvegarde : PBS nightly + copie SQLite hebdomadaire sur NAS
  • Ce qui reste à surveiller : les mises à jour de l’image vaultwarden/server:latest. Je me suis abonné aux releases GitHub du projet pour être notifié des nouvelles versions et des éventuelles failles de sécurité. La politique de mise à jour que j’ai adoptée : lire le changelog avant de tirer la nouvelle image, tester en premier sur un compte de test, déployer dans les 7 jours suivant la release stable.


    slug: vaultwarden-bitwarden-auto-heberge
    meta: Migration Bitwarden cloud vers Vaultwarden auto-hébergé sur LXC Proxmox : HTTPS Traefik, 247 entrées, 3 comptes famille, 2FA TOTP, sauvegarde SQLite. (~150 chars)
    tags: Vaultwarden, Bitwarden, mots de passe, Docker, Traefik, homelab, vie privée, 2FA

    Une remarque, un retour ?

    Cet article est vivant - corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.

    Laisser un commentaire