วิธีสำรองข้อมูลและกู้คืน Vaultwarden บน VPS อย่างปลอดภัย
เรียนรู้วิธีสำรองข้อมูล Vaultwarden บน VPS ด้วยคำสั่ง sqlite3 .backup เพื่อความปลอดภัยของฐานข้อมูล พร้อมวิธีเก็บไฟล์ attachments, config.json และ rsa_key อย่างครบถ้วน
สิ่งที่การสำรองข้อมูล Vaultwarden ต้องมี
การสำรองข้อมูล Vaultwarden คือการคัดลอกโฟลเดอร์ข้อมูลทั้งหมด โดยฐานข้อมูลที่อยู่ภายในจะต้องถูกคัดลอกอย่างถูกวิธี ให้ใช้ sqlite3 db.sqlite3 ".backup out.sqlite3" แทนการใช้ cp เพราะการคัดลอกไฟล์ฐานข้อมูลโดยตรงในขณะที่มีการเขียนข้อมูลอยู่อาจทำให้ได้ไฟล์ที่ไม่สามารถเปิดใช้งานได้ จากนั้นให้เก็บไฟล์ที่เกี่ยวข้องทั้งหมดไว้ด้วยกัน ซึ่งเป็นส่วนที่หลายคนมักลืม
สำหรับการติดตั้งผ่าน Docker โฟลเดอร์ข้อมูลคือตำแหน่งที่คุณ mount ไว้ที่ /data ซึ่งอาจเป็น path บน host หรือ 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: การตั้งค่าทั้งหมดที่คุณบันทึกไว้จากหน้า adminrsa_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;"คำสั่งแรกจะแสดงที่อยู่อีเมลของผู้ใช้ของคุณในรูปแบบข้อความธรรมดา คำสั่งที่สองจะแสดงชื่อรายการหนึ่งรายการ ซึ่งมีลักษณะดังนี้:
2.k9Qw1nQ0y7Yy2Xw==|E1r0J3l5s7d9f1g3h5j7k9==|Lm4nOp6qRs8tUv0wXy2zAb4cDe6fGh8i=ชื่อรายการ ชื่อผู้ใช้ รหัสผ่าน และบันทึกต่างๆ จะถูกเข้ารหัสโดยไคลเอนต์ก่อนที่จะถูกส่งออกไป ดังนั้นเซิร์ฟเวอร์จึงจัดเก็บเฉพาะข้อมูลที่เข้ารหัส (ciphertext) ซึ่งเซิร์ฟเวอร์ไม่สามารถอ่านได้ คำนำหน้า 2. คือประเภทการเข้ารหัสของ Bitwarden ตามด้วย initialisation vector (IV), ข้อมูลที่เข้ารหัส และ MAC (message authentication code) โดยทั้งหมดอยู่ในรูปแบบ base64 และคั่นด้วย | กุญแจที่ใช้ถอดรหัสข้อมูลนี้ได้มาจากรหัสผ่านหลัก (master password) ของบัญชี ซึ่งรหัสผ่านนี้จะไม่ถูกส่งไปยังเซิร์ฟเวอร์ในรูปแบบที่นำไปใช้งานได้จริง ส่วนนี้เหมือนกันไม่ว่าคุณจะใช้งาน Vaultwarden หรือเซิร์ฟเวอร์ทางการ ดังที่ระบุไว้ใน การเปรียบเทียบระหว่าง Vaultwarden และ Bitwarden ที่โฮสต์เอง
ข้อมูลส่วนที่เหลือของฐานข้อมูลไม่ได้ถูกเข้ารหัส ที่อยู่อีเมล ชื่อบัญชี คำใบ้รหัสผ่าน และรหัสกู้คืนการยืนยันตัวตนสองชั้น (two-factor recovery codes) จะถูกจัดเก็บเป็นข้อความธรรมดา ควบคู่ไปกับข้อมูลเมตา เช่น เวลาที่สร้างรายการ และองค์กรใดที่เป็นเจ้าของรายการนั้น ดังนั้นไฟล์สำรองข้อมูลจึงถือเป็นความลับในตัวมันเอง ใครก็ตามที่ครอบครองไฟล์นี้จะทราบว่าผู้ใช้ของคุณคือใคร และสามารถโจมตีข้อมูลที่เข้ารหัสแบบออฟไลน์ได้ด้วยความเร็วเท่าที่ฮาร์ดแวร์ของพวกเขาจะทำได้ ข้อเท็จจริงเพียงข้อเดียวนี้เป็นเหตุผลที่ทำให้ต้องมีกฎการจัดเก็บข้อมูลในส่วนถัดไป: สำเนาข้อมูลจะต้องถูกเข้ารหัสก่อนที่จะถูกย้ายออกจากเซิร์ฟเวอร์
เหตุใดการคัดลอก 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 ที่มาจากสำเนาอื่น ซึ่งจะทำให้ฐานข้อมูลที่สมบูรณ์เกิดความเสียหายได้ ไฟล์เก็บถาวรที่สร้างโดยสคริปต์ข้างต้นจะไม่มีไฟล์เหล่านี้อยู่ เนื่องจาก .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 ciphertext) ซึ่งอาจถูกนำไปโจมตีแบบออฟไลน์ได้
- ห้ามพึ่งพาเพียงแค่ 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 ไปที่ไดเรกทอรีสำรองข้อมูล (archive directory) แทนที่จะเป็นโฟลเดอร์ข้อมูลที่ใช้งานจริง เพื่อให้สิ่งที่อัปโหลดเป็นสำเนาที่สอดคล้องกันตามที่คุณตรวจสอบไว้แล้ว ควรเก็บรหัสผ่านของ repository ไว้ในที่อื่นที่ไม่ใช่เซิร์ฟเวอร์ที่ต้องการปกป้อง หากทำรหัสผ่านนั้นหาย ข้อมูลใน snapshot จะไม่สามารถอ่านได้ตามการออกแบบของระบบ หากพื้นที่จัดเก็บรองรับ ให้กำหนดสิทธิ์แก่เซิร์ฟเวอร์ให้สามารถเขียนข้อมูลได้แต่ห้ามลบ เพื่อป้องกันไม่ให้ผู้โจมตีที่ยึดเครื่องได้ลบประวัติการสำรองข้อมูลของตนเอง การตั้งค่าการสำรองข้อมูลด้วย restic บน VPS ครอบคลุมรายละเอียดเกี่ยวกับ repository และตารางเวลาทั้งหมด และ การเปรียบเทียบ restic กับ BorgBackup จะช่วยคุณตัดสินใจหากคุณยังไม่ได้เลือกเครื่องมือที่ต้องการ
ทดสอบการกู้คืนตามกำหนดการ
เลือกหนึ่งวันต่อเดือน ดึง snapshot ล่าสุดมาไว้ในไดเรกทอรีชั่วคราวด้วย restic restore latest --tag vaultwarden --target /tmp/vw-check จากนั้นรัน PRAGMA integrity_check ชุดเดิม และตรวจสอบจำนวนแถวข้อมูลเหมือนเดิม แล้วบันทึกวันที่และจำนวนแถวที่ได้ไว้ การสำรองข้อมูลที่ไม่มีใครเคยทดสอบกู้คืนมานานกว่าหกเดือนถือเป็นข้อมูลที่ไม่ทราบสถานะที่แท้จริง และคุณจะมาทราบสถานะของมันในช่วงที่ระบบล่ม ซึ่งเป็นเวลาที่เลวร้ายที่สุดในการรับรู้ปัญหา
ปีละหนึ่งครั้ง ให้ทำการทดสอบแบบเต็มรูปแบบ เริ่มต้น container ของ 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) เป็นข้อความธรรมดาในฐานข้อมูล และผู้โจมตีที่เข้าถึงข้อมูลแบบออฟไลน์สามารถถอดรหัส ciphertext ได้ตามต้องการ ให้เข้ารหัสไฟล์สำรองก่อนที่จะนำออกจากเครื่อง repository ของ 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 ให้ทำการ dump ฐานข้อมูลด้วยเครื่องมือเฉพาะของมัน คือ pg_dump หรือ mysqldump และปฏิบัติตามกฎข้ออื่นๆ เหมือนเดิม ไฟล์ dump ควรถูกรวมอยู่ในไฟล์สำรองเดียวกับ attachments/, sends/, config.json และไฟล์ rsa_key โดยต้องทำในรอบการสำรองข้อมูลเดียวกัน เข้ารหัสไฟล์ และจัดเก็บไว้ในที่อื่นที่ไม่ใช่เซิร์ฟเวอร์ที่สร้างไฟล์นั้นขึ้นมา