Dealownia
Menu
sprawdzono – jak sprawdzamy oferty

Vaultwarden: własny menedżer haseł zgodny z Bitwarden

Vaultwarden w Dockerze na VPS: HTTPS przez nginx, wyłączenie rejestracji, bezpieczna kopia bazy SQLite i kluczy oraz uczciwie o ryzyku trzymania haseł u siebie.

Aplikacje na własnym serwerzezaktualizowano 6 min czytania

Redakcja serwisu Dealownia · ceny i fakty ze stron dostawców, jak sprawdzamy

Własny serwer haseł pod adresem https://vault.example.pl, z którym łączą się oficjalne aplikacje Bitwarden: rozszerzenie przeglądarki, aplikacja na telefon i na komputer. Rejestracja zamknięta, panel administratora wyłączony, a baza kopiowana co noc w sposób, który nie uszkodzi pliku w trakcie zapisu. Vaultwarden to niezależna implementacja serwera Bitwarden napisana w Rust; nie jest projektem firmy Bitwarden ani nie ma jej wsparcia. Zanim zaczniesz, przeczytaj część o ryzyku na końcu.

Czego potrzebujesz

  • VPS z Ubuntu 24.04 LTS (komendy działają też na Debianie 12 i 13) przygotowany jak w poradniku od zera: użytkownik z sudo, ufw, nginx na hoście, Docker z Compose, certbot.
  • Minimum: najmniejszy plan, 1 vCPU i 1 GB RAM. Wiki Vaultwarden nie podaje wymagań sprzętowych; domyślnie używa SQLite, więc nie potrzebuje osobnej bazy. Na serwer trzymający hasła ważniejsze od rozmiaru są kopie i aktualizacje systemu.
  • Subdomenę vault.example.pl z rekordem A. HTTPS jest obowiązkowy: sejf w przeglądarce korzysta z Web Crypto API, które przeglądarki udostępniają tylko w bezpiecznym kontekście. Przez http:// aplikacja webowa po prostu nie zadziała.

Krok 1. Katalog i compose.yaml

bash
sudo mkdir -p /opt/apps/vaultwarden && sudo chown deploy:deploy /opt/apps/vaultwarden
cd /opt/apps/vaultwarden

Plik compose.yaml, oparty na przykładzie z wiki Vaultwarden:

yaml
services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.example.pl"
      SIGNUPS_ALLOWED: "true"
      INVITATIONS_ALLOWED: "false"
    volumes:
      - ./vw-data:/data
    ports:
      - "127.0.0.1:8001:80"
  • Kontener słucha na porcie 80; publikujemy go tylko na 127.0.0.1:8001 (8000 zajmuje zwykle GlitchTip), żeby nikt nie ominął HTTPS. Docker omija reguły ufw, więc "8001:80" wystawiłoby serwer na świat.
  • latest to bieżące stabilne wydanie. Jeśli wolisz aktualizować świadomie, wpisz numer wydania z listy wydań i podnoś go ręcznie po kopii.
  • DOMAIN musi być pełnym adresem z https:// – na jego podstawie powstają linki w e-mailach i ustawienia WebAuthn.
  • SIGNUPS_ALLOWED: "true" jest tylko na chwilę, do założenia Twojego konta.
  • Bez zmiennej ADMIN_TOKEN panel /admin jest wyłączony. Przy jednym lub kilku użytkownikach nie jest potrzebny.
bash
docker compose up -d
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8001/alive

Oczekujesz kodu 200.

Krok 2. nginx i certyfikat

WebSocket służy tu do powiadomień o zmianach w sejfie. Mapę WebSocket tworzysz raz na serwer (wyjaśnienie w poradniku o reverse proxy); polecenie niżej nic nie zmieni, jeśli plik już istnieje:

bash
[ -f /etc/nginx/conf.d/websocket-map.conf ] || sudo tee /etc/nginx/conf.d/websocket-map.conf >/dev/null <<'EOF'
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
EOF

