homelab-showcase/05-ct103-vaultwarden.md

4.4 KiB

CT 103 — Vaultwarden (Passwortmanager) — Rebuild-Runbook

Sensibelster Dienst im gesamten Setup — bewusst nur über Tailscale erreichbar, nie öffentlich.

1. Container erstellen

pct create 103 local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst \
  --hostname vaultwarden \
  --unprivileged 1 \
  --cores 1 \
  --memory 512 \
  --swap 512 \
  --rootfs local-lvm:4 \
  --net0 name=eth0,bridge=vmbr0,ip=<LAN_IP_VAULTWARDEN>/24,gw=<LAN_IP_ROUTER> \
  --features nesting=1 \
  --timezone Europe/Berlin \
  --onboot 1 \
  --nameserver <LAN_IP_ADGUARD>
pct start 103

2. TUN-Device für Tailscale (siehe 01-pve-host.md, Abschnitt 8)

pct stop 103
cat >> /etc/pve/lxc/103.conf <<'EOF'
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 0 0
EOF
pct start 103

3. Docker

pct exec 103 -- apt-get update
pct exec 103 -- apt-get install -y curl ca-certificates
pct exec 103 -- bash -c "curl -fsSL https://get.docker.com | sh"

4. Tailscale-Mitgliedschaft + HTTPS-Zertifikat

pct exec 103 -- bash -c "curl -fsSL https://tailscale.com/install.sh | sh"
pct exec 103 -- tailscale up --authkey=<AUTH_KEY> --hostname=vaultwarden

Kein --accept-routes nötig — anders als CT 102 sitzt dieser Container nicht zusätzlich im <LAN_SUBNET>-LAN in einer Weise, die einen Routing-Konflikt erzeugen würde (Netz-Interface ist zwar auf dem LAN, aber der Dienst wird ausschließlich über die Tailscale-IP angesprochen).

Voraussetzung in der Tailscale-Admin-Konsole: Settings → DNS → „HTTPS Certificates" muss aktiviert sein (einmalig für den ganzen Tailnet, siehe 01-pve-host.md).

5. Vaultwarden via Docker Compose

mkdir -p /opt/vaultwarden/data
ADMIN_TOKEN=$(openssl rand -base64 48 | tr -d '\n')

cat > /opt/vaultwarden/docker-compose.yml <<EOF
services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    environment:
      DOMAIN: "https://vaultwarden.<TAILNET>"
      ADMIN_TOKEN: "$ADMIN_TOKEN"
      SIGNUPS_ALLOWED: "true"
      WEBSOCKET_ENABLED: "true"
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8080:80"
EOF

cd /opt/vaultwarden && docker compose up -d

SIGNUPS_ALLOWED bewusst zuerst true, um das/die Konto(s) anzulegen. Nach der Konto-Anlage zwingend zurückstellen:

sed -i 's/SIGNUPS_ALLOWED: "true"/SIGNUPS_ALLOWED: "false"/' /opt/vaultwarden/docker-compose.yml
cd /opt/vaultwarden && docker compose up -d

6. HTTPS-Exposition via tailscale serve (kein Caddy, kein manuelles Zertifikat)

tailscale serve --bg http://127.0.0.1:8080

Erreichbar unter https://vaultwarden.<TAILNET>, automatisch gültiges Let's-Encrypt-Zertifikat, automatische Erneuerung, nicht öffentlich (nur Tailnet-Mitglieder).

⚠️ NICHT TUN: tailscale serve --bg https / http://... (alte Syntax). Neuere Tailscale-CLI-Versionen erwarten nur noch tailscale serve --bg http://127.0.0.1:8080 (HTTPS wird automatisch angenommen).

⚠️ NICHT TUN: Vaultwarden zusätzlich über Caddy auf dem VPS öffentlich exponieren (wie bei Guacamole). Bewusste Entscheidung: der Passwort-Tresor ist der sensibelste Dienst im gesamten Setup, öffentliche Erreichbarkeit erhöht die Angriffsfläche unnötig — der Internetcafé-Fall wird stattdessen über Guacamole selbst abgedeckt (SFTP/RDP-Zugriff auf andere Dienste, nicht direkt auf den Tresor).

7. Konto-Anlage (manueller Web-Schritt, nicht per CLI)

Über https://vaultwarden.<TAILNET> (nur von einem Tailnet-Mitglied aus erreichbar) im Browser ein Konto mit selbst gewähltem Master-Passwort anlegen. Das Master-Passwort wird bewusst nie programmatisch gesetzt oder irgendwo notiert — es ist der einzige Schlüssel zum gesamten Tresor.

Bekannte Stolpersteine (bereits behoben, hier nur als Hinweis)

  • Beim Passwort-Import versehentlich Duplikate erzeugt (zweimal importiert) → behoben durch: alle Einträge auswählen → löschen → Papierkorb leeren → sauber neu importieren.

Verifikation

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/          # lokal, im Container
curl -s -o /dev/null -w "%{http_code}\n" https://vaultwarden.<TAILNET>/   # von einem Tailnet-Mitglied
tailscale serve status