Die Installation eines Bitwarden-Servers ist ganz einfach, hatten sie gesagt.

Einfach nur 3 Befehle, hatten sie gesagt.

Das Schlimme an so einfachen Sachen: Wenn sie mal nicht funktionieren, dann findet sich die Lösung nur schwer bis garnicht...

Im Endeffekt habe ich es nicht geschafft, den Bitwarden-Server auf einem Mac zu installieren. Weder über die "einfachen 3 Zeilen", noch über den "on premise"-Weg.

Zum Glück stieß ich auf einen inoffiziellen bitwarden-Port namens vaultwarden (früher: bitwarden_rs), welcher sich als nicht "so einfach in der Installation" präsentiert, aber dafür so einfach in der Installation ist und schon nach der Eingabe von 2 Befehlen läuft.
Außerdem findet sich hier auch ein Weg, wie man von außerhalb des Docker-Containers auf den im Container laufenden Server zugreifen kann. Daran war ich beim "offiziellen Bitwarden" ebenfalls gescheitert.

Während das offizielle Bitwarden-Projekt für ein großes Hosting mit tausenden bis millionen Usern gedacht ist, kocht vaultwarden auf kleinerer Flamme. Für meinen Privatgebrauch ausreichend. Mehr als fünf User werden es sowieso nicht werden...

Doch moment... Was ist Bitwarden überhaupt?
Einer toller Passwortmanager, der erstens OpenSource ist und den man noch dazu selbst hosten kann.
Vaultwarden ist ein in Rust geschriebener Ableger des Bitwarden-Projektes, welcher mit den offiziellen Bitwarden-Browser-Plugins kompatibel ist und sich auch sonst in Sachen Frontend nicht wirklich unterscheidet.

Installation

Installationsanweisung, um vaultwarden als Docker-Container laufen zu lassen und dabei Registrierungen von Benutzern zu verbieten, außer ihre E-Mail-Adresse ist auf der Domain-Whitelist:

mkdir /pfad/zu/vaultwarden-verzeichnis
cd /pfad/zu/
docker pull vaultwarden/server:latest
docker run -d --name vaultwarden \
           -v /pfad/zu/vaultwarden-verzeichnis/:/data/ \
           -e SIGNUPS_ALLOWED=false \
           -e SIGNUPS_DOMAINS_WHITELIST=apfelz.ned
           -p 80:80 \
           --restart always
           vaultwarden/server:latest

Mit den beiden -e Environment-Variablen, die wir mitgeben, verbieten wir, dass irgendjemand auf diesem Server ein neues Konto eröffnet, fügen E-Mail-Adressen der eigenen Domain aber zu einer Whitelist hinzu.
Das heißt: Niemand kann ein Konto bei unserer Installation eröffnen, außer, er registriert sich mit einer E-Mail-Adresse der Domain apfelz.ned

Die -v Sache hat bei mir leider nicht funktioniert und das angegebene /pfad/zu/vaultwarden-verzeichnis-Verzeichnis blieb leer, obwohl /data innerhalb des Containers mit Dateien bevölkert ist.

Ich habe dann stattdessen ein Docker-Volume erstellt, um die vaultwarden-Daten erhalten zu können, sollte ich den Server mal neu herunterladen:

sudo docker volume create --name vaultwarden_data

Und dann statt -v /pfad/zu/vaultwarden-verzeichnis/:/data/ die Verknüpfung zum erstellten Docker Volume verwendet: -v vaultwarden_data:/data/

Wollen wir auf die Admin-Seite zugreifen, über welche sich Benutzer verwalten und globale Einstellung ändern lassen, müssen wir außerdem noch mit -e die Environment-Variable ADMIN_TOKEN mit einem möglichst langen geheimen Token mitgeben.
Diesen Token sollte man sich irgendwo sicher notieren (am besten in Vaultwarden selbst), da man ihn benötigt, um auf die Admin-Seite zugreifen zu können.

Vielleicht lieber nochmals der gesamte docker run-Befehl mit meinen Updates:

docker run -d --name vaultwarden \
           -v vaultwarden_data:/data/ \
           -e SIGNUPS_ALLOWED=false \
           -e SIGNUPS_DOMAINS_WHITELIST=apfelz.ned
           -a ADMIN_TOKEN=ganzLangerGeheimerAdminToken \
           -p 80:80 \
           --restart always
           vaultwarden/server:latest

"Interner" Docker-Port 80 auf den lokalen Port 8001 mappen

VBoxManage controlvm "default" natpf1 "vaultwarden,tcp,,8001,,80"

Web-UI lässt sich nun am lokalen Rechner unter http://127.0.0.1:8001 öffnen. Hier nun ein neues Konto erstellen und danach kann man sich einloggen.
Die Admin-Seite ist unter http://127.0.0.1:8001/admin aufrufbar. Zum Login benötigt man den bei docker run mitgegebenen ADMIN_TOKEN.

