Bitwarden

Aujourd'hui, nous allons déployer un serveur Bitwarden en Docker, un gestionnaire de mots de passe Open Source sous licence AGPL.

Bitwarden

Freemium si vous souscrivez à leur offre en ligne, vous pouvez tout à fait héberger Bitwarden en docker (image non officielle), vous permettant d'accéder à la plupart des fonctionnalités réservées au freemium.

Je vous propose d'installer Bitwarden et de le rendre accessible au travers d'un serveur web, depuis internet, de façon sécurisée.

Prérequis

  • Être coutumier de Docker
  • Avoir pré-installé Docker est Docker-compose
  • Avoir pré-installé OpenSSL
  • Disposer d'un serveur web permettant de faire un reverse proxy et/ou être familier avec son utilisation. Dans le présent article, nous utilisons Nginx via l'image docker SWAG de linuxserver
  • Si vous souhaitez rendre accessible depuis internet Bitwarden, votre serveur hôte doit être accessible sur le port 80 et le port 443. Typiquement si vous utilisez un Raspberrypi dans un réseau local, vous devrez faire un port forwarding depuis votre routeur (si c'est possible) vers l'IP locale de votre hôte, en s'assurant de rendre cette IP fixe.

Le présent tutoriel a été réalisé un serveur dédié sous Debian Buster, il a été reproduit sur un Raspberry Pi 3 Model B Plus Rev 1.3.

Répertoire de travail

Dans le présent tutoriel nous aurons comme répertoire de travail /docker :

  • Nous le créons avec :
sudo mkdir /docker
  • On s'assurera que notre utilisateur ai les droits avec :
sudo chown -R <user>:<user> /docker
  • L'arborescence ressemblera à ceci :
/docker/bitwarden
├── data
└── docker-compose.yml

Image docker : Vaultwarden

Il existe une image docker officielle, que nous n'allons pas utiliser par commodité. Il existe en effet une image non-officielle développée en Rust, Vaultwarden (elle s'appelait avant ça BitwardenRS) : Hub Docker - Github.

La mise en place est aisée et tout à fait adaptée à une utilisation typique d'un particulier ou de petite structure? De plus la communauté autour de cette image est grande et attentive, tout pour plaire au libriste que je suis.

Une documentation solide existe.

Fonctionnalités

Fondamentalement, une implémentation complète de l'API Bitwarden est fournie, notamment:

Aussi :

Fonctionnalités manquantes : Certaines fonctions de Bitwarden ne sont pas encore implémentées (ou ne le seront pas sans PR), pour n'en citer que quelques-unes (liste détaillée) : SSO, Accès d'urgence, Synchronisation en direct pour mobile, etc.

Configuration

Docker-compose : Bitwarden

On commence en créant un fichier nommé docker-compose.yml dans le répertoire de votre choix, avec votre éditeur de texte favori (nano ?).

Il vous faudra adapter selon vos besoins et votre infrastructure, notamment :

  • le volume persistent; ici le chemin est relatif et monte le volume dans le répertoire courant ./data
  • vous devrez générer <generated_admin_token> pour accéder à l'interface admin; dans un terminal exécutez openssl rand -base64 48 (voir l a doc)
  • Déterminez le réseau docker <network>, si c'est pertinent pour vous. Dans mon cas j'utilise un réseau docker, qui me permet de faire un reverse proxy avec linuxserver/SWAG sans devoir exposer les ports dans mon container Bitwarden, en dehors du réseau docker. Les containers SWAG et Bitwarden sont dans le même réseau.

Aussi lors du première démarrage, il vous faudra passer l'option SIGNUPS_ALLOWED sur true et la désactiver (false donc) par la suite si vous ne souhaitez pas permettre la création de compte. A savoir que si vous modifiez la fonctionnalité depuis l'admin, cette variable d'environnement sera override.


  • on créé le répertoire - mkdir /docker/bitwarden
  • on créé le fichier - nano /docker/bitwarden/docker-compose.yml
  • et on y colle ce qui suit, en adaptant selon nos besoins :
version: '3'

services:
  bitwarden:
    image: vaultwarden/server:latest
    container_name: bitwarden
    volumes:
      - ./data:/data # where to keep persistent volume?
    environment:
      WEBSOCKET_ENABLED: 'true'
      SIGNUPS_ALLOWED: 'true'   # set to false to disable signups
      ADMIN_TOKEN: <generated_admin_token> # openssl rand -base64 48   
    restart: always      
    networks:
      - <network>

networks:
  <network>:
    external:
      name: <network>

Docker-compose : NGINX (SWAG)

