Vaultwarden 安全加固指南
Vaultwarden 1.37.4 安全加固指南:argon2 管理员口令、HTTPS 强制与真实客户端 IP、关闭注册与邀请、加密备份、fail2ban 联动与上线前检查清单,全部基于 Ubuntu 22.04 实测。
Vaultwarden 安全加固指南
> 目标:读完本文后,你可以把一台默认配置的 Vaultwarden 加固到可公网暴露的水平:管理员口令哈希化、全链路 HTTPS、注册与邀请收敛、加密备份、暴力破解防护(fail2ban 联动),并用文末清单逐项核验。
一、环境信息
本文加固动作均在下表环境实测执行,涉及的服务为直连部署形态(反向代理链路同样适用)。
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 22.04.4 LTS(内核 6.8.0-138-generic) |
| Vaultwarden 版本 | 1.37.4(Web Vault 2026.7.0) |
| 数据库 | SQLite 3.53.2(诊断页实测) |
| 数据目录 | /opt/data/vaultwarden(db.sqlite3 实测 290816 字节) |
| 服务端口 | 监听 80,对外 8222(直连验证);HTTPS 链路见第四章 |
| fail2ban | 0.11.2(Ubuntu 22.04 官方仓库) |
| openssl | OpenSSL 3.0.2(Ubuntu 22.04 自带) |
| 管理台 | /admin,会话 Cookie VW_ADMIN(有效期实测 1200 秒) |
加固前的基线事实:默认 SIGNUPS_ALLOWED=true(任何人可注册)、INVITATIONS_ALLOWED=true、ADMIN_TOKEN 未设置时管理台处于关闭状态(设置后才打开)、直连 HTTP 明文。以下章节逐项收敛这些暴露面。
二、攻击面与加固基线
Vaultwarden 公网部署需要面对四类攻击面,对应的加固基线如下表:
| 攻击面 | 风险 | 加固动作 | 对应章节 |
|---|---|---|---|
| 管理台 /admin | 管理员口令暴力破解、明文口令泄露 | argon2 哈希化 + 限流 + 不对公网开放 | 第三章 |
| 传输链路 | 令牌与页面明文窃听 | 全链路 HTTPS + 真实 IP 传递 | 第四章 |
| 注册接口 | 陌生人注册、垃圾账户 | 关闭注册/域白名单/邀请收敛 | 第五章 |
| 数据落地 | 服务器失陷后备份数据库 | 备份加密 + 异地保存 | 第六章 |
| 认证接口 | 主密码在线爆破 | fail2ban 联动日志封禁 | 第七章 |
一个原则贯穿全文:Vaultwarden 的用户数据本身是端到端加密的(《Vaultwarden 介绍与选型指南》第三章),加固重点是保护「账户身份入口」与「服务端可用性」,避免攻击者获得可在线爆破主密码的立足点。
三、管理员口令 argon2 化
3.1 生成 PHC 字符串
ADMIN_TOKEN 支持明文口令或 argon2id 哈希两种形态。生产必须用哈希形态:文件里只有单向哈希,口令只在人脑中。Ubuntu 22.04 安装官方 argon2 工具并生成(完整参数见部署指南 2.2 节):
# apt-get install -y argon2
# echo -n "q1w2e3r4" | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4
$argon2id$v=19$m=65540,t=3,p=4$NXJIcE1kMURMRGZ3OTBYZDgrdlRSWFIwWXVsNElFdlU3V0ZtakZuWUF4ST0$jXhsLhviXX2rEyQmARrfcXrT3H75bs+0D0EPbqgVBnM
参数组 -k 65540 -t 3 -p 4(64 MiB 内存、3 轮、4 并行)是官方 wiki 推荐值,单次校验耗时约几十毫秒,足以对抗离线爆破又不影响登录体验。把输出整串(含 $argon2id$ 前缀)写入 ADMIN_TOKEN,登录时用明文口令。
官方还提供 /vaultwarden hash 交互式工具;实测它在无终端环境下会直接 panic(完整回显见部署指南 2.2 节),无法交互时一律用 argon2 命令行。
3.2 登录验证与限流实测
正确口令登录成功(HTTP 200 + 会话 Cookie):
# curl -s -i -c /tmp/vw_cookies.txt -X POST http://127.0.0.1:8222/admin \
-H "Content-Type: application/x-www-form-urlencoded" --data-urlencode 'token=q1w2e3r4' | head -1
HTTP/1.1 200 OK
错误口令返回 401,页面错误文案实测为 Invalid admin token, please try again.。管理台登录自带限流:实测连续错误尝试,前 2 次返回 401,第 3 次起返回 429:
attempt 1 -> 401
attempt 2 -> 401
attempt 3 -> 429
# curl -s -i -X POST http://127.0.0.1:8222/admin \
-H "Content-Type: application/x-www-form-urlencoded" --data-urlencode 'token=wrong-token' | head -1
HTTP/1.1 429 Too Many Requests
Too many requests, try again later.
限流默认参数为窗口 300 秒、突发额度 3 次(ADMIN_RATELIMIT_SECONDS/ADMIN_RATELIMIT_MAX_BURST),限流状态保存在内存中,等待窗口结束或重启服务即恢复。
3.3 管理台暴露面收敛
即使口令已哈希化,/admin 仍应尽量不暴露公网:
- 反向代理层对
/admin路径加 IP 白名单或 VPN 网段限制; - 最保守做法:设
DISABLE_ADMIN_TOKEN=true直接禁用管理台,配置全部走部署形态的环境变量(日常改配置需要重启服务,换来的是少一个公网入口); - 管理台会话有效期默认 1200 秒,会话 Cookie 仅作用于 /admin 路径且 HttpOnly(实测),不额外放大风险。
四、HTTPS 强制与真实客户端 IP
4.1 TLS 终结
Vaultwarden 的数据在客户端加密,但认证令牌与会话是明文 HTTP 载荷,必须全链路 HTTPS。生产统一在反向代理终结 TLS,nginx 参考配置(实测验证通过,完整部署与验证回显见部署指南第五章):
server {
listen 443 ssl;
server_name vault.example.com;
ssl_certificate /etc/nginx/ssl/vw.crt;
ssl_certificate_key /etc/nginx/ssl/vw.key;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 525M;
location / {
proxy_pass http://127.0.0.1:8222;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
实测验证链路(自签证书场景用 -k 跳过校验):
# curl -sk https://127.0.0.1:8443/alive
"2026-10-17T08:23:54.292263Z"
# curl -sk -o /dev/null -w "https / -> %{http_code}\n" https://127.0.0.1:8443/
https / -> 200
# curl -sk -o /dev/null -w "https /admin -> %{http_code}\n" https://127.0.0.1:8443/admin
https /admin -> 200
同时把服务端 DOMAIN 设为反代对外的 https 地址(影响令牌签发与邮件链接,实测对照见《Vaultwarden 1.37.4 配置参数详解》第三章)。证书用 Let's Encrypt(certbot)签发,自签证书仅限内网。
4.2 真实客户端 IP
服务端默认从 X-Real-IP 头取真实客户端 IP(参数 IP_HEADER 默认值即 X-Real-IP,IP_HEADER_TRUSTED_PROXIES 默认 local)。反代未传递该头时,日志与限流记录到的都是代理地址——第五章的实测日志就是证据(直连时记录的来源为网关地址)。两个纪律:反代必须设置 X-Real-IP;IP_HEADER_TRUSTED_PROXIES 不要放开到任意来源,防止伪造头绕过限流与封禁。
验证方法:接入反代后从外部访问一次,查看服务日志中响应行的 IP 是否为客户端真实地址(日志行格式实测示例见第七章 7.2 节)。
4.3 端口收敛
公网防火墙只放行 443(或自定义 HTTPS 端口);Vaultwarden 原始端口(8222)只对内网/本机开放,管理台若未加白名单则同样只对内。可用 ufw 或云平台安全组实现,规则变更后用外部探测确认原始端口不可达。
五、关闭注册与邀请收敛
5.1 关闭开放注册
SIGNUPS_ALLOWED=false 是公网部署的第一加固项。实测两步注册流程第一步即被拒绝(完整回显见《Vaultwarden 1.37.4 配置参数详解》4.1 节):
{"message":"Registration not allowed or user already exists","validationErrors":{"":["Registration not allowed or user already exists"]},...}
HTTP 400
[2026-10-17 08:14:27.652][vaultwarden::api::identity][ERROR] Registration not allowed or user already exists
[2026-10-17 08:14:27.652][response][INFO] (register_verification_email) POST /identity/accounts/register/send-verification-email => 400 Bad Request
提示文案刻意合并「不允许注册」与「用户已存在」两种情形,无法据此探测账户存在性。首个管理员账户在关闭注册之前创建(顺序见部署指南 3.4 节)。
5.2 注册策略组合
按暴露程度选择组合:
| 场景 | 配置组合 |
|---|---|
| 完全封闭(仅既有账户) | SIGNUPS_ALLOWED=false |
| 限定域名的有限开放 | SIGNUPS_ALLOWED=true + SIGNUPS_DOMAINS_WHITELIST=corp.example.com |
| 需要邮箱真实性保证 | 叠加 SIGNUPS_VERIFY_EMAIL=true(配好 SMTP) |
| 禁止任何人自行建组织 | ORG_CREATION_USERS=<管理员邮箱> |
5.3 邀请与组织收敛
INVITATIONS_ALLOWED=false:仅当成员完全由管理员在管理台手动创建时使用;否则保持 true 供邀请加入;ORG_CREATION_USERS:限制谁能创建组织,避免普通用户随意建组织产生管理噪音;- 邀请有效期默认 120 小时(
INVITATION_EXPIRATION_HOURS),过期自动失效; - 实测部署中
SIGNUPS_ALLOWED=false、INVITATIONS_ALLOWED=false双关(见部署指南 compose.yml),新成员全部走管理台路径。
六、备份加密
6.1 为什么备份必须加密
数据库 db.sqlite3 存的是密文(用户数据零知识),但备份中还有 rsa_key.pem(JWT 签名密钥)与配置文件(可能含 SMTP 凭据),且未来 KDF 强度不足的旧账户数据可能被离线攻击。备份文件外发(网盘、对象存储、异地机房)必须先加密。
6.2 openssl 加密备份实测
用 openssl 对数据目录打包加密(AES-256-CBC + PBKDF2 10 万轮派生密钥):
# cd /opt/data/vaultwarden && tar -czf /tmp/vw_data.tar.gz db.sqlite3 attachments
# openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -salt \
-in /tmp/vw_data.tar.gz -out /tmp/vw_data.tar.gz.enc -pass pass:Backup@2026
# ls -la /tmp/vw_data.tar.gz /tmp/vw_data.tar.gz.enc
-rw-r--r-- 1 root root 21594 10月 17 16:19 /tmp/vw_data.tar.gz
-rw-r--r-- 1 root root 21616 10月 17 16:19 /tmp/vw_data.tar.gz.enc
还原演练(验证加密包可恢复且内容一致):
# openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 \
-in /tmp/vw_data.tar.gz.enc -out /tmp/vw_data_restore.tar.gz -pass pass:Backup@2026
# tar -tzf /tmp/vw_data_restore.tar.gz
db.sqlite3
attachments/
attachments/f8afa06c-7aab-444f-92a2-573cc9fcf22b/
attachments/f8afa06c-7aab-444f-92a2-573cc9fcf22b/d01e9799f624569e784d
# sha256sum /tmp/vw_data.tar.gz /tmp/vw_data_restore.tar.gz
9a2a8f6ee3034adf79871adcc37f6a494d2e02bbba88f1e5e3cb0eb973ffe9cd /tmp/vw_data.tar.gz
9a2a8f6ee3034adf79871adcc37f6a494d2e02bbba88f1e5e3cb0eb973ffe9cd /tmp/vw_data_restore.tar.gz
两个哈希一致说明加密/解密往返无损。纪律:加密口令与数据分开保管(口令走线下/密码管理器,不与备份放同一位置);定期做还原演练,只备份不演练等于没有备份。运行中数据库的一致性备份方法见《Vaultwarden 运维手册》第三章。
七、fail2ban 联动
7.1 原理
主密码在线爆破发生在登录接口,失败时服务日志会输出明确的错误行(实测):
[2026-10-17 08:17:38.410][vaultwarden::api::identity][ERROR] Username or password is incorrect. Try again. IP: 172.17.0.1. Username: admin@vaultwarden.local.
对应的接口响应为 HTTP 400,{"message":"Username or password is incorrect. Try again",...}。fail2ban 轮询该日志,按来源 IP 统计失败次数并调用防火墙封禁。前提(第四章):反代传递真实 IP,日志中的 IP: 字段必须是客户端地址。
7.2 过滤器与验证
Ubuntu 22.04 安装 fail2ban:
# apt-get install -y fail2ban
# fail2ban-client --version
Fail2Ban v0.11.2
创建过滤器 /etc/fail2ban/filter.d/vaultwarden.conf:
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <HOST>\. Username:.*$
^.*Invalid admin token, please try again\. IP: <HOST>.*$
^.*Sign-ups not allowed.*IP: <HOST>.*$
journalmatch = _SYSTEMD_UNIT=vaultwarden.service
服务日志通过 systemd 单元输出(StandardOutput=append:/var/log/vaultwarden.log 或 journald + journalmatch)。用实测日志行验证过滤器匹配(4 条真实失败日志样本):
# fail2ban-regex /tmp/vw_sample.log /etc/fail2ban/filter.d/vaultwarden.conf
...
|- #) [# of hits] regular expression
| 1) [4] ^.*Username or password is incorrect\. Try again\. IP: <HOST>\. Username:.*$
`-
Ignoreregex: 0 total
Date template hits:
|- [# of hits] date format
| [4] {^LN-BEG}ExYear(?P<_sep>[-/.])Month(?P=_sep)Day(?:T| ?)24hour:Minute:Second(?:[.,]Microseconds)?(?:\s*Zone offset)?
`-
Lines: 4 lines, 0 ignored, 4 matched, 0 missed
[processed in 0.00 sec]
4 lines, 4 matched 表明时间戳解析与失败行提取全部命中,过滤器可直接投产。
7.3 监狱配置与运维命令
创建 /etc/fail2ban/jail.d/vaultwarden.local:
[vaultwarden]
enabled = true
port = 443,8222
filter = vaultwarden
backend = auto
logpath = /var/log/vaultwarden.log
maxretry = 3
findtime = 300
bantime = 3600
阈值参考服务端自带限流(登录限流默认 60 秒窗口/突发 10 次,管理台 300 秒/3 次):fail2ban 管跨请求的持续性爆破,maxretry=3、findtime=300 是与内置限流互补的常见起点。启用与巡检:
# systemctl enable --now fail2ban
# fail2ban-client status vaultwarden
# fail2ban-client set vaultwarden unbanip <IP>
上线前用错误口令连续触发几次,确认 status vaultwarden 中失败计数与封禁列表按预期增长(File filter 段的实测匹配已在上面验证)。
八、上线前加固检查清单
逐项核对(每项都附实测验证途径):
| # | 检查项 | 验证方法 | 本文实测锚点 |
|---|---|---|---|
| 1 | 管理口令为 argon2id PHC | 配置中 ADMIN_TOKEN 以 $argon2id$ 开头;明文口令能登录 | 3.1/3.2 节实测 |
| 2 | 管理台限流生效 | 连续错 3 次出现 HTTP 429 | 3.2 节实测 |
| 3 | 全链路 HTTPS | curl https://<域名>/alive 返回 200;诊断页 HTTPS 检查 | 4.1 节实测 |
| 4 | 真实客户端 IP 入日志 | 外部访问后日志 IP: 为客户端地址 | 4.2 节(直连对照 172.17.0.1) |
| 5 | 开放注册已关闭 | 匿名注册返回 400 + Registration not allowed | 5.1 节实测 |
| 6 | KDF 轮数达标 | prelogin 返回 ≥100000(实测注册用户 600000) | 《Vaultwarden 1.37.4 配置参数详解》4.4 节 |
| 7 | 附件配额已设置 | USER_ATTACHMENT_LIMIT/ORG_ATTACHMENT_LIMIT 非空 | 第六章/配置参数详解 |
| 8 | 备份加密可还原 | 还原演练 sha256 一致 | 6.2 节实测 |
| 9 | fail2ban 过滤器匹配 | fail2ban-regex 4/4 matched | 7.2 节实测 |
| 10 | 原始端口不对公网 | 外部探测 8222 不可达 | 4.3 节 |
再叠加两个例行项:每次升级后重跑本清单;订阅安全公告(GitHub Releases),安全版本发布后尽快跟进(《Vaultwarden 运维手册》第四章升级流程)。
九、参考链接
- Vaultwarden 官方 Wiki(安全加固总览):https://github.com/dani-garcia/vaultwarden/wiki/Securing-your-Vaultwarden
- 官方 Wiki(fail2ban 部署):https://github.com/dani-garcia/vaultwarden/wiki/Fail2Ban-Setup
- 官方 Wiki(HTTPS 反代配置):https://github.com/dani-garcia/vaultwarden/wiki/Enabling-HTTPS
- argon2 PHC 格式规范:https://github.com/P-H-C/phc-string-format/blob/master/phc-sf.md
- fail2ban 官方文档:https://fail2ban.readthedocs.io/
- Let's Encrypt / certbot:https://certbot.eff.org/
- 本专栏相关:《Vaultwarden 介绍与选型指南》、部署指南、《Vaultwarden 1.37.4 配置参数详解》《Vaultwarden 运维手册》
- 相关文档:《Keycloak Docker Compose 部署指南》《OpenBao Docker Compose 部署指南》