Miao's Blog

自托管 Vaultwarden 并配置 WebDAV 自动备份

我此前一直使用的是 Bitwarden 官方在欧洲的服务器,今年开始感觉在中国大陆连接性不太好,经常性的无法连接。由于各种原因,我的安卓设备不方便安装代理软件,但我有多台拥有中国路由优化的海外服务器,因此我选择在我自己的服务器上自托管 Vaultwarden,一个 Rust 写的 Bitwarden 轻量实现。为了确保密码库的安全和防止我手贱删库,我需要一套完整的方案:容器化部署 + WebDAV 异地备份 + 失败通知推送,突出一个安心!

最终我选择在洛杉矶 VPS 上通过 Caddy 反向代理 Docker 内安装的 Vaultwarden,并实现上述功能。

1. 安装 Docker

在开始之前,确保服务器已安装 Docker。用官方的一键安装脚本:

curl -fsSL https://get.docker.com | sh

2. 创建工作目录

建议将所有配置文件放在统一目录下,方便管理:

mkdir -p ~/vaultwarden/vw_data
mkdir -p ~/vaultwarden/rclone
cd ~/vaultwarden

目录结构如下:

~/vaultwarden/
├── docker-compose.yaml
├── .env
├── rclone/
│   └── rclone.conf
└── vw_data/            # Vaultwarden 数据目录

3. 编写 Docker Compose 配置

编写 docker-compose.yaml,包含 Vaultwarden 主程序和备份插件。注意,配置文件中使用的 ${VARIABLE} 语法会自动从后续步奏创建的 .env 文件中读取变量。

services:
  vaultwarden:
    image: ghcr.io/dani-garcia/vaultwarden:latest
    container_name: vaultwarden
    restart: unless-stopped
    volumes:
      - ./vw_data/:/data/
    environment:
      - DOMAIN=${DOMAIN} # 域名
      - ADMIN_TOKEN=${ADMIN_TOKEN} # 管理员Token
      - SIGNUPS_ALLOWED=false # 禁止注册
      - LOG_LEVEL=error # 日志级别
      - SENDS_ALLOWED=false # 禁止使用 Send
      - TRASH_AUTO_DELETE_DAYS=7 # 自动删除回收站
    ports:
      - 127.0.0.1:8888:80 # 仅限本地访问,Caddy 反向代理

  backup:
    image: ttionya/vaultwarden-backup:latest
    container_name: vaultwarden-backup
    restart: unless-stopped
    environment:
      - RCLONE_REMOTE_NAME=WebDAV
      - RCLONE_REMOTE_DIR=/vaultwarden-backup/
      - CRON=0 3 * * * # 每天凌晨 3 点执行备份
      - ZIP_ENABLE=TRUE # 启用压缩
      - ZIP_PASSWORD=${BACKUP_PASSWORD} # 压缩密码
      - ZIP_TYPE=7z # 压缩格式
      - BACKUP_FILE_SUFFIX=%Y%m%d-%H%M%S # 备份文件名后缀
      - BACKUP_KEEP_DAYS=30 # 保留备份天数
      - TIMEZONE=Asia/Hong_Kong # 时区
      - PING_URL_WHEN_FAILURE=https://ntfy.sh/${NTFY_KEY}|https://api.day.app/${BARK_KEY}/Vaultwarden备份失败/请检查日志?sound=alarm&level=timeSensitive&group=backup
      - 'PING_URL_WHEN_FAILURE_CURL_OPTIONS=-H "Title: ⚠️ Vaultwarden 备份失败" -H "Priority: urgent" -H "Tags: warning,backup,vaultwarden" -d "备份任务执行失败,请立即检查服务器日志"'
    volumes:
      - ./vw_data/:/bitwarden/data/:ro
      - ./rclone:/config/rclone
    depends_on:
      - vaultwarden

4. 配置环境变量 (.env)

为了安全和方便管理,我们将敏感信息抽离到 .env 文件中。

首先,生成 ADMIN_TOKEN 的 Argon2 哈希加密:

docker run --rm -it vaultwarden/server /vaultwarden hash

然后,创建环境配置文件 .env,将生成的哈希值填入:

cat <<EOF > .env
DOMAIN=https://vault.example.com
ADMIN_TOKEN=你的管理员Token哈希值
BACKUP_PASSWORD=你的备份压缩包密码
NTFY_KEY=你的ntfy密钥
BARK_KEY=你的Bark密钥
EOF

5. 配置 rclone (WebDAV 存储)

备份容器使用 rclone 将加密压缩包上传到 WebDAV 远程存储。WebDAV 服务我用的是 Koofr,多年前我就购买了 Koofr 的永久 100G 存储空间,长期用下来性价比很高。

# 交互式配置 rclone,在 ~/vaultwarden 路径下执行
docker run --rm -it \
  -v $(pwd)/rclone:/config/rclone \
  ttionya/vaultwarden-backup:latest \
  rclone config

配置要点:

  1. Name: 输入 WebDAV
  2. Storage: 选择 webdav
  3. URL: 输入 https://app.koofr.net/dav/Koofr
  4. Vendor: 选择 other
  5. User/Pass: 输入 WebDAV 账号和应用专用密码
  6. 注意: 密码必须通过此交互界面生成,rclone 会自动对其进行混淆(Obscure),直接在配置文件写明文会导致报错。

配置完成后,~/vaultwarden/rclone/rclone.conf 内容大致如下:

[WebDAV]
type = webdav
url = https://app.koofr.net/dav/Koofr
vendor = other
user = your_email@example.com
pass = xxx_your_obscured_password_xxx

配置完成后,即可启动服务:

docker compose up -d

6. Caddy 反向代理与 GeoIP 限制

为了提高安全性,使用带有 maxmind_geolocation 模块的 Caddy,限制仅允许中国大陆 IP 访问。

编辑 /etc/caddy/conf.d/vault.caddy

# --- Vaultwarden ---
vault.example.com {
    # 日志配置
    log {
        level INFO
        output file /var/log/caddy/vaultwarden.log {
            roll_size 10MB
            roll_keep 10
        }
    }

    # GeoIP 匹配器:仅允许中国大陆
    @china {
        maxmind_geolocation {
            db_path "/etc/caddy/GeoLite2-Country.mmdb"
            allow_countries CN
        }
    }

    # 匹配成功:反代到 Vaultwarden
    handle @china {
        encode zstd gzip
        reverse_proxy localhost:8888 {
            header_up X-Real-IP {remote_host}
        }
    }

    # 匹配失败:返回 403
    handle {
        respond "Access Denied: China only" 403
    }
}

7. 坑点总结

1. rclone 配置文件路径

备份容器 ttionya/vaultwarden-backup 默认寻找配置的路径是 /config/rclone/rclone.conf。 在挂载时,建议挂载整个目录:- ./rclone:/config/rclone,这样 rclone 在更新配置时不会因为文件被占用(Device or resource busy)而失败。

2. RCLONE_REMOTE_NAME 匹配

docker-compose.yaml 中的 RCLONE_REMOTE_NAME 必须与 rclone.conf 中方括号内的名称完全一致。 例如配置里是 [WebDAV],环境变量就必须是 WebDAV

8. 最后

从开始到放弃很快的,最后的最后,我删掉了所有内容,毕竟跟我自己的技术相比我还是更相信 Bitwarden 官方,,,,而且一年只需要 10美金十分良心了,慢点就慢点吧,又不是不能用🥲。