Wiki Vaultwardena pokazuje mapę z pustą wartością ('' "";) zamiast close – dla Vaultwardena obie działają, a jedna wspólna definicja nie koliduje z innymi aplikacjami.

Plik /etc/nginx/sites-available/vault.example.pl, na podstawie przykładu nginx z wiki:

nginx
server {
    listen 80;
    listen [::]:80;
    server_name vault.example.pl;

    client_max_body_size 525M;

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
bash
sudo ln -s /etc/nginx/sites-available/vault.example.pl /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d vault.example.pl

client_max_body_size 525M pochodzi z przykładu w wiki i pozwala wgrywać załączniki; domyślny limit nginx to 1 MB. Mapa w wersji z wiki wysyła pusty nagłówek Connection zamiast close, żeby utrzymać połączenia keepalive do backendu.

Krok 3. Konto i zamknięcie rejestracji

Wejdź na https://vault.example.pl, załóż konto i zaloguj się. Hasło główne jest jedyną rzeczą, której serwer nie zna i nie odtworzy – sejf jest szyfrowany po stronie klienta. Zapisz je na papierze i schowaj, zanim wpiszesz do sejfu cokolwiek ważnego.

Teraz zamknij rejestrację w compose.yaml:

yaml
      SIGNUPS_ALLOWED: "false"
bash
docker compose up -d

Według wiki przy SIGNUPS_ALLOWED=false nowych kont nie założy nikt z zewnątrz, ale właściciel lub administrator organizacji nadal może zapraszać użytkowników – dlatego ustawiliśmy też INVITATIONS_ALLOWED: "false". Jeśli chcesz dzielić sejf z rodziną, zmień to na "true" na czas zaproszeń. Uwaga: gdy ustawisz SIGNUPS_DOMAINS_WHITELIST, wartość SIGNUPS_ALLOWED jest ignorowana.

W ustawieniach konta włącz logowanie dwuetapowe (aplikacja TOTP albo klucz sprzętowy) i zapisz kod odzyskiwania.

Krok 4. Klienty

W aplikacji Bitwarden na ekranie logowania wybierz serwer samodzielnie hostowany (self-hosted) i wpisz https://vault.example.pl. To samo w rozszerzeniu przeglądarki. Import z innego menedżera robisz w aplikacji webowej: Narzędzia → Importuj dane. Plik eksportu z poprzedniego menedżera to niezaszyfrowana lista haseł – usuń go zaraz po imporcie.

Krok 5. Kopie

Wiki opisuje kopie szczegółowo. W katalogu vw-data liczą się:

  • db.sqlite3 – wymagane, prawie cały stan serwera;
  • attachments/ – wymagane, jeśli używasz załączników;
  • rsa_key* – zalecane, klucze podpisujące tokeny logowania (bez nich wszyscy muszą się zalogować ponownie);
  • config.json – zalecane, jeśli kiedyś użyjesz panelu administratora (zawiera sekrety jawnym tekstem);
  • sends/ – opcjonalne; icon_cache/ – nie warto.

Samego pliku db.sqlite3 nie kopiuj zwykłym cp, gdy serwer działa – możesz złapać go w połowie zapisu. Wiki zaleca polecenie .backup SQLite:

bash
sudo apt -y install sqlite3

Skrypt /opt/apps/vaultwarden/backup.sh:

bash
#!/usr/bin/env bash
set -euo pipefail
umask 077
cd /opt/apps/vaultwarden
D=$(date +%F)
install -d -m 700 backup
sqlite3 vw-data/db.sqlite3 ".backup 'backup/db-$D.sqlite3'"
tar -czf "backup/files-$D.tar.gz" -C vw-data --ignore-failed-read attachments sends rsa_key.pem config.json
find backup -type f -mtime +14 -delete
bash
chmod +x backup.sh && sudo ./backup.sh && sudo ls -lh backup
echo "20 3 * * * root /opt/apps/vaultwarden/backup.sh >>/var/log/vaultwarden-kopia.log 2>&1" | sudo tee /etc/cron.d/vaultwarden-kopia

Skrypt działa jako root, bo pliki w vw-data tworzy kontener z uprawnieniami roota. umask 077 i katalog z prawami 700 są tu konieczne: archiwum zawiera rsa_key.pem i config.json (z tokenem administratora), których nie chronią hasła użytkowników – inni użytkownicy serwera nie mogą ich odczytać. Odtwarzanie: zatrzymaj kontener, podmień pliki, a przed wgraniem kopii z .backup usuń stary db.sqlite3-wal, bo według wiki może uszkodzić odtworzoną bazę.

Ta kopia leży na tym samym dysku. Archiwum haseł wysyłaj poza serwer tylko w postaci zaszyfrowanej – sejf jest szyfrowany, ale klucze RSA i config.json nie. Sposób z szyfrowaniem po stronie VPS-a opisuje poradnik o restic i rclone. Raz na jakiś czas odtwórz kopię na innym komputerze i zaloguj się do niej.

Ryzyko trzymania haseł u siebie

  • Twoje kopie to jedyne kopie. Aplikacje trzymają zaszyfrowaną kopię sejfu offline, więc po awarii serwera dalej odczytasz hasła na telefonie, ale bez kopii serwera odtworzenie konta, załączników i organizacji jest niemożliwe.
  • Ty odpowiadasz za aktualizacje. Vaultwarden publikuje poprawki bezpieczeństwa w wydaniach na GitHubie. Aktualizacja to docker compose pull && docker compose up -d, ale ktoś musi ją zrobić. Pakiety systemu aktualizuj automatycznie (unattended-upgrades).
  • Serwer jest celem. Dane w bazie są zaszyfrowane hasłem głównym, więc wyciek bazy nie daje haseł wprost, ale słabe hasło główne da się łamać offline. Używaj długiej frazy i 2FA, przejdź listę kontrolną zabezpieczeń VPS.
  • Brak wsparcia producenta. Gdy coś nie działa, zostaje Ci wiki i zgłoszenia na GitHubie.

Jeśli którekolwiek z tych zdań Cię niepokoi, hostowany plan oficjalnego Bitwardena albo innego menedżera to rozsądniejszy wybór niż oszczędność na abonamencie.

Najczęstsze problemy

  • Aplikacja webowa pokazuje pusty ekran albo błąd kryptografii. Wchodzisz przez http:// lub przez adres IP. Używaj tylko domeny z certyfikatem.
  • Aplikacja na telefonie nie łączy się, przeglądarka tak. Niepełny łańcuch certyfikatu. Certbot w trybie --nginx używa fullchain.pem; jeśli wpisywałeś ścieżki ręcznie, sprawdź, czy nie wskazałeś cert.pem.
  • Zmiany z jednego urządzenia nie pojawiają się od razu na drugim. Brak nagłówków Upgrade/Connection w nginx – synchronizacja działa wtedy dopiero przy ręcznym odświeżeniu.
  • Po zmianie SIGNUPS_ALLOWED rejestracja dalej działa. Kontener nie został odtworzony (docker compose up -d, nie restart) albo wcześniej zapisałeś ustawienia w panelu administratora – wartości z config.json mają pierwszeństwo przed zmiennymi środowiskowymi.

Co dalej

Dołóż monitoring dostępności z Uptime Kuma, żeby awaria serwera haseł nie zaskoczyła Cię przy logowaniu. Jeśli chcesz wysyłać zaproszenia i potwierdzenia e-mailem, Vaultwarden potrzebuje zewnętrznego SMTP – opcje znajdziesz w poradniku o poczcie na domenie. Serwery pod takie narzędzia są w dziale promocje: VPS.

Powiązane poradniki