Files
server-deploy/vaultwarden/README.md
T

488 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Vaultwarden 部署指南
Bitwarden 兼容的自托管密码管理器,轻量、安全、功能完整。
## 功能特性
- 完全兼容 Bitwarden 官方客户端(浏览器插件、桌面端、移动端)
- 密码、笔记、信用卡、身份信息安全存储
- TOTP 两步验证码生成
- 密码分享(Send 功能)
- 组织与多用户协作
- 管理员面板
- 轻量级:使用 SQLite,单容器约 50MB 内存
## 技术栈
| 组件 | 版本 | 说明 |
|------|------|------|
| Vaultwarden | 固定版本号 | Bitwarden 兼容服务端(Rust 实现),由 `upgrade.sh` 升级 |
| SQLite | 内置 | 轻量数据库,无需额外部署 |
| Nginx | 系统包 | 反向代理 + HTTPSBitwarden 客户端必须 HTTPS |
| Docker | 最新版 | 容器运行环境 |
## 前置条件
1. 一台 Linux 服务器(Ubuntu 22.04/24.04 推荐)
2. 一个已解析到服务器的域名(如 `vault.example.com`
3. 服务器 80/443 端口可从外网访问
4. **必须启用 HTTPS**Bitwarden 客户端强制要求)
## 目录结构
```
vaultwarden/
├── docker-compose.yml # 容器编排
├── .env.example # 配置模板
├── deploy.sh # 一键部署脚本
├── upgrade.sh # 安全升级脚本(带备份、校验、自动回滚)
├── backup.sh # 备份脚本
├── uninstall.sh # 完全卸载脚本
├── nginx/
│ └── vaultwarden.conf # Nginx 反向代理配置
└── README.md # 本文件
```
服务器上的数据目录:
```
/var/lib/vaultwarden/ # Vaultwarden 数据(SQLite + 附件 + RSA 密钥)
/var/backups/vaultwarden/ # 备份文件
```
## 快速部署
### 第一步:上传文件到服务器
```bash
# 在本地执行,上传 vaultwarden 目录
scp -r vaultwarden/ root@<服务器IP>:/opt/vaultwarden
# 如果服务器上还没有部署过 base(首台服务或全新服务器),还需上传 base
scp -r base/ root@<服务器IP>:/opt/base
```
### 第二步:登录服务器执行部署
```bash
ssh root@<服务器IP>
# 如果是全新服务器,先安装基础环境
cd /opt/base
cp .env.example .env
bash setup.sh
# 部署 Vaultwarden
cd /opt/vaultwarden
bash deploy.sh
# 首次运行会生成 .env(含自动生成的 ADMIN_TOKEN),按提示修改后重新运行
vi .env
bash deploy.sh
```
### 第三步:配置域名解析
在域名服务商添加 A 记录:
| 记录类型 | 主机记录 | 记录值 |
|----------|----------|--------|
| A | vault | `<服务器公网IP>` |
### 第四步:注册账号
1. 浏览器访问 `https://vault.yourdomain.com`
2. 点击「创建账户」注册一个主账号
3. 注册完成后,**强烈建议关闭注册功能**:
```bash
cd /opt/vaultwarden
# 编辑 .env,将 SIGNUPS_ALLOWED 改为 false
vi .env
docker compose up -d # 注意:不是 restart
```
> **不要用 `docker compose restart`**。环境变量是在容器**创建**时注入的,`restart` 只是重启
> 原容器、不会重建,改了 `.env` 也不会生效。必须用 `up -d` 让 compose 检测到配置变更并重建容器。
### 第五步:安装客户端
1. 下载 Bitwarden 客户端:https://bitwarden.com/download/
2. 打开客户端,点击左上角齿轮图标
3. 在「自托管服务器」中填入服务器 URL:`https://vault.yourdomain.com`
4. 保存后使用注册的账号登录
## 配置说明
### .env 配置项
| 变量 | 说明 | 默认值 |
|------|------|--------|
| `VAULTWARDEN_DOMAIN` | 访问域名 | 必填 |
| `CERTBOT_EMAIL` | Let's Encrypt 邮箱 | 必填 |
| `ADMIN_TOKEN` | 管理员面板令牌 | 自动生成 |
| `SIGNUPS_ALLOWED` | 允许新用户注册 | `true` |
| `SHOW_PASSWORD_HINT` | 显示密码提示 | `false` |
| `SENDS_ALLOWED` | 允许 Send 功能 | `true` |
| `VAULTWARDEN_IMAGE` | Docker 镜像 | `vaultwarden/server:latest` |
| `VAULTWARDEN_DATA_DIR` | 数据目录 | `/var/lib/vaultwarden` |
| `VAULTWARDEN_PORT` | 本地监听端口 | `8080` |
| `BACKUP_DIR` | 备份目录 | `/var/backups/vaultwarden` |
| `LOG_LEVEL` | 日志级别 | `info` |
### 管理员面板
访问 `https://vault.yourdomain.com/admin`,输入 `ADMIN_TOKEN` 中的明文令牌即可登录。
管理员面板可以:
- 查看所有用户
- 邀请新用户(关闭注册后)
- 删除用户
- 查看系统诊断信息
- 调整运行时配置
> **安全提醒**:可在 Nginx 配置中限制 `/admin` 路径仅允许特定 IP 访问。
### 使用 argon2id 加强 ADMIN_TOKEN 安全性
部署脚本默认生成随机字符串作为 ADMIN_TOKEN。如需使用更安全的 argon2id 哈希:
```bash
# 生成 argon2id 哈希(使用 Bitwarden 默认参数: m=64MiB, t=3, p=4
docker run --rm -it vaultwarden/server /vaultwarden hash
# 或使用 OWASP 推荐参数(m=19MiB, t=2, p=1,更节省内存)
docker run --rm -it vaultwarden/server /vaultwarden hash --preset owasp
```
将生成的哈希值填入 `.env` 时,**必须用单引号包裹**(因为 argon2id 字符串包含 `$` 符号):
```bash
# .env 中的写法(注意单引号)
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeK......'
```
> **注意**:登录管理面板时输入的是你生成哈希时使用的**原始明文密码**,不是哈希值本身。
## 日常运维
### 查看日志
```bash
cd /opt/vaultwarden
docker compose logs -f
docker compose logs --tail 100
```
### 备份
```bash
cd /opt/vaultwarden
bash backup.sh
```
备份内容包括:
- SQLite 数据库(支持在线安全备份)
- 附件和 RSA 密钥
- 部署配置
也可使用 Vaultwarden v1.32.1+ 内置备份命令:
```bash
docker exec -it vaultwarden /vaultwarden backup
```
备份文件保存在 `/var/backups/vaultwarden/`,自动清理 30 天前的旧备份。
**建议配置定时备份:**
```bash
# 每天凌晨 2 点自动备份
crontab -e
# 添加:
0 2 * * * cd /opt/vaultwarden && bash backup.sh >> /var/log/vaultwarden-backup.log 2>&1
```
### 恢复备份
```bash
cd /opt/vaultwarden
docker compose down
# 查看可用备份
ls /var/backups/vaultwarden/
# ⚠ 重要:先删除现有 WAL 文件,避免与恢复的数据库不匹配导致损坏
rm -f /var/lib/vaultwarden/db.sqlite3-wal /var/lib/vaultwarden/db.sqlite3-shm
# 恢复数据库
cp /var/backups/vaultwarden/<日期>/db.sqlite3 /var/lib/vaultwarden/
# 恢复附件
tar xzf /var/backups/vaultwarden/<日期>/vaultwarden-data.tar.gz -C /var/lib/
# 重启
docker compose up -d
```
> **警告**:恢复通过 `.backup` 方式创建的备份时,**必须先删除**现有的 `db.sqlite3-wal` 文件,否则 SQLite 会尝试用旧的 WAL 文件恢复数据库,可能导致数据损坏。
### 升级
使用 `upgrade.sh` 升级,**不要**手动 `docker compose pull && up -d`(见下方「为什么不要手动升级」)。
```bash
cd /opt/vaultwarden
bash upgrade.sh --check # 先看看会发生什么,不做任何改动
bash upgrade.sh # 正式升级到 GitHub 最新 release
```
脚本执行流程:
| 步骤 | 动作 | 失败时 |
|------|------|--------|
| 1 | 预检:root、依赖命令、数据目录、磁盘空间 | 直接退出,未动服务 |
| 2 | 拉取新镜像 | 直接退出,**服务零影响**,可安全重试 |
| 3 | 把当前镜像打上 `pre-upgrade-<时间戳>` 标签作为回滚锚点 | — |
| 4 | 停止容器 | 自动拉起原服务 |
| 5 | WAL checkpoint + `integrity_check` + 冷备份整个数据目录 | 自动拉起原服务 |
| 6 | 切换镜像并 `--force-recreate` 重建容器 | 自动回滚 |
| 7 | 校验容器实际镜像 == 目标镜像 | 自动回滚 |
| 8 | 校验 `/alive`、新旧 prelogin 路由、Nginx 反代 | 自动回滚 |
| 9 | 比对升级前后各表行数,任一表减少即判定数据丢失 | 自动回滚 |
常用选项:
```bash
bash upgrade.sh --check # 只检查,零改动
bash upgrade.sh --version 1.37.0 # 升级到指定版本
bash upgrade.sh --yes # 跳过交互确认(自动化场景)
bash upgrade.sh --rollback # 回滚到上次升级前的状态
```
升级完成后请**登录 Web 端确认数据无误**,再清理回滚镜像:
```bash
docker image rm vaultwarden/server:pre-upgrade-<时间戳>
```
### 回滚
```bash
cd /opt/vaultwarden
bash upgrade.sh --rollback
```
回滚会读取 `.upgrade-state` 里记录的备份和镜像锚点,恢复数据目录并切回旧镜像。
> **注意**:回滚会丢弃升级后新增/修改的密码条目。
> 回滚**不会删除**升级后的数据,而是把它改名保留为 `<数据目录>.failed-<时间戳>`,确认无需后再自行删除。
### 为什么不要手动升级
手动 `docker compose pull && docker compose up -d` 有两个坑,都会导致「看起来升级了,其实没有」:
1. **停止状态的容器不会因镜像变更而重建**。如果先 `stop``up -d`compose 只是把原容器重新 `start`,仍然跑旧镜像。必须 `--force-recreate`
2. **环境变量优先级高于 `.env` 文件**。如果当前 shell 里已经 `export``VAULTWARDEN_IMAGE`(比如脚本里 `source .env` 过),改 `.env` 文件无效,compose 依然用旧值。
`upgrade.sh` 处理了这两点,并在启动后强制校验「容器实际运行的镜像 ID == 目标镜像 ID」,不匹配直接回滚。
### 停止 / 启动
```bash
cd /opt/vaultwarden
docker compose down # 停止
docker compose up -d # 启动
docker compose restart # 重启
```
## 完全卸载
如果需要从服务器上完全移除 Vaultwarden,使用卸载脚本:
```bash
cd /opt/vaultwarden
bash uninstall.sh
```
脚本会**交互式确认**每个危险操作,按顺序执行:
| 步骤 | 操作 | 确认方式 |
|------|------|----------|
| 0 | 卸载前备份(可选) | y/N |
| 1 | 停止并删除 Vaultwarden 容器 | 输入 YES |
| 2 | 删除 Docker 镜像 | 自动 |
| 3 | 删除 Nginx 站点配置并重载 | 自动 |
| 4 | 删除 Let's Encrypt SSL 证书 | 自动 |
| 5 | 清理 Certbot 定时任务(仅当无其他证书时) | 自动 |
| 6 | 删除数据目录 | 输入 DELETE |
| 7 | 删除部署目录 `/opt/vaultwarden` | y/N |
**备份目录 `/var/backups/vaultwarden/` 始终保留**,不会被删除。
> 卸载后如需恢复,参考上方「恢复备份」章节。
<details>
<summary>手动卸载步骤(不使用脚本)</summary>
```bash
cd /opt/vaultwarden
# 1. 建议先备份
bash backup.sh
# 2. 停止并删除容器
docker compose down -v
# 3. 删除 Docker 镜像(可选)
docker image rm vaultwarden/server:latest
# 4. 删除 Nginx 配置
rm -f /etc/nginx/sites-enabled/vaultwarden /etc/nginx/sites-available/vaultwarden
nginx -t && systemctl reload nginx
# 5. 删除 SSL 证书
certbot delete --cert-name 你的域名
# 6. 删除数据目录(⚠ 不可恢复)
rm -rf /var/lib/vaultwarden
# 7. 删除部署目录(可选)
rm -rf /opt/vaultwarden
# 备份目录保留在 /var/backups/vaultwarden/
```
</details>
## 安全加固建议
### 1. 关闭注册
注册好所有需要的账号后:
```bash
cd /opt/vaultwarden
sed -i 's/^SIGNUPS_ALLOWED=.*/SIGNUPS_ALLOWED=false/' .env
docker compose up -d # 必须 up -drestart 不生效
```
验证是否真的关闭了(应返回 `Registration not allowed or user already exists`):
```bash
curl -s -X POST http://127.0.0.1:8080/identity/accounts/register/send-verification-email \
-H 'Content-Type: application/json' \
-d '{"email":"probe@example.invalid","name":"probe"}'
```
> **不要用 `/api/config` 里的 `disableUserRegistration` 判断**。该字段来自
> `is_signup_disabled()`,含义是「是否隐藏 UI 上的注册链接」,而非「注册是否被拒绝」。
> 当 `INVITATIONS_ALLOWED=true` 且未配置 SMTP 时,即使 `SIGNUPS_ALLOWED=false`
> 它也会显示为 `false`(因为管理员邀请的用户仍需走注册流程)。真正的拦截在注册接口里。
### 2. 限制管理面板访问
`nginx/vaultwarden.conf` 中取消注释 `/admin` 的 IP 限制部分,仅允许你的 IP 访问。
### 3. 启用两步验证
登录 Vaultwarden Web 界面 → 设置 → 两步登录 → 启用 TOTP 或其他验证方式。
### 4. 定期备份
配置定时任务,每天自动备份,并将备份文件同步到异地存储。
## 故障排查
### 客户端无法连接
```bash
# 检查 HTTPS 是否正常
curl -I https://vault.yourdomain.com
# 检查容器是否运行
docker compose ps
# 检查端口
curl http://127.0.0.1:8080/alive
```
### 客户端登录报错,控制台 404 /identity/accounts/prelogin/password
**症状**:已登录的会话正常,但重新登录失败,浏览器控制台显示:
```
POST https://vault.example.com/identity/accounts/prelogin/password 404 (Not Found)
```
**原因**:服务端版本过旧。Bitwarden 客户端从 v2026.4.0 起改用 `/identity/accounts/prelogin/password`
这个新路由,而 Vaultwarden 在 **1.36.0** 才实现它。客户端会自动更新,服务端不会。
**确认**
```bash
# 看服务端实到底跑的是哪个版本
curl -s https://vault.example.com/api/config | grep -o '"gitHash":"[^"]*"'
# 直接探测新旧两个路由
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' \
-d '{"email":"x@example.com"}' https://vault.example.com/identity/accounts/prelogin/password
```
旧路由返回 200、新路由返回 404,即可确诊。
**修复**:升级到 1.36.0 以上。
```bash
cd /opt/vaultwarden && bash upgrade.sh
```
如果暂时无法升级,可在 Nginx 的 443 server 块内加一条重写作为临时过渡:
```nginx
rewrite ^/identity/accounts/prelogin/password/?$ /identity/accounts/prelogin last;
```
### 502 Bad Gateway
```bash
# 容器未运行
docker compose up -d
# 检查端口是否匹配
grep VAULTWARDEN_PORT .env
grep proxy_pass /etc/nginx/sites-available/vaultwarden
```
### 管理面板无法登录
```bash
# 确认 ADMIN_TOKEN 已设置
grep ADMIN_TOKEN .env
# 如果使用 argon2id 哈希,登录时输入的是原始明文密码
# 检查容器日志中的错误
docker compose logs --tail 20
```
### SSL 证书问题
```bash
# 检查证书状态
certbot certificates
# 手动续期
certbot renew --dry-run
# 检查域名解析
dig vault.yourdomain.com
```
## 端口说明
| 端口 | 协议 | 说明 |
|------|------|------|
| 80 | TCP | HTTP → HTTPS 重定向 |
| 443 | TCP | HTTPSNginx 反向代理) |
| 8080 | TCP | Vaultwarden HTTP(仅监听 127.0.0.1 |