V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
0o0O0o0O0o
V2EX  ›  程序员

vaultwarden 备份思路

  •  
  •   0o0O0o0O0o · 351 天前 · 5037 次点击
    这是一个创建于 351 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天统计各服务商的 S3 意外发现 vaultwarden 的备份数据已经很大了,所以思考了一下。

    我的旧方案是每日备份若干次,并且 inotifywait 了 data 目录一旦有写操作就备份,备份方式是无脑打 .tar.gz ,尽管大小不超过 5MB ,但由于备份频繁,所以占用和增长速度都变得有点过分。

    新思路:

    1. 先 git init 个 repository
    2. 利用 vaultwarden 建议的 SQLite Online Backup API 先备份 db.sqlite3
      sqlite3 db.sqlite3 ".backup /tmp/vaultwarden.sqlite3"
    3. 再利用 sqlite3 dump 成 sql 文件
      sqlite3 /tmp/vaultwarden.sqlite3 .dump > vaultwarden.sql
    4. 再按需添加数据库之外的文件,我是很纯粹地当密码管理器使用,所以不需要 attachments 、sends ,否则又引入了二进制文件,而且 git 无法记录文件权限
    5. 判断一下有没有变化,有变化就 commit

    最后用支持增量备份的工具来备份这个 git repository 就好了,不但可以备份到 S3 ,还可以 push 到任意 git 服务。

    26 条回复    2024-01-13 21:11:36 +08:00
    nagisaushio
        1
    nagisaushio  
       351 天前 via Android
    每次备份,新的 targz 直接覆盖旧的不就行了吗,为什么所有历史版本都要存
    Akkuman
        2
    Akkuman  
       351 天前 via Android
    litestream 可以将 sqlite3 增量同步到 s3
    0o0O0o0O0o
        3
    0o0O0o0O0o  
    OP
       351 天前
    @nagisaushio #1 不存历史版本的话这备份意义何在啊
    cccer
        4
    cccer  
       351 天前
    直接 sqlite3 原文件提交就行,git 自己会压缩相同部分。我每次备份提交 500k ,500 多提交整个仓库才 4M 大小。
    0o0O0o0O0o
        5
    0o0O0o0O0o  
    OP
       351 天前
    @Akkuman #2 这个从备份数据库这个角度确实专业太多了
    0o0O0o0O0o
        6
    0o0O0o0O0o  
    OP
       351 天前
    @cccer #4 转成文本也算有点附加收益,参见:

    https://news.ycombinator.com/item?id=38110286
    spritevan
        7
    spritevan  
       351 天前
    Usage: bw export [options]

    Export vault data to a CSV or JSON file.

    Options:
    --output <output> Output directory or filename.
    --format <format> Export file format.
    --password [password] Use password to encrypt instead of your Bitwarden account encryption key. Only applies to the encrypted_json format.
    --organizationid <organizationid> Organization id for an organization.
    -h, --help display help for command

    Notes:

    Valid formats are `csv`, `json`, and `encrypted_json`. Default format is `csv`.

    If --raw option is specified and no output filename or directory is given, the
    result is written to stdout.

    Examples:

    bw export
    bw --raw export
    bw export --output ./exp/bw.csv
    bw export myPassword321 --output bw.json --format json
    bkmi
        8
    bkmi  
       351 天前 via Android   ❤️ 1
    @0o0O0o0O0o 备份只是以防万一罢了,留一段时间的足以,留着所有历史记录意义何在
    0o0O0o0O0o
        9
    0o0O0o0O0o  
    OP
       351 天前
    @bkmi #8 确实,这次也顺便加上了生命周期规则,过久的历史版本转类型或删除
    sypopo
        10
    sypopo  
       351 天前 via Android
    我就每天备份一次,保留 30 天
    0o0O0o0O0o
        11
    0o0O0o0O0o  
    OP
       351 天前
    @spritevan #7

    1. 主密码永远不应该离开客户端,所以我不可能在服务器上定期备份时解密,我不清楚 bw cli 能不能在没有主密码的前提下备份已加密的数据库;
    2. vaultwarden 毕竟是第三方实现,bw cli 应该是很难完整导出 vaultwarden 的数据的
    zggsong
        12
    zggsong  
       351 天前
    chinni
        13
    chinni  
       351 天前
    restic
    borgbackup
    可以了解下
    比你这方便很多
    0o0O0o0O0o
        14
    0o0O0o0O0o  
    OP
       351 天前
    @chinni #13 其实你说的这俩是我的常规备份手段,但

    1. 我压根不需要加密,而且在这个场景里我最需要的后端是 S3 ,所以不选用 borg
    2. 虽然我长期使用 restic 很满意它的增量备份,但我觉得它并不够稳定,在面对多后端的情况下不如 rclone (尽管 restic 支持 rclone 当后端),偶尔会在一些非 AWS 的 S3 上面报错,历史 issues 有时也不了了之或者以提 issue 的人删掉之后重新 init 当作解决方案

    综上,我还是直接选择 rclone 了
    yumusb
        15
    yumusb  
       351 天前
    version: '3'
    services:
    vaultwarden:
    image: vaultwarden/server:latest
    volumes:
    - ${PWD}/data:/data
    restart: always
    tunnel:
    image: cloudflare/cloudflared
    restart: always
    command: tunnel run
    depends_on:
    - vaultwarden
    environment:
    - TUNNEL_TOKEN=xxxxxxx
    backup:
    image: alpine:latest
    volumes:
    - ./:/app
    - ./.gitconfig:/root/.gitconfig
    - ./.git-credentials:/root/.git-credentials
    command: /bin/sh -c "apk add --no-cache git && echo '*/5 * * * * cd /app && git add . && git commit -m AutoBackup && git push && echo OK ' | crontab - && crond -f"
    goodryb
        16
    goodryb  
       351 天前
    并且 inotifywait 了 data 目录一旦有写操作就备份 , 这个也太频繁了吧,我现在一天备份一次,保留 30 天的足够了
    6IbA2bj5ip3tK49j
        17
    6IbA2bj5ip3tK49j  
       351 天前
    你这种备份策略有点太傻了。
    6IbA2bj5ip3tK49j
        18
    6IbA2bj5ip3tK49j  
       351 天前
    合理的是像 promox 提供的策略。
    不管你备份有多频繁。
    在保留最近的 X 份的基础上。
    每日备份保留 D 份,每周备份保留 W 份,每月备份保留 M 份,每年备份保留 Y 份。
    最终也只要保留 X+D+W+M+Y 。
    越早的备份可以越稀疏。
    0o0O0o0O0o
        19
    0o0O0o0O0o  
    OP
       351 天前 via iPhone
    @goodryb #16 是想着让丢失数据的时间窗口短一些,赶不上楼上提到的 litestream ,但也够用了。如果只靠一天一次的 cron job ,最坏的情况要丢失一天的数据
    0o0O0o0O0o
        20
    0o0O0o0O0o  
    OP
       351 天前 via iPhone
    @xgfan 我平时用 borg 等备份也是类似的 prune 策略,vaultwarden 这个有点特殊,你听我狡辩:

    我默认服务器是不安全的,为了避免服务器被黑后备份的数据被删,我是只给了它对 S3 的写入权限用于不停写入 .tar.gz ,然后也存了一种看看到底会如何增长的心态留了一年没配置生命周期规则;

    至于新方案,我则多给了些权限用于同步 git repo ,但依然不能给可以删除历史版本的权限,所以我目前的策略可能也就是通过 S3 服务商提供的生命周期规则把时间够久的历史版本换个存储类型节省成本。但考虑到这个方案的文件大小增长速度肯定很慢,所以我可能还是会全部保留观察一年。
    bao3
        21
    bao3  
       350 天前
    真的要安全,每个密钥,你都应该在 bitwarden 和另一个 app 同时存储,且在不同位置。只有这样你服务器上的备份才有了意义。你既然都默认是服务器不安全了,当然要异地灾备
    0o0O0o0O0o
        22
    0o0O0o0O0o  
    OP
       350 天前 via iPhone
    @bao3 #21 vaultwarden 的设计允许服务器是不安全的,数据不会被攻击者解密。所以你说的这个保护的是“服务器被黑后写入的数据”吗?
    bao3
        23
    bao3  
       350 天前
    @0o0O0o0O0o 这取决于你对安全的理解,比如损失文件是否是安全的一部分,如果不是,那就无需考虑。
    0o0O0o0O0o
        24
    0o0O0o0O0o  
    OP
       350 天前
    @bao3 #23

    谢谢,认同你说的,我确实完全没考虑到如何防止这部分数据的丢失。尽管我在 PC 上是把 Bitwarden 目录加进了日常备份的列表,但只是习惯性地写了进去,没有这方面的考虑。

    目前想到的是在客户端以密文状态定期对比已备份到 S3 的数据和本地的数据
    0o0O0o0O0o
        25
    0o0O0o0O0o  
    OP
       350 天前
    24 小时过去,发现一个问题是每次打开 bitwarden 都会在数据库里修改 devices 表的 updated_at ,这种改变没有备份的意义,脚本里应该处理一下类似的问题
    zhuweitung
        26
    zhuweitung  
       350 天前
    ttionya/vaultwarden-backup
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1711 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 16:42 · PVG 00:42 · LAX 08:42 · JFK 11:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.