วิธีสำรองข้อมูลและกู้คืน Vaultwarden บน VPS อย่างปลอดภัย
เรียนรู้วิธีสำรองข้อมูล Vaultwarden บน Docker ด้วยคำสั่ง sqlite3 .backup เพื่อป้องกันฐานข้อมูลเสียหาย พร้อมขั้นตอนการสำรองไฟล์ config.json และ attachments อย่างครบถ้วนและถูกต้อง
สิ่งที่การสำรองข้อมูล Vaultwarden ต้องมี
การสำรองข้อมูล Vaultwarden คือการคัดลอกโฟลเดอร์ข้อมูลทั้งหมด โดยฐานข้อมูลที่อยู่ภายในจะต้องถูกคัดลอกอย่างถูกวิธี ให้รัน sqlite3 db.sqlite3 ".backup out.sqlite3" แทนการใช้ cp เนื่องจากการคัดลอกไฟล์ฐานข้อมูลโดยตรงในขณะที่มีการเขียนข้อมูลอยู่อาจทำให้ได้ไฟล์ที่เปิดใช้งานไม่ได้ จากนั้นให้เก็บไฟล์ที่เกี่ยวข้องทั้งหมดไว้ด้วยกัน ซึ่งเป็นส่วนที่ผู้ใช้งานมักลืม
สำหรับการติดตั้งผ่าน Docker โฟลเดอร์ข้อมูลคือตำแหน่งที่คุณ mount ไว้ที่ /data ซึ่งอาจเป็น path บนโฮสต์หรือ named volume โดย ความแตกต่างระหว่าง bind mounts และ named volumes จะเป็นตัวกำหนดว่าข้อมูลใน vault ของคุณถูกจัดเก็บไว้ที่ใดบนดิสก์ โดยเนื้อหาภายในประกอบด้วยสิ่งต่อไปนี้
db.sqlite3: บัญชีผู้ใช้ทั้งหมด, รายการใน vault, โฟลเดอร์ และองค์กรทั้งหมด หากสูญเสียไฟล์นี้จะถือว่าสูญเสีย vault ทั้งหมดdb.sqlite3-walและdb.sqlite3-shm: write-ahead log (WAL) และดัชนีหน่วยความจำร่วม ข้อมูลที่เขียนล่าสุดจะถูกเก็บไว้ที่นี่จนกว่า SQLite จะรวมข้อมูลเข้ากับไฟล์หลักattachments/: ไฟล์ที่ผู้ใช้แนบไว้กับรายการใน vault โดยจะถูกเข้ารหัสและแยกเก็บเป็นไดเรกทอรีตามแต่ละรายการsends/: ไฟล์ที่อยู่เบื้องหลังลิงก์ Bitwarden Sendconfig.json: การตั้งค่าทั้งหมดที่คุณบันทึกไว้จากหน้าผู้ดูแลระบบ (admin page)rsa_key.pemรวมถึงrsa_key.derและrsa_key.pub.derในการติดตั้งเวอร์ชันเก่า: กุญแจสำหรับลงนามใน login tokenicon_cache/: ไอคอนเว็บไซต์ที่ดาวน์โหลดมา ไดเรกทอรีนี้เป็นส่วนเดียวที่คุณสามารถข้ามไปได้ เนื่องจาก Vaultwarden สามารถดึงข้อมูลเหล่านี้ใหม่ได้ตามความต้องการ
ฐานข้อมูล Vaultwarden ของฉันปลอดภัยหรือไม่? ข้อมูลที่แท้จริงในไฟล์คืออะไร
คำสั่ง 2 คำสั่งสามารถตอบคำถามนี้ได้ และคุณสามารถรันคำสั่งทั้งสองได้ทันที
sudo apt update && sudo apt install -y sqlite3
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select email from users;"
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select name from ciphers limit 1;"คำสั่งแรกจะแสดงที่อยู่อีเมลของผู้ใช้ของคุณในรูปแบบข้อความปกติ (cleartext) ส่วนคำสั่งที่สองจะแสดงชื่อรายการหนึ่งรายการ ซึ่งมีลักษณะดังนี้:
2.k9Qw1nQ0y7Yy2Xw==|E1r0J3l5s7d9f1g3h5j7k9==|Lm4nOp6qRs8tUv0wXy2zAb4cDe6fGh8i=ชื่อรายการ ชื่อผู้ใช้ รหัสผ่าน และบันทึกต่างๆ จะถูกเข้ารหัสโดยไคลเอนต์ก่อนที่จะถูกส่งออกไป ดังนั้นเซิร์ฟเวอร์จึงจัดเก็บเฉพาะข้อมูลที่เข้ารหัส (ciphertext) ซึ่งเซิร์ฟเวอร์ไม่สามารถอ่านได้ คำนำหน้า 2. คือประเภทการเข้ารหัสของ Bitwarden ตามด้วย initialisation vector (IV), ข้อมูลที่เข้ารหัส และ MAC (message authentication code) โดยทั้งหมดอยู่ในรูปแบบ base64 และคั่นด้วย | กุญแจที่ใช้ถอดรหัสข้อมูลเหล่านี้ได้มาจากรหัสผ่านหลัก (master password) ของบัญชี ซึ่งรหัสผ่านนี้จะไม่ถูกส่งไปยังเซิร์ฟเวอร์ในรูปแบบที่ใช้งานได้จริง ส่วนนี้จะเหมือนกันไม่ว่าคุณจะใช้งาน Vaultwarden หรือเซิร์ฟเวอร์ทางการ ดังที่ระบุไว้ใน การเปรียบเทียบระหว่าง Vaultwarden และ Bitwarden แบบ self-hosted
ข้อมูลส่วนที่เหลือในฐานข้อมูลไม่ได้ถูกเข้ารหัส ที่อยู่อีเมล ชื่อบัญชี คำใบ้รหัสผ่าน และรหัสกู้คืนการยืนยันตัวตนสองชั้น (two-factor recovery codes) จะถูกจัดเก็บเป็นข้อความปกติ (plain text) ควบคู่ไปกับข้อมูลเมตา เช่น เวลาที่สร้างรายการ และองค์กรที่เป็นเจ้าของรายการนั้น ดังนั้นไฟล์สำรองข้อมูลจึงถือเป็นความลับในตัวมันเอง ใครก็ตามที่ครอบครองไฟล์นี้จะทราบว่าผู้ใช้ของคุณคือใคร และสามารถโจมตีข้อมูลที่เข้ารหัสแบบออฟไลน์ได้ด้วยความเร็วเท่าที่ฮาร์ดแวร์ของพวกเขาจะทำได้ ข้อเท็จจริงเพียงข้อเดียวนี้เป็นเหตุผลที่ทำให้ต้องมีกฎการจัดเก็บข้อมูลในส่วนถัดไป: ไฟล์สำรองข้อมูลจะต้องถูกเข้ารหัสก่อนที่จะถูกย้ายออกจากเซิร์ฟเวอร์ ส่วน admin token คืออีกครึ่งหนึ่งของปัญหาเดียวกันนี้ และ ขั้นตอนการเพิ่มความปลอดภัยสำหรับ Vaultwarden แบบ self-hosted จะอธิบายรายละเอียดของทั้งสองประเด็นนี้
เหตุใดการคัดลอกไฟล์ db.sqlite3 ขณะที่ Vaultwarden ทำงานอยู่จึงไม่ใช่การสำรองข้อมูล
โดยปกติแล้ว Vaultwarden จะรัน SQLite ในโหมด WAL (ENABLE_DB_WAL=true) ข้อมูลที่เขียนจะถูกบันทึกลงใน db.sqlite3-wal ก่อน และจะถูกรวมเข้ากับ db.sqlite3 ก็ต่อเมื่อมีการทำ checkpoint เท่านั้น หากคุณคัดลอกเพียง db.sqlite3 คุณจะได้ฐานข้อมูล ณ เวลาที่ทำ checkpoint ครั้งล่าสุด ซึ่งหมายความว่ารหัสผ่านที่บันทึกไว้เมื่อสิบนาทีก่อนอาจหายไปจากไฟล์สำรองของคุณโดยไม่มีการแจ้งเตือนใดๆ
การคัดลอกไฟล์ทั้งสามไฟล์ด้วย cp ก็ไม่ใช่วิธีแก้ไขที่ถูกต้องเช่นกัน เนื่องจากไฟล์ถูกคัดลอกในช่วงเวลาที่เหลื่อมกันเล็กน้อย ไฟล์ WAL ที่คุณบันทึกไว้อาจระบุถึงเวอร์ชันของหน้าข้อมูล (page) ที่ไม่ตรงกับไฟล์หลักที่คุณบันทึกไว้ ส่งผลให้ SQLite พยายามกู้คืนข้อมูลจากไฟล์หนึ่งไปยังอีกไฟล์หนึ่งจนเกิดความผิดพลาด ซึ่งคุณจะพบปัญหานี้ในภายหลัง:
Error: database disk image is malformed.backup ช่วยหลีกเลี่ยงปัญหานี้ได้เนื่องจากใช้ Online Backup API ของ SQLite ซึ่งเป็นวิธีที่ SQLite ระบุไว้สำหรับการคัดลอกฐานข้อมูลที่กำลังใช้งานอยู่ โดยจะอ่านหน้าข้อมูลภายใต้สถานะ read lock และจะเริ่มกระบวนการใหม่หากมีการเขียนข้อมูลลงในไฟล์ระหว่างการคัดลอก ทำให้ข้อมูลที่บันทึกลงดิสก์มีความสอดคล้องกัน ณ ช่วงเวลาใดเวลาหนึ่งอย่างแท้จริง
สำรองข้อมูลฐานข้อมูลด้วย sqlite3 .backup
sudo apt update && sudo apt install -y sqlite3
sudo install -d -m 700 /var/backups/vaultwarden
OUT=/var/backups/vaultwarden/db-$(date '+%Y%m%d-%H%M').sqlite3
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 ".backup '$OUT'"
sudo sqlite3 "$OUT" "PRAGMA integrity_check;"คำสั่งสุดท้ายจะแสดง ok ในบรรทัดของตัวเอง หากแสดงเป็นอย่างอื่นถือว่าไฟล์สำรองนั้นใช้งานไม่ได้ ห้ามเก็บไฟล์ดังกล่าวไว้และห้ามลบไฟล์สำรองชุดก่อนหน้าทิ้ง กระบวนการทั้งหมดนี้ทำงานกับเซิร์ฟเวอร์ที่กำลังรันอยู่ จึงไม่มีการตัดการเชื่อมต่อของผู้ใช้และไม่มีการรีสตาร์ทคอนเทนเนอร์
เครื่องมือ sqlite3 ไม่ได้ถูกรวมอยู่ในคอนเทนเนอร์ Vaultwarden ตัวอิมเมจถูกสร้างขึ้นบน debian:trixie-slim ด้วย ca-certificates, curl, libmariadb3, libpq5 และ openssl ดังนั้น docker exec vaultwarden sqlite3 ... จึงล้มเหลวพร้อมข้อความ:
exec: "sqlite3": executable file not found in $PATHให้รันคำสั่งบนโฮสต์โดยอ้างอิงจาก path ที่ mount ไว้แทน ซึ่งเป็นสิ่งที่คำสั่งข้างต้นได้ดำเนินการไปแล้ว หากข้อมูลอยู่ใน named volume คำสั่ง docker volume inspect <name> จะแสดง path บนโฮสต์ภายใต้ /var/lib/docker/volumes/
Vaultwarden ได้เพิ่มคำสั่งสำรองข้อมูลของตัวเองมาตั้งแต่เวอร์ชัน 1.32.1 บนเซิร์ฟเวอร์ของคุณ:
docker exec -it vaultwarden /vaultwarden backupคำสั่งนี้จะรัน VACUUM INTO และเขียนไฟล์ db_YYYYMMDD_HHMMSS.sqlite3 ลงในโฟลเดอร์ข้อมูล มีสองสิ่งที่ต้องพิจารณาคือ ไฟล์สำรองจะถูกวางไว้ข้างไฟล์ต้นฉบับในดิสก์ลูกเดียวกัน ดังนั้นนี่จึงเป็นเพียงขั้นตอนการเตรียมข้อมูล (staging) ไม่ใช่การสำรองข้อมูลที่สมบูรณ์ และคำสั่งนี้รองรับเฉพาะ SQLite เท่านั้น หากใช้ MariaDB หรือ PostgreSQL ระบบจะหยุดทำงานพร้อมข้อความ The database type is not SQLite. Backups only works for SQLite databases
ไฟล์ที่มักถูกลืม
attachments/ เก็บข้อมูลที่ถูกเข้ารหัสไว้ภายใต้ชื่อไฟล์แบบสุ่ม ฐานข้อมูลสำหรับไฟล์แนบแต่ละรายการจะเก็บชื่อไฟล์ที่เข้ารหัสและข้อมูลกุญแจที่ไคลเอนต์ต้องใช้ในการถอดรหัสไฟล์นั้น หากไม่มีฐานข้อมูล ไฟล์แนบจะเป็นเพียงข้อมูลที่อ่านไม่ได้ และหากไม่มีไฟล์แนบ ฐานข้อมูลจะแสดงรายการที่ผู้ใช้ไม่สามารถดาวน์โหลดได้ ดังนั้นควรสำรองข้อมูลทั้งสองส่วนไปพร้อมกันในการรันคำสั่งเดียว
config.json เก็บทุกการตั้งค่าที่คุณบันทึกผ่านหน้าผู้ดูแลระบบ โดยค่าในไฟล์นี้จะมีลำดับความสำคัญเหนือกว่า environment variable ที่เกี่ยวข้อง ซึ่งส่งผลสองทางคือ การกู้คืน config.json เวอร์ชันเก่าจะเข้าไปแทนที่การตั้งค่าในไฟล์ compose ของคุณโดยอัตโนมัติ และตัวไฟล์เองก็มีความสำคัญเนื่องจากอาจเก็บรหัสผ่าน SMTP และโทเค็นผู้ดูแลระบบของคุณไว้ ควรจัดเก็บโทเค็นดังกล่าวในรูปแบบสตริง Argon2id PHC (password hashing competition) แทนการเก็บเป็นข้อความธรรมดา โดยคุณสามารถใช้ docker run --rm -it vaultwarden/server /vaultwarden hash เพื่อสร้างสตริงดังกล่าวได้
rsa_key.pem ใช้สำหรับลงนาม JSON web tokens (JWT) ที่ใช้รักษาเซสชันการล็อกอินของไคลเอนต์ หากไฟล์นี้หายไปในขณะเริ่มระบบ Vaultwarden จะสร้างกุญแจใหม่ขึ้นมา ทำให้โทเค็นทั้งหมดที่ลงนามด้วยกุญแจเก่าไม่สามารถใช้งานได้และไคลเอนต์ทั้งหมดจะถูกล็อกเอาต์ อย่างไรก็ตาม ข้อมูลใน Vault จะยังคงอยู่เนื่องจากถูกเข้ารหัสด้วยกุญแจที่สร้างจากรหัสผ่านหลัก การกู้คืนไฟล์กุญแจนี้จะช่วยป้องกันไม่ให้เกิดการล็อกเอาต์หมู่
sends/ เก็บไฟล์ที่อยู่เบื้องหลังลิงก์ Send หากไฟล์เหล่านี้หายไป จะส่งผลให้การดาวน์โหลดผ่านลิงก์ดังกล่าวใช้งานไม่ได้ แต่จะไม่กระทบต่อส่วนอื่นของระบบ
รวมทุกอย่างไว้ในสคริปต์เดียว
#!/bin/bash
set -euo pipefail
DATA=/opt/vaultwarden/data
DEST=/var/backups/vaultwarden
STAMP=$(date '+%Y%m%d-%H%M%S')
STAGE=$(mktemp -d /tmp/vw-stage.XXXXXX)
install -d -m 700 "$DEST"
sqlite3 "$DATA/db.sqlite3" ".backup '$STAGE/db.sqlite3'"
test "$(sqlite3 "$STAGE/db.sqlite3" 'PRAGMA integrity_check;')" = "ok"
cp -a "$DATA"/rsa_key* "$STAGE/"
for extra in config.json attachments sends; do
if [ -e "$DATA/$extra" ]; then cp -a "$DATA/$extra" "$STAGE/"; fi
done
tar -C "$STAGE" -czf "$DEST/vw-$STAMP.tar.gz" .
chmod 600 "$DEST/vw-$STAMP.tar.gz"
rm -rf "$STAGE"
tar -tzf "$DEST/vw-$STAMP.tar.gz"บันทึกไฟล์เป็น /usr/local/sbin/vw-backup.sh, ทำการ chmod 700 ไฟล์ และรันด้วยสิทธิ์ root บรรทัด test คือส่วนที่ทำงานจริง โดย sqlite3 จะคืนค่าสถานะ 0 แม้ว่า PRAGMA integrity_check จะรายงานว่าข้อมูลเสียหาย ดังนั้นการเปรียบเทียบผลลัพธ์กับ ok จึงเป็นสิ่งที่เปลี่ยนการคัดลอกที่ผิดพลาดให้กลายเป็นสคริปต์ที่ล้มเหลว ส่วน set -euo pipefail จะหยุดการทำงานทั้งหมด แทนที่จะปล่อยให้ tar สร้างไฟล์ archive ที่สมบูรณ์รอบฐานข้อมูลที่เสียหาย
คำสั่ง tar -tzf บรรทัดสุดท้ายจะแสดงรายการสิ่งที่คุณสำรองข้อมูลไว้จริง ให้อ่านผลลัพธ์ในครั้งแรก คุณต้องตรวจสอบหา ./db.sqlite3, ./rsa_key.pem, ./config.json และ ./attachments/ และตรวจสอบว่าไม่มี ./db.sqlite3-wal ปรากฏอยู่ ให้รันสคริปต์นี้ทุกคืนด้วย systemd service และ timer แทนการใช้ cron หากคุณต้องการผลลัพธ์แบบ journalctl และต้องการ unit ที่สามารถรายงานความล้มเหลวได้
ตรวจสอบข้อมูลสำรองโดยการกู้คืนลงในไดเรกทอรีชั่วคราว
ข้อมูลสำรองที่ไม่ได้ผ่านการทดสอบถือเป็นเพียงการคาดเดา การกู้คืนข้อมูลลงในไดเรกทอรีชั่วคราวใช้เวลาเพียงหนึ่งนาทีและไม่ส่งผลกระทบต่อระบบที่ใช้งานจริง
sudo install -d -m 700 /tmp/vw-check
sudo tar -C /tmp/vw-check -xzf /var/backups/vaultwarden/vw-20260805-030000.tar.gz
ls -l /tmp/vw-check
sudo sqlite3 /tmp/vw-check/db.sqlite3 "PRAGMA integrity_check;"
sudo sqlite3 /tmp/vw-check/db.sqlite3 "select count(*) from users;"
sudo sqlite3 /tmp/vw-check/db.sqlite3 "select count(*) from ciphers;"
sudo du -sh /tmp/vw-check/attachmentsผลลัพธ์สี่ประการที่มีความสำคัญ integrity_check จะแสดงผล ok จำนวนผู้ใช้งานต้องตรงกับจำนวนบัญชีที่คุณทราบอยู่แล้ว จำนวน cipher ต้องใกล้เคียงกับตัวเลขจากระบบจริงที่ได้จาก sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;" และต้องไม่เป็นศูนย์สำหรับ vault ที่มีการใช้งานอยู่ ไดเรกทอรี attachments ควรมีขนาดใกล้เคียงกับที่คุณคาดไว้ (คุณสามารถข้ามขั้นตอนนี้ได้หากไม่มีการอัปโหลดไฟล์แนบ) จากนั้นให้รัน sudo rm -rf /tmp/vw-check เนื่องจากไดเรกทอรีดังกล่าวจะมีสำเนาชุดที่สองของข้อมูลทั้งหมดเก็บไว้
กฎข้อหนึ่งเมื่อคุณกู้คืนโฟลเดอร์ข้อมูลที่คัดลอกด้วยตนเองคือ ให้ลบ db.sqlite3-wal และ db.sqlite3-shm ออกก่อนเริ่มการทำงานของเซิร์ฟเวอร์ มิฉะนั้น SQLite จะพยายามกู้คืนฐานข้อมูลที่กู้คืนมาโดยใช้ log ที่เป็นของสำเนาอื่น ซึ่งจะทำให้ฐานข้อมูลที่สมบูรณ์เกิดความเสียหายได้ ไฟล์ archive ที่สร้างจากสคริปต์ข้างต้นจะไม่มีไฟล์เหล่านี้อยู่ เนื่องจาก .backup จะเขียนฐานข้อมูลที่สมบูรณ์ออกมาเป็นไฟล์เดียว
การกู้คืนข้อมูลลงบนเซิร์ฟเวอร์
คำสั่งเหล่านี้ให้รันบนเซิร์ฟเวอร์ของคุณเองในขณะที่ container หยุดทำงานอยู่ Vaultwarden จะต้องไม่เขียนข้อมูลใดๆ ในขณะที่โฟลเดอร์ข้อมูลมีการเปลี่ยนแปลง
cd /opt/vaultwarden
docker compose stop vaultwarden
sudo mv data data.old.$(date '+%Y%m%d-%H%M%S')
sudo install -d -m 700 data
sudo tar -C data -xzf /var/backups/vaultwarden/vw-20260805-030000.tar.gz
sudo chown -R root:root data
docker compose start vaultwarden
docker compose logs --tail 20 vaultwardenchown จะต้องระบุชื่อผู้ใช้ที่ container ใช้งานอยู่ โดยปกติแล้ว image มาตรฐานจะรันด้วยสิทธิ์ root ดังนั้น root:root จึงถูกต้อง เว้นแต่คุณจะตั้งค่า user: ไว้ในไฟล์ compose ของคุณ ซึ่งในกรณีนั้นให้ใช้ uid และ gid ตามที่กำหนดไว้ หากโฟลเดอร์ข้อมูลไม่มีสิทธิ์ให้เซิร์ฟเวอร์เขียนได้ คุณจะพบหน้าล็อกอินที่ล้มเหลวในทุกคำขอ และจะมีข้อความแจ้งเตือนใน log
การเริ่มต้นระบบที่สมบูรณ์จะจบลงด้วยบรรทัด Rocket:
[INFO] Rocket has launched from http://0.0.0.0:80จากนั้นให้ล็อกอินผ่านเบราว์เซอร์ เปิดรายการข้อมูล และลองดาวน์โหลดไฟล์แนบหนึ่งรายการ หากล็อกอินได้แต่ดาวน์โหลดไฟล์แนบไม่สำเร็จ แสดงว่าไฟล์สำรองมีฐานข้อมูลแต่ไม่มี attachments/ ให้เก็บ data.old.* ไว้จนกว่าจะตรวจสอบทุกอย่างเรียบร้อยแล้วจึงค่อยลบออก การย้อนกลับ (rollback) ให้ทำตามขั้นตอนเดิมทั้ง 3 ขั้นตอนโดยสลับตำแหน่งไดเรกทอรี
หาก path ของคุณไม่ตรงกับที่ระบุไว้ที่นี่ คู่มือการติดตั้ง Vaultwarden สำหรับ VPS จะแสดงไฟล์ compose ที่คำสั่งเหล่านี้อ้างอิงถึง
สถานที่ที่ไม่ควรเก็บข้อมูลสำรอง
- ไม่ควรเก็บไว้ในดิสก์ลูกเดียวกับโฟลเดอร์ข้อมูล หากโวลุ่มเสียหาย ข้อมูลทั้งสองชุดจะสูญหายไปพร้อมกัน รวมถึงกรณีที่เกิด
rm -rfบนพาธที่ไม่ถูกต้องด้วย - ไม่ควรเก็บไว้บนเซิร์ฟเวอร์เครื่องเดียวกัน แม้จะเป็นโวลุ่มที่สองก็ตาม หากผู้โจมตีเข้าถึงสิทธิ์ root ได้ ก็จะเข้าถึงข้อมูลสำรองของคุณได้ในเซสชันเดียวกัน
- ไม่ควรเก็บไว้ใน object storage โดยไม่มีการเข้ารหัส เนื่องจากไฟล์เก็บถาวรมีทั้งที่อยู่อีเมล, คำใบ้รหัสผ่าน, รหัสกู้คืน และข้อมูล vault ที่ถูกเข้ารหัสไว้ ซึ่งสามารถถูกโจมตีแบบออฟไลน์ได้
- ไม่ควรพึ่งพาเพียงแค่ snapshot ของผู้ให้บริการเท่านั้น แม้ว่า snapshot จะกู้คืนได้รวดเร็วและมีประโยชน์ แต่เนื่องจากมันอยู่ในบัญชีเดียวกับเซิร์ฟเวอร์ หากบัญชีผู้ใช้มีปัญหา ข้อมูลสำรองเหล่านั้นก็จะสูญหายไปด้วย
การเก็บสำเนาไว้ภายนอกสถานที่ (offsite) คือจุดที่ restic เข้ามามีบทบาท เนื่องจาก restic repository จะถูกเข้ารหัสบนเครื่องก่อนที่จะอัปโหลดข้อมูลใดๆ บนเซิร์ฟเวอร์ของคุณ:
sudo apt install -y restic
export RESTIC_REPOSITORY=s3:https://s3.example.com/vaultwarden-backups
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init
restic backup /var/backups/vaultwarden --tag vaultwarden
restic snapshots --tag vaultwarden
restic forget --tag vaultwarden --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --pruneให้ชี้ restic ไปที่ไดเรกทอรีเก็บถาวร ไม่ใช่โฟลเดอร์ข้อมูลที่ใช้งานจริง เพื่อให้สิ่งที่อัปโหลดเป็นสำเนาที่สอดคล้องกับที่คุณตรวจสอบไว้แล้ว เก็บพาสเวิร์ดของ repository ไว้ในที่อื่นที่ไม่ใช่เซิร์ฟเวอร์ที่มันปกป้องอยู่ หากทำพาสเวิร์ดหาย ข้อมูล snapshot จะไม่สามารถอ่านได้ตามการออกแบบ ในกรณีที่ระบบจัดเก็บข้อมูลรองรับ ให้กำหนดสิทธิ์ให้เซิร์ฟเวอร์สามารถเขียนข้อมูลได้แต่ลบไม่ได้ เพื่อป้องกันไม่ให้ผู้โจมตีที่ยึดเครื่องได้ทำการลบประวัติการสำรองข้อมูลของตนเอง การตั้งค่าการสำรองข้อมูลด้วย restic บน VPS ครอบคลุมรายละเอียดเกี่ยวกับ repository และตารางเวลาทั้งหมด และ การเปรียบเทียบ restic กับ BorgBackup จะช่วยคุณตัดสินใจหากคุณยังไม่ได้เลือกเครื่องมือที่ต้องการ
ทดสอบการกู้คืนตามกำหนดการ
เลือกหนึ่งวันต่อเดือน ดึง snapshot ล่าสุดมาไว้ในไดเรกทอรีชั่วคราวด้วย restic restore latest --tag vaultwarden --target /tmp/vw-check จากนั้นรันคำสั่ง PRAGMA integrity_check ชุดเดิม และตรวจสอบจำนวนแถวข้อมูล จากนั้นให้บันทึกวันที่และจำนวนแถวที่ได้ไว้ การสำรองข้อมูลที่ไม่มีใครเคยทดสอบกู้คืนมานานกว่าหกเดือนถือเป็นการสำรองข้อมูลที่ไม่ทราบสถานะที่แท้จริง และหากคุณเพิ่งมาทราบสถานะของมันในช่วงที่ระบบล่ม นั่นจะเป็นช่วงเวลาที่เลวร้ายที่สุดในการเรียนรู้
ปีละหนึ่งครั้ง ให้ทำการทดสอบแบบเต็มรูปแบบ โดยเริ่มการทำงานของคอนเทนเนอร์ Vaultwarden ตัวที่สองบนพอร์ตสำรองโดยใช้โฟลเดอร์ข้อมูลที่กู้คืนมา แล้วลองเข้าสู่ระบบด้วยบัญชีผู้ใช้งานจริง วิธีนี้จะช่วยยืนยันเส้นทางการตรวจสอบ master password ได้ตั้งแต่ต้นจนจบ ซึ่งการนับจำนวนแถวเพียงอย่างเดียวไม่สามารถทำได้ การรัน restic check --read-data-subset=10% ตามกำหนดการเดียวกันจะช่วยตรวจสอบว่าข้อมูลที่จัดเก็บไว้นั้นสามารถอ่านได้จริง ไม่ใช่เพียงแค่มีรายชื่อไฟล์ปรากฏอยู่เท่านั้น
FAQ
ฉันสามารถใช้คำสั่ง cp เพื่อคัดลอกไฟล์ db.sqlite3 ในขณะที่ Vaultwarden กำลังทำงานอยู่ได้หรือไม่?
ไม่ได้ Vaultwarden ทำงาน SQLite ในโหมด WAL ดังนั้นข้อมูลที่เขียนล่าสุดจะค้างอยู่ใน db.sqlite3-wal และยังไม่ได้ถูกรวมเข้ากับ db.sqlite3 การคัดลอกเฉพาะไฟล์หลักด้วย cp จะทำให้ข้อมูลส่วนนี้สูญหายไปโดยไม่มีการแจ้งเตือน และการคัดลอกทั้งสองไฟล์แยกกันอาจทำให้ได้ไฟล์ที่ไม่สัมพันธ์กัน ซึ่งจะกลายเป็น Error: database disk image is malformed ในภายหลัง ให้ใช้ sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'" แทน คำสั่งนี้ใช้ Online Backup API ของ SQLite และสร้างไฟล์ที่สอดคล้องกันหนึ่งไฟล์ในขณะที่เซิร์ฟเวอร์ยังคงให้บริการตามปกติ
ฉันจำเป็นต้องหยุดคอนเทนเนอร์ Vaultwarden เพื่อสำรองข้อมูลหรือไม่?
ไม่จำเป็น และนั่นคือจุดประสงค์ของ .backup การคัดลอกฐานข้อมูลมีความปลอดภัยแม้เซิร์ฟเวอร์กำลังทำงานอยู่ ไฟล์แนบและไฟล์ที่ส่งผ่าน Send จะถูกเขียนลงดิสก์เมื่อผู้ใช้อัปโหลด ดังนั้นไฟล์ที่เพิ่มเข้ามาในช่วงเวลาระหว่างการคัดลอกฐานข้อมูลและการทำ tar อาจไม่ถูกรวมอยู่ในไฟล์สำรองของคืนนั้น ซึ่งกรณีที่แย่ที่สุดคือคุณจะเสียไฟล์แนบไปเพียงหนึ่งไฟล์ หากคุณไม่กังวลเรื่องการหยุดทำงานเพียงไม่กี่วินาที การใช้ docker compose stop ก่อนเริ่มสคริปต์และ docker compose start หลังจากนั้นจะช่วยป้องกันปัญหานี้ได้
จะเกิดอะไรขึ้นหากฉันกู้คืนข้อมูลโดยไม่มีไฟล์ rsa_key?
Vaultwarden จะสร้างคีย์ใหม่เมื่อเริ่มต้นระบบ คีย์ดังกล่าวใช้สำหรับลงนาม JSON web tokens (JWT) ที่รักษาเซสชันของผู้ใช้ไว้ ดังนั้นโทเค็นที่มีอยู่ทั้งหมดจะตรวจสอบความถูกต้องไม่ได้ ส่งผลให้ไคลเอนต์ทั้งหมดถูกออกจากระบบและต้องลงชื่อเข้าใช้ใหม่ เนื้อหาใน Vault จะไม่ได้รับผลกระทบเนื่องจากถูกเข้ารหัสด้วยคีย์ที่มาจากรหัสผ่านหลักของผู้ใช้แต่ละคน ไม่ใช่ด้วย RSA key ให้กู้คืน rsa_key.pem ไปพร้อมกับข้อมูลส่วนที่เหลือในโฟลเดอร์ข้อมูล แล้วจะไม่มีใครสังเกตเห็นการกู้คืนนั้น
ไฟล์สำรองที่เก็บไว้ปลอดภัยพอที่จะอัปโหลดไปยัง object storage เลยหรือไม่?
ไม่ปลอดภัย ชื่อรายการ รหัสผ่าน และบันทึกย่อเป็นข้อความที่เข้ารหัสไว้ (ciphertext) แต่ที่อยู่อีเมล ชื่อบัญชี คำใบ้รหัสผ่าน และรหัสกู้คืนแบบสองปัจจัย (two-factor recovery codes) เป็นข้อความธรรมดา (plain text) อยู่ในฐานข้อมูล และผู้โจมตีแบบออฟไลน์สามารถถอดรหัส ciphertext ได้ตามต้องการ ให้เข้ารหัสไฟล์สำรองก่อนที่จะนำออกจากเครื่อง ที่เก็บข้อมูลของ restic สามารถจัดการเรื่องนี้ให้คุณได้ และ gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz จะสร้างไฟล์ที่เข้ารหัสเพียงไฟล์เดียวที่คุณสามารถนำไปเก็บไว้ที่ใดก็ได้
ฉันจะสำรองข้อมูล Vaultwarden ที่ใช้ PostgreSQL หรือ MariaDB ได้อย่างไร?
ขั้นตอนสำหรับ SQLite ไม่สามารถนำมาใช้ได้ และคำสั่งที่มีมาให้จะปฏิเสธการทำงานด้วย The database type is not SQLite. Backups only works for SQLite databases ให้ทำการดัมพ์ฐานข้อมูลด้วยเครื่องมือเฉพาะของระบบนั้นๆ คือ pg_dump หรือ mysqldump และปฏิบัติตามกฎข้ออื่นๆ เหมือนเดิม ไฟล์ดัมพ์ควรถูกรวมอยู่ในไฟล์สำรองชุดเดียวกับ attachments/, sends/, config.json และไฟล์ rsa_key โดยต้องทำในรอบการสำรองข้อมูลเดียวกัน เข้ารหัสไฟล์ และจัดเก็บไว้ในที่อื่นที่ไม่ใช่เซิร์ฟเวอร์ที่สร้างไฟล์นั้นขึ้นมา