On passe à la configuration de NGINX, au travers l'image linuxserver/SWAG. Nous nous attarderons surtout sur la configuration de NGINX. Aussi je me dois de vous la présenter et vous expliquer pourquoi j'ai opté pour cette solution.

SWAG - Secure Web Application Gateway - est une image docker développée par LinuxServer, embarquant NGINX, Certbot (Let's Encrypt), PHP et Fail2Ban. SWAG permet également d'utiliser la connexion avec HTTP Auth, LDAP via leur image ldap-auth, et Authelia (2 facteurs). Bref, je vous recommande.


Il vous faudra adapter selon vos besoins et votre infrastructure, notamment :

  • le volume persistent : ici le chemin est relatif et monte le volume dans le répertoire courant ./config
  • Déterminez le réseau docker <network>. Les containers SWAG et Bitwarden doivent être dans le même réseau.
  • Spécifiez les variables d'environnement selon vos besoins : EMAIL, URL, MAXMINDDB_LICENSE_KEY (à lire : doc)

  • on créait le répertoire - mkdir /docker/swag
  • on créait le fichier - nano /docker/swag/docker-compose.yml
  • et on y colle ce qui suit, en adaptant selon nos besoins :
version: '3'

services:
  swag:
    image: ghcr.io/linuxserver/swag
    container_name: swag
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Paris
      - URL=<domain.tld>
      - SUBDOMAINS=
      - VALIDATION=http
      - EMAIL= <email> #optional
      - ONLY_SUBDOMAINS=false #optional
      - EXTRA_DOMAINS=<extra.domain.tld> #optional
      - MAXMINDDB_LICENSE_KEY=<maxminddb key>
      - STAGING=false #optional
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80 #optional
    restart: unless-stopped
    networks:
      - <network>

networks:
  <network>:
    external:
      name: <network>
  • On se place dans le répertoire - cd /docker/swag
  • On enregistre et on démarre le container en mode attaché - docker-compose up
  • Une fois que les certificats Let's Encrypt ont été créés et que tout fonctionne, on peut arrêter le container avec ctrl + c. On s'assurera d'arrêter complétement le container et de supprimer les volumes non persistents en tapant docker-compose down

Reverse Proxy

SWAG propose entre autres des presets NGINX, notamment pour Bitwarden. Nous allons ici utiliser le preset bitwarden.subdomain.conf.sample.

Vous devrez vous êtes assuré d'avoir à votre disposition un domaine de second niveau ou un sous-domaine, que vous aurez déclaré dans la zone DNS de votre provider.

  • Par défaut le DNS resolver de Docker est 127.0.0.11 (loopback), il suffit alors de faire une requête avec le nom du container, qui est défini ici par set $upstream_app bitwarden. Pour que la requête aboutisse, il est nécessaire que le container bitwarden se trouve dans le même réseau docker que SWAG.

  • En supposant que vous utilisez également SWAG, il vous suffit de copier le preset - cp /docker/swag/config/nginx/proxy-confs/bitwarden.subdomain.conf.sample /docker/swag/config/nginx/site-confs/bitwarden
  • Et de l'éditer à votre convenance - nano /docker/swag/config/nginx/site-confs/bitwarden

## Version 2020/12/09
# make sure that your dns has a cname set for bitwarden and that your bitwarden container is not using a base url
# make sure your bitwarden container is named "bitwarden"
# set the environment variable WEBSOCKET_ENABLED=true on your bitwarden container

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name bitwarden.domain.tld;

    include /config/nginx/ssl.conf;

    client_max_body_size 128M;

    location / {
        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app bitwarden;
        set $upstream_port 80;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    }

    location /admin {
        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app bitwarden;
        set $upstream_port 80;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    }

    location /notifications/hub {
        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app bitwarden;
        set $upstream_port 3012;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    }

    location /notifications/hub/negotiate {
        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app bitwarden;
        set $upstream_port 80;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    }
}

Démarrage

Tout est configuré ! Maintenant il ne reste plus qu'à démarrer.

  • Dans /docker/bitwarden puis dans /docker/swag exécutez successivement la commande docker-compose up -d
  • Rendez-vous maintenant à l'adresse que vous avez choisi (ex.: bitwarden.domain.tld) et inscrivez-vous !

bitwarden-1

Notes

A savoir que j'ai ici volontairement dissocié les configurations de Bitwarden et SWAG, car j'utilise d'autres containers qui sont proxy par SWAG. Si vous souhaitez regrouper sous un même stack (un même fichier docker-compose.yml) ces deux containers, vous n'aurez alors pas à préciser de network, celui-ci sera créé par défaut au démarrager de la stack.

Bon Bitwarden à tous !

Ressources