vaultwarden 用了几年,备份方案翻来覆去折腾了好多种,这次利用 nginx syslog 实现“精确”的备份:
log_format json-log escape=json '{"status":$status,"request_method":"$request_method","request_uri":"$request_uri"}';
server {
location / {
# ...
proxy_redirect off;
proxy_pass http://vaultwarden;
proxy_http_version 1.1;
access_log syslog:server=syslog-server:50333,facility=local7,tag=nginx,severity=info,nohostname json-log;
}
}
这样 syslog-server:50333
就可以收到完成的请求,可以拿到 status
request_method
request_uri
等信息,如此只需要实现个简单的 syslog server ,便可以根据这些信息来决定是否调用备份:例如所有 GET 请求都无需关心,因为对数据库没有更改;例如我不关心登录、注册、设备、二次验证之类的变更,则 ^/identity/accounts/prelogin
^/identity/connect/token
.*/devices/.*
.*/two-factor/.*
等请求也可以略过;例如我不使用 sends ,.*/sends/.*
也可以忽略。
bitwarden 客户端让每一次更改都必须对应 vaultwarden 数据库的更改,这个备份方案理论上也可以确保对自己有价值的变更都被备份。
部署运行了一周,我很喜欢这个方案
诸位觉得如何
很认同 #13 #15 提到的问题,所以从 access_log syslog server 改成实现一个简单的 reverse proxy,得益于 Go 核心库就有 https://pkg.go.dev/net/http/httputil#ReverseProxy ,这样一个简单实现的代码量很少,犯错的机会也就很少,维护时只需要保持 Go 是最新版本即可
func NewProxy(upstream string) (*httputil.ReverseProxy, error) {
u, err := url.Parse(upstream)
if err != nil {
return nil, err
}
return httputil.NewSingleHostReverseProxy(u), nil
}
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
}
}
func main() {
proxy, err := NewProxy("http://vaultwarden")
if err != nil {
panic(err)
}
proxy.ModifyResponse = func(response *http.Response) error {
// nil r.Request?
log.Println(response.Request.Method, response.Request.URL.Path, response.StatusCode)
// ...
return nil
}
http.HandleFunc("/", ProxyRequestHandler(proxy))
log.Fatal(http.ListenAndServe(":8080", nil))
}
1
hiplon 158 天前
crontab 全局备份方案
0 3 * * * zip -r /root/wardendata.zip /root/wardendata 5 3 * * * scp /root/wardendata.zip [email protected]:/home/xxx/. |
2
Xusually 158 天前 via iPhone
全量 zip 一下,占空间极小,没必要增加复杂度吧?
|
3
0o0O0o0O0o OP |
4
terry0314 158 天前
|
5
guisheng 158 天前 via iPhone
不理解,首先只要存入数据库中一般客户端都会及时更新,另外即使后台服务没有开启也能使用客户端只是不能新增(这点我感觉没做好,为什么不能先保存能通讯的时候在上传呢)。所以即使我没有及时备份也不会出现数据丢失的情况吧……
另外全量备份虽然大但是可以删除之前的。我都是整个 docker 挂载目录压缩。 |
6
RiddMa 158 天前
@0o0O0o0O0o 不明白为什么丢失密码很严重,不是有“忘记密码”功能吗?按天为单位备份感觉完全足够了,又不是天天在加新的密码。要是觉得不靠谱按小时按分钟也行,反正数据库很小。
真正重要的数据不应该依赖单一的备份手段,要是有完全不能丢的东西,应该在多个介质上重复保存。 |
7
Smilencer 158 天前 via iPhone
我是靠虚机镜像备份...
|
8
0o0O0o0O0o OP @guisheng #5 允许本地保存的话不可避免要处理多客户端的冲突吧,不太懂,但应该挺难妥善处理的? https://www.inkandswitch.com/local-first/
|
9
0o0O0o0O0o OP @RiddMa #6
> 不是有“忘记密码”功能吗? 确实,但有的东西也许不存在一个"忘记密码"功能 > 又不是天天在加新的密码 我倒觉得这恰恰是我追求每次变更都备份的重要支撑。。。变更不频繁所以完全可以每次都备份 > 真正重要的数据不应该依赖单一的备份手段 赞同,所以这个方案并不提及具体如何备份 并且我是在每次变更后备份并发送通知汇报备份结果,变更一定是我和 bitwarden 主动交互,所以如果出现了错误或者没收到通知,我也可以在很短的时间内得知并处理 |
10
greenskinmonster 158 天前
incron (根据文件变化触发命令) -> restic (快照、压缩、加密、去重) 感觉简单也更完善
|
11
0o0O0o0O0o OP @greenskinmonster #10 感谢,学到个命令 incron
我之前用的 inotifywatch ,有些问题,例如我提到的 "登录、注册、设备、二次验证之类的变更",每次登录都会导致数据库文件变化,而我并不需要处理这种变化,access_log 方案就是对此的升级 |
12
pems002 158 天前
虽然这种自托管就是自己对自己负责,但是 Bitwarden 或者说 Vaultwarden 并没有推出一个自己真真意义上的备份功能,所以我觉得其实备份的方法就因人而异了,但是楼主这个思路其实我觉得挺好的,比我单纯 cron 一个任务把压缩包传到网盘里的思路要好的多,我再观望一下改天试试看!
|
13
drymonfidelia 158 天前
根据我长期运维 nginx 的经验,nginx 在某些特殊情况(超时、源站返回的时候中断)有概率不写 access log ,还没有定时稳定
|
14
0o0O0o0O0o OP @pems002 #12 我有在维护基于此思路的 syslog server 简单实现和 docker compose file ,包括一些容器、Nginx 加固等,不过还很早期,等我继续查阅资料学习和优化一番再来分享
|
15
drymonfidelia 158 天前
另外如果你的 :50333 因为什么原因卡死了,nginx 也不会在他恢复正常的时候重发
|
16
drymonfidelia 158 天前
备份方案还是越简单越不容易出问题,我们以前也搞得这么复杂最后到要用备份的时候发现根本没备份进去,最后花了几十万找人恢复
|
17
0o0O0o0O0o OP @drymonfidelia #13 很好的提醒,谢谢。那后面有可能自己实现一个反代,实际上这个简单反代需求也不太需要 Nginx 。
|
18
zhhmax 158 天前
当你使用客户端的时候,创建或修改的密码必然是先在客户端上保存,然后再推送到服务端。使用 cron 命令定时备份密码文件到云盘,即便是服务器挂了,客户端上已经有最新的数据,也不会有任何密码丢失的情况发生吧,除非是登录网页操作新增密码而不使用任何客户端?。且密码文件体积很小,即便是每五分钟产生一个压缩包,不会有文件量太大没有地方保存的情况出现,如果彻底删了某个密码,也能根据每 5 分钟的备份找回来,很难再想到使用 cron 命令备份还会有什么极端情况发生导致密码同步出问题/丢失密码的情况发生。
|
19
0o0O0o0O0o OP @zhhmax #18
> 当你使用客户端的时候,创建或修改的密码必然是先在客户端上保存,然后再推送到服务端 https://bitwarden.com/help/using-bitwarden-offline/ Any unlocked Bitwarden app can be used offline in read-only mode ... you won't be able to make edits to or add vault items > 每五分钟产生一个压缩包 不赞同,我认为和编程一样,自然是越精确越好,对于一些用户来说,为了可能几天才出现一次的变更,何必用这么频繁的备份呢? |
20
RoccoShi 158 天前
这太细了, 我都直接一把梭 cron 每天凌晨停 container 把所有 data volume 打包上传到 google drive 和 onedrive 🤣
|
21
zhhmax 158 天前
@0o0O0o0O0o #19
1 、说明你的后端已经挂了,没法新增,何谈丢失? 2 、每五分钟产生一个压缩包,完全可以凭借一些手段把一段时间内没有变更的压缩包废弃掉,这样的话所有的压缩包也都是密码库产生变动后的文件,照样达成精确备份所有变动的目的。 |
22
ashong 158 天前
vaultwarden-backup 打包加密 再上传网盘
|
23
0o0O0o0O0o OP @zhhmax #21
> 凭借一些手段把一段时间内没有变更的压缩包废弃掉 既然这些手段都是为了达成相同目标,那 cron 相对于这个方案还有哪些优势呢。。。 目前认可的是这个方案增加了复杂性所以引入了 #13 #15 提到的问题,还有别的什么?这个问题对我来说也算有解 并且例如 #11 提到的,其实靠各种意义上的 diff ( binary 或是 SQL plain text )都挺难处理到 access_log 方案这样"精确"的 |
24
viWww0vvxmolvY5p 158 天前
没看懂。vaultwarden 新增或修改了账号密码更新会立即同步到所有客户端上,就算服务器停机了,数据库重置了,客户端仍可以打开并导出所有资料,理论上不会丢失。
|
25
zhhmax 158 天前
@0o0O0o0O0o #23 如果要做到“精确且有必要的备份”,那靠一些简单的 diff 确实比较难实现。我个人更倾向于简单又稳定的 cron ,最大程度确保不会丢数据,可以不用那么精确(在某些极端情况下可能会增加恢复时的工作量)。当然了说这么多没有一定要说服你的意思,讨论嘛,自己用起来舒服的方案那就是最好的方案。
|
26
kuanat 158 天前
稍微跑个题,既然变更历史那么重要,有没有考虑过用 git 和文本文件做密码管理呢?
使用 git 你可以天然记录变更历史,同时可以任意 self host 或者利用各种公有设施实现分布式备份。 缺点是文本文件要加密。那就再套一层 gpg 加密好了,git 管理 gpg 加密后的文本文件。 剩下的事情就是写个 wrapper 把这个操作简化掉。 如果你认为这个思路合理的话,可以参考 pass - the standard unix password manager https://www.passwordstore.org/ 我已经用了十多年了,在各种平台都有开源可自编译的客户端或命令行工具。 |
27
0o0O0o0O0o OP @kuanat #26
> git 可以天然记录变更历史 我以前就有这么做的,因为 vaultwarden 默认使用 sqlite ,密码相关的数据全部在 sqlite 文件中,我就是通过 .dump 转为 sql 文件加到 .git 里来追踪变更,类似于 https://news.ycombinator.com/item?id=38110286 |
28
0o0O0o0O0o OP @zhhmax #25
> 在某些极端情况下可能会增加恢复时的工作量 赞同,不过对于自建密码管理器我很重视: - 很关注 bitwarden 客户端的 issues ,在更新 vaultwarden 之前都会像这样 https://github.com/dani-garcia/vaultwarden/compare/1.30.4...1.30.5 查看改了什么,所以基本上可以确保不会因为一些 breaking change 丢失数据 - 定期会做还原演练 |
29
masir 158 天前
定时任务 每天压缩备份整个目录到 onedrive ,保留 30 份存档,毕竟文件不大。一般密码变化也少,基本够用了。
|
30
Janyd 158 天前 via iPhone
请问能写篇完整的教程吗?
|
31
0o0O0o0O0o OP @Janyd #30 见 #14 和 #17 ,等我解决了再来分享
|
32
ysmood 157 天前
@kuanat 可以试试这个,比 gpg 更简单方便 https://github.com/ysmood/whisper
|
34
0o0O0o0O0o OP |
35
Janyd 152 天前 via iPhone
@0o0O0o0O0o 谢谢,去试试
|
36
frankilla 135 天前
手动更新,更新到 Bitwarden 里- -
|