Instalare Bitwarden (password manager)
=====================
Bitwarden este un password manager foarte usor de folosit. Personal nu folosesc nici google nici mozilla pt parole, nu este recomandat sa distribuiti unor firme terte parolele voastre.
In acest tutorial voi prezenta o solutie mai serioasa cu failover care presupune 3 noduri: un VPS in cloud unde va exista un load balancer si inca 2 noduri ale voastre (sau cum am eu un server acasa si inca un server al unui prieten) unde vor rula instantele bitwarden cu baza de date din spatele aplicatiei (cluster galera).
In toate cele 3 locatii trebuie sa ai ip public si un DNS. Ex: example.com
Trebuie sa creati in zona DNS o inregistrare catre IP public cum aveti, sa zicem 11.22.33.44:
A bitwarden.example.com 11.11.22.22 #DNS-ul aplicatiei
A server1.example.com 33.33.44.44
A server2.example.com 55.55.66.66
Varianta clusterizata cu 3 noduri
In primul rand trebuie creata o retea wireguard intre cele 3 noduri:
VPS: 10.0.6.1
server1: 10.0.6.2
server2: 10.0.6.3
Instalati wireguard pe cele 3 noduri cu apt install wireguard -y si apoi trebuie pe fiecare in /etc/wireguard sa faceti wg9.conf:
Prima data generati keyle care se vor imperechea:
$ wg genkey | sudo tee /etc/wireguard/VPS_priv | wg pubkey | sudo tee /etc/wireguard/VPS_pub
$ wg genkey | sudo tee /etc/wireguard/server1_priv | wg pubkey | sudo tee /etc/wireguard/server1_pub
$ wg genkey | sudo tee /etc/wireguard/server2_priv | wg pubkey | sudo tee /etc/wireguard/server2_pub
Fisierele wg9.conf se fac in fewlul urmator:
VPS (garbd) - 10.0.6.1:
server1 (galera1) - 10.0.6.2:
server2 (galera2) - 10.0.6.3:
Pe toate cele 3 noduri porniti tunelele cu wg-quick up wg9 si va asigurati sa fie pornite la startup.
Tot traficul se va realiza prin aceste tunele wireguard insa pe server1 si 2 va rula un VIP (keepalived 10.0.6.10) care va trebui integrat ca si peer in aceasta configuratie.
Foarte important este sa
forwardati portul udp 51999 pe toate cele 3 noduri!
Cand server1 pica, VIP se muta pe server2 si wireguard va trebui sa stie de el.
Asadar,
pe VPS cu acest script peer-ul VIP este adaugat corect la wg:
Este nevoie si de pachetul contrack deci apt install -y conntrack
Alternativa unde doar schimbam endpoint-urile (fara VIP)+restart,
simplifca treburile f mult, as merge pe varianta asta!!!!!!!!!
Il rulati normal ca sa adauge peer (presupunand ca server1 este up):
Cand pica server1 care este primary, keepalived muta VIP pe server2 si de aici de pe VPS nu mai raspunde la ping dar o sa raspunda dupa ce e mutat pe server2.
Cand server1 revine, VIP se muta inapoi automat si in felul asta VIP este mereu disponibil.
Alternativ,
nu mai este nevoie de VIP, se schimba doar unde bate nginx/haproxy.
Recomandarea este sa folositi systemd timer, merge setat sa faca aceasta verificare la sub 1 minut(limitare cron), noi o sa facem la sa zicem 15 secunde:
/etc/systemd/system/check-vip.service:
[Unit]
Description=Check VIP and adjust WireGuard peers
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/root/check-vip.sh
/etc/systemd/system/check-vip.timer
[Unit]
Description=Run check-vip.sh every 15 seconds
[Timer]
OnBootSec=180sec
OnUnitActiveSec=15sec
AccuracySec=1sec
Unit=check-vip.service
[Install]
WantedBy=timers.target
Salvati fisierele si apoi activati timerul:
systemctl daemon-reload
systemctl enable --now check-vip.timer
systemctl list-timers --all | grep check-vip
Puteti verifica real time cu tail -f /root/check-vip-debug.log in caz de failover.
Este gandit sa scrie in log NUMAI cand se muta VIP/sau endpoint-ul fara VIP, nu la 9 secunde sa nu mai intelegeti nimic din log.
Din experienta va spun ca wireguard dupa vreo 2 zile devine instabil si e bine sa ii dati restart. Recomand cron pe toate cele 3 noduri, o data pe zi:
15 2 * * * /usr/bin/wg-quick down wg9 && sleep 2 && /usr/bin/wg-quick up wg9
Observati ora 2 si un sfert (nopatea) deci pe toate la fel
dar cu diferenta de 5 min, deci 2 si 10, si 15 si 20!
Config VPS
Pe VPS ruleaza un nginx si haproxy(docker), garbd(arbiter pt baza de date Galera care ruleaza pe server1 si 2) si server NFS necesar pt bitwarden.
Momentan ne ocupam de balansare, iata configuratiile:
nginx:(start.sh ca sa porniti containerul si configul aplicatiei)
$ docker run -itd --name nginxx \
--privileged \
-v /etc/localtime:/etc/localtime:ro \
-v /root/rtmp/nginx:/etc/nginx \
-v /root/rtmp/html:/var/www/html \
-v /opt:/etc/ssl \
--network=host \
ubuntu/nginx:latest
Fisierul de configurare:
Tot la acest nginx este foarte important in nginx.conf (deci configul global sa aveti astea:):
worker_processes auto; # f important sa decida singur cate core foloseste
error_log /var/log/nginx/error.log debug; #asa am descoperit problemele
pid /var/run/nginx.pid;
events {
worker_connections 10240; #cu 1024 exista intreruperi
use epoll;
multi_accept on; #asta cica ajuta dar inca nu l-am activat
}
*Nota: directiva debug la error_log genereaza error.log de giga, poate zeci de giga, e util doar sa gasesti problema si sa o repari, el de fapt trebuie sa fie setat in mod normal warn nu debug pt ca alfel va treziti fara spatiu pe disc!!!
haproxy:(start.sh ca sa porniti containerul si configul aplicatiei)
$ docker run -itd --name haproxy --memory="1g" --restart=always \
--network=host --dns 1.1.1.1 \
-v /root/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg \
-v /root/haproxy/errors:/etc/haproxy/errors \
-v /opt:/etc/ssl \
haproxy:2.8 #ver 3.x creeaza probleme la upload fisiere mari, 2.8 este LTS!
Fisierul de configurare:
Aveti nevoie si de un subfolder errors ca sa functioneze, valabil pt orice haproxy:
$ ls errors
400.http 403.http 408.http 500.http 502.http 503.http 504.http
Nota: * Eu initial am incercat sa balansez dinamic in ambele endpoint-uri insa din pacate nu se poate cu Bitwarden. De ce? Din ce am citit, foloseste un cookie (token JWT) semnat cu o cheie privata care e valida doar pe nodul care a generat-o.
In setupul de mai jos este acoperit scenariul asta pt ca foloseste shared storage si nu te delogeaza dar nu merge sa faci update/insert in DB, da eroare la balansare dinamica cu haproxy, la un simplu clic aterizezi pe celalalt server si desi tokenul este acelasi, tot nu functioneaza. Asta a fost conceput sa functioneze de sine statator, monolithic design.
Chiar daca galera ar sincroniza nodurile la < 1ms tot nu are merge, asa a fost gandit din pacate.
Asadar, motivul pt care exista aici un haproxy era balansarea dinamica insa daca il folosim in mod failover, nu prea isi mai are sensul. Se poate scoate de tot si pur si simplu la nginx-ul din fata puneti direct proxy_pass http://10.0.6.10:8087 (sau .2:8087 la varianta fara VIP.). Nu incurca cu nimic haproxy avand in vedere ca practic nu mananca resurse deloc insa in realitate e cam degeaba in conditiile astea.
Garbd:(Arbiterul care coordoneaza galera)
Ideea este ca versiunea garbd trebuie sa se potriveasca cu versiunea mariadb din containerul galera care ruleaza pe serverele1 si 2. Eu pe acel VPS am ubuntu 22.04 ARM si a fost o intreaga aventura sa compilez
versiunea potrivita:
/usr/local/bin/garbd --version
INFO: 4.22.r
In functie de distributia linux pe care o folositi trebuie sa faceti in asa fel incat sa rulati aceasta versiune garbd 4.22. Imaginea bitnami/mariadb-galera foloseste mariadb 11.4.7 si se potriveste cu aceasta versiune, in aceasta pagina mai jos este pus link cu imaginea.
Se pare ca exista repo oficial pt ubuntu (20.04, 22.04 ,24.04) deci schimbati ce ubuntu aveti: (focal, jammy, noble) in linia echo:
$ echo "deb https://releases.galeracluster.com/galera-4.22/ubuntu jammy main" | sudo tee /etc/apt/sources.list.d/galera-4.22.list #deci schimbati aici cu ce aveti
$ curl -fsSL https://releases.galeracluster.com/galera-4.22/KEY.gpg | sudo apt-key add -
$ sudo apt update
$ sudo apt install galera-arbitrator-4
Fix acum cand scriu acest tutorial am descoperit ca exista repo oficial cu orice versiune garbd pe orice ubuntu si orice arhitectura si eu m-am chinuit ca vita sa compilez pe VPS-ul arm. Rau e nene sa fii prost..........
Acest garbd (arbiter) va gestiona cele doua mariadb 11.4.7 (server1 si 2) si se va asigura ca aveti un cluster healthy.
Fisierul service trebuie sa arate asa, linia ExecStart este f importanta, acolo se declara IP-urile galera1 si 2 si numele clusterului (vwcluster):
[Unit]
Description=Galera Arbitrator Daemon (garbd)
After=network.target
[Service]
ExecStart=/usr/local/bin/garbd --group="vwcluster" --address="gcomm://10.0.6.2,10.0.6.3" --name="arbiter"
Restart=always
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
NFS:(server nfs pt /data in bitwarden)
Se poate folosi si docker insa nu merita, nu usureaza cu nimic. Deci apt install nfs-server (poate fi diferit de la debian la ubuntu)
Faceti folder /bitwarden-data si exportati-l cu nfs-server
$ cat /etc/exports
/bitwarden-data 10.0.6.0/28(rw,sync,no_root_squash,insecure)
$ exportfs -ra
$ showmount -e
Export list for VPS:
/bitwarden-data 10.0.6.0/28
Se poate face foarte elegant un shared storage cu glusterfs intre nod1 si nod2 insa asta presupune linux distros identice. Am facut o imagine docker cu glusterfs(server) 10.5
ca sa fie la fel insa din pacate cand montezi, clientul glusterfs trebuie sa fie identic, librariile fuse3, libfuse3-x si fuse3-libs-3 la fel, deci cu linuxuri diferite e posibil sa nu mearga.
Pt acest tutorial, export NFS de aici de pe VPS catre cele 2 noduri prin wireguard e ok.
Evident ca trebuie din firewall sa acceptati aici 2049 tcp si 111 tcp si udp, am tot explicat in alte tutoriale cum se face.
Config Server1 si 2
Pe fiecare nod am reusit sa fac totul cu docker si configul este aproape identic: keepalived, nginx, bitwarden, galera.
keepalived:(serviciul responsabil cu VIP)
$ docker run -d --name keepalived \
--cap-add=NET_ADMIN --cap-add=NET_BROADCAST --cap-add=NET_RAW \
--network host \
-v /root/keepalived/keepalived.conf:/usr/local/etc/keepalived/keepalived.conf \
osixia/keepalived:2.0.20
Fisierul de configurare keepalived.conf pe ambele noduri:
Nod1:
Nod2:
Nota: * Am mentionat mai sus ca scriptul de failover de pe VPS poate functiona si fara VIP deci folosirea acestui keepalived devine optionala.
De functionat functioneaza insa pt setup explicat aici adauga complexitate inutila la wireguard.
nginx:(rp pus in fata bitwarden)
start.sh se face similar cu cel cu care se porneste nginx pe VPS, tot cu network host, nu il mai pun inca o data.
Fisierul de configurare:
Nota: * Acest nginx pare sa fie obligatoriu, nu am reusit de pe VPS sa fac proxy_pass direct in bitwarden si e oricum ok ca sa vezi ce nod este activ (add_header X-Server-Node)
bitwarden:(serverul in sine)
Mare atentie aici, inainte de a porni containerul bitwarden trebuie sa va asigurati ca s-a montat continutul din VPS exportat prin NFS in /root/bitwarden/vw-data-nfs
Trebuie sa va asigurati ca aveti cum sa montati deci instalati nfs-client!
Eu folosesc un script care verifica tunel wg9 si daca s-a facut mount sau nu:
In caz ca este necesara restartarea, se apeleaza start.sh:
Si inclus.sh:
$ sleep 10
$ docker run -itd --name bitwarden \
--network=host \
-v /etc/localtime:/etc/localtime:ro \
-v /root/bitwarden/vw-data-nfs/:/data \
-e DATABASE_URL='mysql://pulica:
[email protected]/vaultwarden?ssl_mode=disabled' \
-e RUST_BACKTRACE=1 \
-e ENABLE_DB_WAL='false' \
-e ROCKET_PORT=40081 \
-e DOMAIN=https://bitwarden.example.com \
-e WEBSOCKET_ENABLED=false \
-e ROCKET_SECRET_KEY="cheia-ta-generata-aici-foarte-complexa" \
-e ROCKET_LIMITS='{json=104857600,data-form=104857600,file=104857600}' \
vaultwarden/server:latest
Puteti sa le combinati intr-un singur fisier, dupa preferinte.
galera:(clusterul mariadb)
Faceti un subfolder galera sa fie facut si trebuie mentionat faptul ca sunt stand alone. Adica galera1 cu folderul lui, galera 2 separat. Nu trebuie sa fie pe storage distribuit!
Porniti cu acest fisier start.sh:
Acest start.sh face niste verificari si apoi porneste containerul galera. Totodata verifica fisierul din subfolderele galera/data grastate.dat care are doi parametri: seqno si safe_to_bootsrap.
Clusterul galera este master-master insa prima data cand se porneste e bine sa fie toate oprite (deci si arbiterul) si pe server1 se porneste galera1 dar cu valoarea safe_to_bootstrap: 1
Deci editati inainte fisierul /root/galera1/galera/data/grastate.dat!
Mai elegant, la pornire, doar pe nodul1 puteti folosi un fisier separat start-galera-avarie.sh care se ruleaza pe nod1
insa pe celelalte totul trebuie sa fie oprit adica galera2 pe nod2 si garbd pe VPS.
Scriptul are si o confirmare, trebuie sa scrieti yes ca sa continue:
Ulterior se porneste si galera2 de pe server2 cu start.sh fara sa se modifice nimic, si galera2 va face join la cluster. La sfarsit se da systemctl restart garbd pe VPS.
La fel se procedeaza si in caz de disaster cand pica 2 noduri, trebuie pornite frumos, mecanismul automat functioneaza daca pica server1 (primary). Daca pica server2 nu e nicio problema pt ca e oricum in stand by si intra activ numai cand pica server1, asta este ideea failover-ului de fapt.
* Nota: Observati in start.sh ca se face o verificare a clusterului si pt treaba asta se foloseste comanda "if mysql --connect-timeout=3", deci trebuie sa aveti mysql (mai exact mariadb-client) instalat pe host:
# dpkg -l|grep mariadb
ii libmariadb3:amd64 1:10.11.13-0ubuntu0.24.04.1 amd64 MariaDB database client library
ii mariadb-client-core 1:10.11.13-0ubuntu0.24.04.1 amd64 MariaDB database core client binaries
ii mariadb-common 1:10.11.13-0ubuntu0.24.04.1 all MariaDB database common files (e.g. /etc/mysql/mariadb.conf.d/)
Asadar asigurati-va ca aveti pachetul
mariadb-client-core instalat!
Pe centos/fedora se instaleaza cu yum install MariaDB-client, dar noi aici in principiu folosim ubuntu/debian.
Am incercat un soi de automatizare care detecteaza daca clusterul mai exista sau nu si in felul asta nodul face bootstrap sau join la clusterul existent. Totodata, containerelul dependent bitwarden trebuie si el restartat si pt asta trebuie sa astepte dupa galera sa se ridice corect, dureaza minim 30s in functie de sistem!
Imaginea este oficiala dar din tag: latest i-am pus eu un alt nume si tag:mariadb-galera:garbd422. Imaginea o luati de
aici si o puneti pe server1 si 2 cu docker load -i.
Ca sa vad ce versiune de garbd foloseste imaginea bitnami/mariadb-galera (link randul de deasupra) am facut asa:
$ docker cp galera1:/opt/bitnami/mariadb/lib/libgalera_smm.so ./libgalera_smm.so
$ strings ./libgalera_smm.so | grep -i -E 'galera|version'
wsrep_interface_version
......
/bitnami/blacksmith-sandox/libgalera-26.4.22/galera/src/wsrep_provider.cpp
.....
deci exact garbd 4.22 cum am eu
Si ca sa vad versiunea mariadb:
$ docker exec -it galera1 mariadb --version
mariadb from 11.4.7-MariaDB, client 15.2 for Linux (x86_64) using readline 5.1
Vreau sa evit pe viitor o incompatibilitate cu garbd asa ca am blocat imaginea asa, e oricum ultima versiune de galera care va mai merge bine merci ani de zile.
Ca sa verificati cati membri sunt in cluster:
$ docker exec -it $(docker ps --format "{{.Names}}"|grep galera) /opt/bitnami/mariadb/bin/mariadb -uroot -pparolaroot -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 3 |
+--------------------+-------+
Numai cand vedeti toti cei 3 membri cluster e ok. Verificati logurile galera1 si 2 si de asemeni si logurile garbd
Alte comenzi de verificare pe care le rulati in consola mariadb:
SHOW STATUS LIKE 'wsrep_cluster_status';
+----------------------+---------+
| wsrep_cluster_status | Primary |
+----------------------+---------+
SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| wsrep_cluster_size | 3 |
+--------------------+-------+
SHOW STATUS LIKE 'wsrep_incoming_addresses';
+--------------------------+------------------------------+
| wsrep_incoming_addresses | 10.0.6.2:3306,,10.0.6.3:3306 |
+--------------------------+------------------------------+
SHOW STATUS LIKE 'wsrep_local_state_comment';
+---------------------------+--------+
| wsrep_local_state_comment | Synced |
+---------------------------+--------+
SHOW STATUS LIKE 'wsrep_connected';
+-----------------+-------+
| wsrep_connected | ON |
+-----------------+-------+
SHOW STATUS LIKE 'wsrep_local_send_queue_avg';
+----------------------------+-----------+
| wsrep_local_send_queue_avg | 0.0185185 | e ok, valoare mica
+----------------------------+-----------+
SHOW STATUS LIKE 'wsrep_local_state';
+-------------------+-------+
| wsrep_local_state | 4 (synced) |
+-------------------+-------+
Nota: Ca o mica completare, ati observat ca toate serviciile ruleaza pe wg9 si trebuie sa va asigurati si pe VPS si pe cele 2 servere ca prima data aveti tunelul wg9 ridicat dupa care porniti dockere, garbd cu systemd samd pt ca daca nu e up wg9, nu or sa porneasca. Nu acopar aspectele astea in acest tutorial.
Verificare failover
Cum puteti testa ca intr-adevar functioneaza dupa ce va asigurati ca e ok clusterul galera
Simulati ca a picat nodul1: Rulati pe nodul1
$ wg-quick down wg9;docker stop galera1
Si in felul asta obligatoriu VIP 10.0.6.10 trebuie sa fie mutat pe server2 de catre keepalived:
Verificati pe server2:
$ ip a show wg9
45: wg9:
mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.0.6.3/24 scope global wg9
valid_lft forever preferred_lft forever
inet 10.0.6.10/24 scope global secondary wg9
valid_lft forever preferred_lft forever
Verificati si pe VPS:
$ wg show wg9 allowed-ips
xxxxxxxxxxxxxxxxxxxxxxxx 10.0.6.2/32
yyyyyyyyyyyyyyyyyyyyyyyy 10.0.6.3/32 10.0.6.10/32
In felul asta confirmati ca si wg peer e ok alocat si ca fizic VIP s-a mutat pe server2.
Similar o sa observati efectul invers cand revine nodul1, adica wg peer revine pe nod1 si VIP se muta singur inapoi pe nod1.
Totul in max 9 secunde!!
Partea interesanta este ca bitwarden are vw-data-nfs shared intre ambele noduri (NFS mount) si cand pica nod1 nu va delogeaza din extensia instalata in browser!
Nota * Daca nu folositi VIP (keepalived) o sa vedeti ca failover script pur si simplu modifica pe VPS backend nginx/haproxy si apoi da reload/restart.
Am scris mai sus, vedeti real time in fisierul /root/check-vip-debug.log cum s-a schimbat IP-ul si cel mai cinstit, de oriunde, verificati asa:
for i in {1..150}; do curl -I https://bitwarden.example.com; sleep 1; done
si veteti in output cum se schimba backendul din nod1 in nod2.
Scripturi mentenanta
Din varii motive care sunt multe avand in vedere ca totul se intampla pe niste retele wireguard (wg9), clusterul se mai poate strica si am incercat sa implementez un mecanism autohealing:
Puneti in crontab un script check-galera.sh sa ruleze la vreo 20 min:
Observati ca detecteaza daca nu e ok clusterul si apeleaza /root/galera2/start.sh care la randul lui ulterior restarteaza si bitwarden (+baikal daca exista)
De asemeni scriptul check-galera.sh apeleaza la randul lui un alt script la sfarsit, care trimite mesaj pe Matrix server in cazul in care se face bootsprap pe unul din noduri,
ca sa fiti si notificati in caz ca se intampla.
Nu am tutorial cu Matrix pe acest site de tutoriale, insa eu am un server functional si acolo primesc notificarea. Deci este clar optional insa il pun sa ramana si aici:
Varianta simpla cu un singur nod
Aplicatia se pune f simplu intr-un folder, de ex /root/bitwarden:
Se creeaza reteaua docker mai intai:
$ docker network create --driver=bridge --subnet=172.19.0.0/24 marianet
Se creeaza fisierul care va porni ambele containere (bitwarden si mysql57, nu mai este necesar galera in acest caz):
Deci acesta este start.sh si dupa ce il porniti vor porni 2 containere dupa cum se vede, unul cu bitwarden si celalalt cu mysql.
Se poate folosi fara mysql insa cand vine vorba de parole e mai safe asa intrucat se poate face un mysqldump, probabilitatea sa pierdeti tot devine nula
Daca se strica ceva pur si simplu importati in mysql un backup.sql si totul revine la normal.
Userul si parola mysql se editeaza direct din start.sh si de asemeni si parola userului root. Pt acces in consola mysql:
$ docker exec -it mysql57 mysql -uiuzar -p
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| vaultwarden |
+--------------------+
mysql> use vaultwarden;
mysql> show tables;
mysql> select uuid,created_at,email,enabled from users;
+--------------------------------------+---------------------+---------------------------+---------+
| uuid | created_at | email | enabled |
+--------------------------------------+---------------------+---------------------------+---------+
| 05e499ff-fcf2-4c7a-ac36-9728a2f2ae8b | 2023-04-05 09:32:20 |
[email protected] | 1 |
+--------------------------------------+---------------------+---------------------------+---------+
mysql> DESCRIBE users;
mysql> exit;
Se observa de asmeni ca cele doua containere depind unul de celalat si ruleaza pe o retea diferita network=marianet care se creeaza asa:
docker network create --driver=bridge --subnet=172.19.10.0/24 --ip-range=172.19.10.0/24 --gateway=172.19.10.1 marianet
Este necesar un RP de ex nginx pus in fata ca sa aveti certificat SSL. Nginx se poate pune tot cu docker:
$ nano start.sh
$ docker run -itd \
--name nginx \
--restart unless-stopped \
-v /root/nginx/cert:/etc/ssl \
-v /root/nginx/conf.d/bitwarden.conf:/etc/nginx/conf.d/bitwarden.conf \
--network=host \
nginx:1.25.3-alpine
Se observa ca trebuie sa aveti certificat LetsEncrypt care trebuie pus in /root/nginx/cert. Exemplu generare certificat
aici
De asemeni creati si subfolderul conf.d unde se va afla bitwarden.conf.
Fisierul /root/nginx/conf.d/bitwarden.conf:
Serviciul bitwarden functioneaza pe portul 40081, de aceea in conf-ul de deasupra avem redirectare catre http://127.0.0.1:40081.
In cele mai multe cazuri, un nginx exista deja pe masina respectiva care deserveste alte servicii, in cazul respectiv nu faceti nimic altceva decat sa puneti si acest conf si sa il adaptati in functie de situatie.
Export DB
Cum se poate exporta baza de date sa o pastrati ca si backup (export in container si apoi copiat fisierul pe host):
$ docker exec -it mysql57 bash
bash-4.2# mysqldump -uroot -p vaultwarden > vaultwarden_17mar2024.sql
Enter password: parolaroot
bash-4.2# ls -lah|grep sql
-rw-r--r-- 1 root root 281K Apr 16 22:35 vaultwarden_17mar2024.sql
bash-4.2# exit
root@vapor:~/bitwardenmysql #pwd
/root/bitwardenmysql
root@vapor:~/bitwardenmysql #docker cp mysql57:/vaultwarden_17mar2024.sql .
root@vapor:~/bitwardenmysql #ls -lah |grep sql
drwxr-xr-x 6 systemd-bus-proxy root 4.0K Feb 2 11:57 mysql
-rw-r--r-- 1 root root 340K Feb 5 12:52 vaultwarden_17mar2024.sql
Se poate face si automatizat, testat pe galera dar este acelasi lucru:
Puneti acest script in crontab sa faca o data pe zi:
Utilizare Bitwarden
Se instaleaza in browser extensia Bitwarden care este disponibila pt orice browser dupa care se importa parolele
Eu am folosit f multi ani LastPass si de acolo este foarte simplu sa exporti toate parolele. Similar se pot exporta din Firefox sau din Google Chrome.
Accesati in browser https://bitwarden.example.com si declarati un master password cat mai complex posibil, nu se poate folosi autentificare htpassword, URL-ul este publicat in internet deci mare atentie!
De asemeni aplicatia Bitwarden se poate instala si pe telefoanele Android, functioneaza perfect si pe telefon indiferent de ce browser folositi!
In momentul in care va logati, aveti grija sa schimbati URL-ul cu cel custom adica https://bitwarden.example.com.
Aplicatia este destul de complexa, aveti posibilitatea unor setari mai detaliate, este f intuitiva.
In browser recomand sa bifati unlock with PIN, deci cand va delogeaza nu mai este necesar sa bagati Master Password cu timeout la 30 min, o ora, 4 ore cum doriti,
iar pe telefon timeout mai mic gen 15 min si unlock with biometrics adica cu amprenta.
Am explicat mai sus, in scenariul failover cand pica nod1, nu va delogeaza nici de pe telefon nici pe PC in browser (in extensie mai exact). Singurul inconvenient este peer-ul wireguard care poate dura pana la 1minut pana se actualizeaza, insa aveti solutia cu systemd timer deci e acoperit tot.
Se pot partaja fisiere de pana la 100 mega (testat) direct din extensie de la Send si de asemeni cand salvati parola a unui site, pe langa URL, user si parola mai exista si campurile Notes
si Attachments. Asadar la Attachments se poate uploada fisier de pana la 100 MB. Diferenta este ca aici ramane permanent, la Send expira!
Nu as abuza de acest feature, e ok ca exista dar sincer pt fisiere nu e recomandat sa se foloseasca, poate crea load inutil la servere. E ok Sa pui notite sau de ex sa hostezi TOTP pt siteuri cu autentificare 2FA, este suficient.
Acest lucru este posibil datorita storage-ului comun, folderul montat NFS pe nod1 si nod2 de pe VPS!
Si nu in ultimul rand, puteti tine mai multe conturi, deci nu numai parolele voastre ci si ale familiei, fiecare cu contul lui, cu master password-ul lui!
Utilizare Bitwarden pt echipe
Exista posibilitatea in Bitwarden sa se creeze niste grupuri (organizations) in care se adauga useri si in felul asta se pot sharui items (parole) la comun in cadrul acelui Organization.
In primul rand trebuie sa mentionez faptul ca acest lucru nu se poate face decat prin email, deci obligatoriu trebuie sa existe un SMTP undeva (relay smtp mai exact) prin intermediul caruia aplicatia sa trimita mailuri.
Asa a fost gandita aplicatia de catre cei care au scris-o, vezi Dne sa fie cat mai secure. Mie orice implica mail mi se pare peste mana insa inca o data, daca doriti sa activati acest feature, lucrurile se schimba. Eu am activat ca sa testez insa sincer nu prea merita, e mai simplu fiecare cu parolele lui.
Totusi voi acoperi in acest tutorial cum se face treaba asta daca e cineva interesat, util pt munca, sa ai shared passwords in cadrul echipei.
Sa incepem cu serverul de mail, eu folosesc Mailjet de cand Sendgrid au anuntat oficial ca din 26 iulie 2025 nu va mai exista free tier.....
Deci faceti cont la Mailjet care pe free tier asigura trimiterea a 6000 de emailuri pe luna, este suficient.
Va duceti la sectiunea Domains and senders unde trebuie adaugat
[email protected], mailul de pe care se trimite orice mail. Apoi La SPF/DKIM Authentication trebuie adaugate inregistrarile DNS de tip TXT: SPF, DKIM si DMARC, altfel nu functioneaza. Deci va trebui sa aveti acces la domeniu ca sa puteti crea inregistrarile astea, vi se explica acolo cum sa le faceti samd.
O sa aveti nevoie si de niste KEY, va duceti la API Key Management, generati si obtineti API Key si Secret Key.
Dupa ce va asigurati ca smtp-ul este functional, treceti mai departe la modificarile aduse configuratiei pt a activa acest feature. Bitwarden va detecta si va trece in modul "pro" unde dupa cum spuneam, fara mail nu se mai poate.......Ce e totusi ok e faptul ca nu trebuie modificat decat fisierul start.sh cu care porniti pe nod1 si 2 containerul bitwarden....acolo se vor adauga niste parametri printre care si ADMIN_TOKEN care va trebui declarat criptat cu argon2.
Luati nod1 ca referinta, este nod principal, faceti acolo modificarile dupa care puneti identic si pe nod2, dati restart la container samd......
Deci cum creati admin tokenul:
$ docker run -it --rm vaultwarden/server:latest /vaultwarden hash
Password: tokenulcareeste
Confirm Password: tokenulcareeste
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Generation of the Argon2id PHC string took: 133.055691ms
Va duceti apoi in /root/bitwarden si editati start.sh adaugandu-i urmatorii parametri:
-e ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-e ORG_CREATION_USERS="
[email protected]" \
-e SMTP_HOST="in.mailjet.com" \
-e SMTP_PORT="587" \
-e SMTP_SSL="true" \
-e SMTP_USERNAME="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-e SMTP_PASSWORD="yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" \
-e SMTP_FROM="
[email protected]" \
SMTP_USERNAME si SMTP_PASSWORD sunt de fapt API Key si Secret Key generate la Mailjet.
Dati restart la container si acum logati-va in browser. Imediat dupa login sus de tot va apare un notice sa faceti verificare, dati clic, pleaca mailul si dupa ce il primiti, o sa aveti in mail "Verify Email Address Now". Merge, cont verificat. Deci eu ma refer la contul vostru pe care il aveati deja in bitwarden, dupa ce activezi toate saraciile astea brusc contul nu mai e verified. Mondial, ce sa zic, suntem in modul "pro"....deci valabil pt toti userii care deja existau, le cere la toti sa faca aceasta verificare absolut inutila din punctul meu de vedere. In fine, trecem mai departe....
Si ca sa nu uit, momentan signup este open, un user nou care se inregistreaza va proceda la fel, dupa crearea contului trebuie sa se logeze in browser sa isi trimita confirmarea pe mail, sa dea clic acolo pe link, deci aceeasi procedura imbecila.
In contul fiecaruia la Vaults apare + New organization si oricine teoretic are dreptul sa creeze dar va fi nevoie de anumite drepturi.
Deci consideram ca
[email protected] este contul meu si creez o organizatie noua. Se cere Name si email (ca sa se stie cine este ownerul acelei organizatii, evident pun
[email protected])
Am creat Org1 si ulterior va trebui sa editez aceasta organizatie. Ma duc jos la o sectiune care pana acum nu exista [Admin console]/Collections ma duc pe 3 punctulete Edit info si editez Name: col1.
Acest "collection" e un fel de subgrup al organizatiei, ceva de genul. Tot acolo va apare tabul Access unde momentan ar trebui sa apara doar un membru adica
[email protected]
Eh acum trebuie sa adaugam niste useri in Org1 si implicit in col1. Userii astia trebuie sa existe deja inregistrati si confirmati!
Ma duc in acelasi [Admin Console]/Members si acolo vedem membri Org1. Aveti sus drepta +Invite member si se deschide un meniu. In meniul respectiv in primul tab [Role] ii treceti mail si alocati drepturi, User cred ca este suficient iar in tabul [Collection] aveti permissions (implicit View items) si trebuie sa selectati col1. Save si se trimite mailul automat. Userul respectiv verifica mailul si vede ca a fost invitat to join Org1 si are buton "Join Organization Now", da clic si este adaugat la Org1.
Dupa ce userul a dat join, la [Admin console]/Members o sa vedeti userul invitat cu "invited", am observat ca dureaza vreo juma de minut pana statusul i se schimba in "needs confirmation" si de abia atunci ma duc pe 3 punctulete din dreptul lui si aleg: confirm! In momentul ala userul este membru confirmat in Org1.
Ultimul pas este sa va duceti la [Admin console]/Collections/col1 3 punctulete: Edit access si de abia acum veti putea alege userul de la Select members, clic pe el, il adaugati si puteti sa ii editati Permissions. Save si cam asta e, userul a fost adaugat la col1 si de abia acum daca se logeaza, la Vaults pe langa My vault vede si Org1 cu items comune.
Acele items vor avea logo Org1 comparativ cu cele stand alone care au logo Me:

Explicam mai sus ca la [Admin console]/Collections/col1 daca ma duc pe cele 3 punctulete din dreptul col1, la Edit access o sa vad userul in tabul Access.
Deci mare atentie, dupa ce userul se valideaza in Org1
trebuie deci adaugat si in col1!
Am amintit mai sus despre ADMIN_TOKEN criptat cu argon2, scopul este sa accesam un meniu de administrare care arata total diferit de cum arata cand te logezi normal cu mailul tau.
URL-ul este https://bitwarden.example.com/admin, bagati token si o sa vedeti 4 taburi: Settings, Users, Organizations, Disgnostics si Vault(link catre login normal).
Ce se poate face concret: la tabul Settings nu as umbla, majoritatea sunt setate de parametri cu care se porneste containerul, la Users puteti da disable/enable sau delete, la Organizations va arata Org1 in cazul nostru si la Diagnostics sunt afisati toti parametri serverului. SIngurul parametru cu Error este Websocket pe care intentionat l-am setat disabled ca sa functioneze scenariul failover. Nu afecteaza nicio functionalitate. Deci in afara de management useri, nu se poate face mai nimic....

La sfarsit de tot dupa ce se adauga si se invita toti userii care vor folosi shared passwords din Org1 si nu numai deci dupa ce s-au inregistrat (si confirmat) toti useri care se vor folosi de serverul asta, se poate inchide signup complet, deci adaugati si acest parametru in docker run-ul containerului bitwarden si apoi evident restart la container:
-e SIGNUPS_ALLOWED=false \
-e DISABLE_EMAIL_NEW_DEVICE=true \
Fix asa am facut eu, am trecut peste partea enervanta cu mailul, o data userii inregistrati/confirmati, invitati la Org1/confirmati, nu (prea) se mai trimite niciun mail, asa ramane.
Am observat intamplator mailuri cu "New Device Logged In From" si parametrul DISABLE_EMAIL_NEW_DEVICE=true ar trebui sa il dezactiveze.
Consider ca s-ar fi putut face si fara mail, pt cativa useri mi se pare prea peste mana cu atatea complicaciuni insa asa a fost conceputa aplicatia.
Similar nu se poate folosi in scenariu HA pt ca asa au gandit-o baietii....cam asta ar fi tutorialul.
Succesuri!
Enjoy!
Last update 12 Aug 2025.