Inzwischen verwende ich allerdings docker-compose und das volume-mapping des ww-data-Verzeichnisses auf ein lokales Verzeichnis funktioniert ebenfalls...das vereinfacht das Backup der Datenbank immens!

docker-compose.yaml
services:
  vaultwarden:
    image: vaultwarden/server:1.37.2
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "http://192.168.178.20:8001"
      SIGNUPS_ALLOWED: false
      SIGNUPS_DOMAINS_WHITELIST: "apfelz.ned"
      ADMIN_TOKEN: "ganzLangerGeheimerAdminToken"
      WEBSOCKET_ENABLED: "true"
      SMTP_HOST: "smtp.mailserver.org"
      SMTP_FROM: "vaultwarden@apfelz.ned"
      SMTP_PORT: 587
      SMTP_SECURITY: "starttls"
      SMTP_USERNAME: "vaultwarden@apfelz.ned"
      SMTP_PASSWORD: "meinMailPasswort"
    volumes:
      - ./vw-data/:/data/
    ports:
      - 8001:80

Das Image habe ich fest auf eine Version gemappt. Also in diesem Fall vaultwarden/server:1.37.2 statt vaultwarden/server:latest
Hintergrund: Irgendwann wusste ich nicht mehr, welche Version bei mir überhaupt lief und ich hatte mir den Server auch schon zerschossen, weil ich vor einem Update die Konfiguration hätte ändern müssen. Von dem her gilt nun immer: Neuste Version erst in einem Testcontainer testen und dann in der Produktiv-Version die Versionsnummer ändern.

Anstatt des docker run Befehles von oben ist hier nun natürlich docker-compose up zu verwenden, wenn man sich auf der Kommandozeile gerade im Verzeichnis befindet, welches die docker-compose.yaml und das data-Verzeichnis enthält.

Reverse Proxy einrichten

Die beiden Module

LoadModule proxy_module libexec/apache2/mod_proxy.so
LoadModule proxy_http_module libexec/apache2/mod_proxy_http.so

müssen in /etc/apache2/httpd.conf aktiv sein. Also vorangestelltes # entfernen.

Dann einen neuen Virtual Host hinzufügen, der eine verfügbare Subdomain auf die interne Adresse http://127.0.0.1:8001 umleitet (in diesem Beispiel läuft der Reverse Proxy auf demselben Server, wie Docker/Vaultwarden).
Ein passendes Zertifikat hatte ich zuvor mit LetsEncrypt erstellt und im Verzeichnis /etc/letsencrypt/live/vaultwarden.apfelz.ned/ abgelegt.

sudo pico /etc/apache2/extra/httpd-ssl.conf

Nehmen wir an, meine Subdomain für Vaultwarden lautet vaultwarden.apfelz.ned

<VirtualHost *:443>
    SSLEngine on
    ServerName vaultwarden.apfelz.ned:443
    ServerAlias vaultwarden.apfelz.ned
    ServerAdmin spam-webmaster@apfelz.ned

    ErrorLog "/private/var/log/apache2/error_log"
    TransferLog "/private/var/log/apache2/access_log"
    CustomLog "/private/var/log/apache2/ssl_request_log" "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

    BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
    Include /etc/letsencrypt/options-ssl-apache.conf

    SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
    SSLCertificateFile    /etc/letsencrypt/live/vaultwarden.apfelz.ned/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/vaultwarden.apfelz.ned/privkey.pem

    # Sinnvolle Security Header
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "no-referrer"
    Header always set X-Frame-Options "SAMEORIGIN"

    ProxyPreserveHost On
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    # Reverse Proxy auf Vaultwarden (lokal)
    ProxyPass / http://127.0.0.1:8001/ upgrade=websocket
    ProxyPassReverse / http://127.0.0.1:8001/

    ProxyRequests Off
    RequestHeader set X-Real-IP %{REMOTE_ADDR}s
</VirtualHost>

Apache neustarten, dann sollte Vaultwarden auch aus dem weltweiten Netz nutzbar sein:
sudo apachectl -k restart

Zusammen mit LetsEncrypt-Zertifikaten...

Kleine Info nebenbei: Um den Certbot von LetsEncrypt nutzen zu können, muss auch die Standard-HTTP-Adresse (non-HTTPS auf Port 80) auf dem ReverseProxy-Server eingerichtet werden. Ansonsten bekam ich immer den Fehler Challenge failed for domain vaultwarden.apfelz.ned

Dies wäre noch der kleine Eintrag

/etc/apache2/extra/httpd-vhosts.conf
<VirtualHost *:80>
    ServerAdmin spam-webmaster@apfelz.ned
    DocumentRoot "/Library/WebServer/Documents/vaultwarden.apfelz.ned"
    ServerName apfelz.ned
    ServerAlias vaultwarden.apfelz.ned
    ErrorLog "/private/var/log/apache2/error_log"
    CustomLog "/private/var/log/apache2/access_log" common
</VirtualHost>