1.强化指南

应用程序配置

下面的小节涵盖了 Vaultwarden 本身相关的强化。

禁用注册和(可选)邀请

默认情况下,Vaultwarden 允许任何匿名用户在未被邀请的情况下在服务器上注册新账户。如果您可以访问管理页面,则这不是必需的,如果您是服务器上的第一个用户,则这很有用。建议您在管理面板中(如果启用了管理面板的话)或使用环境变量将其禁用,以防止攻击者在您的 Vaultwarden 服务器上创建账户。

Vaultwarden 还允许注册用户邀请其他新用户在服务器上创建账户并加入其组织。只要您信任您的用户,这不会带来直接风险,但是可以在管理面板或使用环境变量将其禁用。

禁用显示密码提示

Vaultwarden 在登录页面上显示密码提示,以适应没有配置 SMTP 的小型/本地部署,这可能被攻击者滥用,以方便对服务器上的用户进行密码猜测攻击。可以在管理面板中通过取消勾选 Show password hints 选项或使用环境变量来禁用它。

HTTPS / TLS 配置

下面的小节涵盖了 HTTPS/TLS 相关的强化。

严格 SNI

SNI 是网络浏览器请求 HTTPS 服务器为特定网站(如 vaultwarden.example.com)提供 SSL/TLS 证书的方式。假设vaultwarden.example.com 的 IP 地址是 1.2.3.4。理想情况下,您希望您的实例只能通过 https://vaultwarden.example.com 访问,并且不能通过 https://1.2.3.4 进行访问。这是因为 IP 地址会因为各种原因被不断扫描,如果能通过这种方式检测到您的 Vaultwarden 实例,就会成为一个更明显的目标。例如,一个简单的 Shodan 搜索就会发现一些通过 IP 地址访问的 Bitwarden 实例。

反向代理

一般来说,您应该避免通过 Vaultwarden 内置的 Rocket TLS 支持启用 HTTPS,特别是当您的实例是公开访问的时候。Rocket 本身列出了如下警告

Rocket's built-in TLS is not considered ready for production use. It is intended for development use only.(Rocket 内置的 TLS 还不能用于生产。它只用于开发用途。)

比如,Rocket TLS 不支持严格 SNI 或 ECC 证书(仅 RSA)。

请参看代理示例,以了解反向代理配置的示例。

访问日志包含 access_token 参数

通过调用具有 JWT 密钥的 GET 请求来建立用于通知的 WSS 连接。

GET 请求示例:

/notifications/hub?access_token=[this part is always the same].eyJuYmYi[redacted]sImV4cCI6MTcxNzc1NzQ1OCwiaXN[redacted]M6Ly92YXVsdC5zZWMuYXJwYXxsb2dpbiIsInN1YiI6ImY5YmVhN[redacted]tNGJjNS05MDY2LTQ3NjFlZmY4ND[redacted]sInByZW1pdW0iOnRydWU[redacted]JjaXBoZXIiLCJlbWFpbCI6ImNpc[redacted]ljdSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJzc3RhbXAiOiJlZjM3[redacted]MjctODE2OS1hZTQ3NmFjNDc4MGQiLCJkZX[redacted]02ZTk3LTQ2N2M[redacted]jM3NmEiLCJzY29wZSI6WyJhcG[redacted]5lX2FjY2VzcyJdLCJhbXIiOlsiQXBwbGljY[redacted]hGDeCNdjTs1cOL2fV_OR96Sey-gA5eRa8OCGNgCrDeyYAPyk[redacted]BkQGwjEhD7fcWILxRYqQ7W6rkC2o[redacted]LB_nztpAgeRUbsPgsd3RNTWJDKdlH8aMf1[redacted]vB_doENJPeyaeMuEG85KqpAN2A[redacted]GeeCztxmQIe21PMtBG-SAgGeI[redacted]X_9mmyv0nISHBuHjhQ_km[redacted]VCLoFneb-MEzN[redacted]T8VcXSKhGXpwJUx8j1[redacted]k_nH27vrD2Dg

如果您的反向代理配置为保存访问日志,或者访问日志被发送到外部服务(例如 Prometheus + Promtail),建议在外部日志存储上编辑 access_token 参数的值,或者选择直接在您的反向代理上编辑,如果支持的话。

任何其他数据都不会通过 GET 请求发送,无论是加密的还是未加密的。

请注意,内部 Vaultwarden 日志将查询截断为 30 个字符,因此 access_token 会被截断。这意味着默认情况下如果没有使用反向代理,您也应该是安全的。

Docker 配置

下面的小节涵盖了 Docker 相关的强化。

以非 root 用户运行

Vaultwarden Docker 镜像被配置为默认以 root 用户的身份运行容器进程。这允许 Vaultwarden 读取/写入 bind-mounted 到容器中的任何数据,而无需权限问题,即使这些数据是由另一个用户(例如,你在 Docker 主机上的用户账户)拥有的。

默认配置在安全性和可用性之间取得了很好的平衡--在一个非特权 Docker 容器中以 root 身份运行,本身就提供了合理的隔离级别,同时也让那些不是非常精通如何在 Linux 上管理所有权/权限的用户更容易进行设置。然而,作为通用策略,从安全的角度来说,以所需的最低权限运行进程是更好的;对于用 Rust 等内存安全语言编写的程序来说,这一点就不那么重要了,但请注意,Vaultwarden 也使用了一些用 C 语言编写的库代码(例如 SQLite、OpenSSL、MySQL、PostgreSQL 等)。

要在 Docker 中以非 root 用户 (uid/gid 1000) 的身份运行容器进程 (vaultwarden):

docker run -u 1000:1000 [...other args...] vaultwarden/server:latest

docker-compose 中类似操作:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: bitwarden
    user: 1000:1000
    ... other configuration ...c

在许多 Linux 发行版中,默认用户的 uid/gid 为 1000(运行 id 命令进行验证),所以如果您想在不换成其他用户的情况下轻松地访问您的 Vaultwarden 数据,这是一个很好的值,但你可以根据需要调整 uid/gid。请注意,您很可能需要指定一个数字 uid/gid,因为 Vaultwarden 容器不共享用户/组名到 uid/gid 的相同映射(例如,将容器中的 /etc/passwd/etc/group 文件与 Docker 主机上的文件对比)。

Vaultwarden Docker 镜像的设置使得 vaultwarden 可执行文件绑定到端口 80,这工作正常,因为它默认以 root 身份运行。但是,非 root 进程通常无法绑定到特权端口(即低于 1024 的端口)。从版本 20.10.0 开始(参见 moby/moby#41030),Docker 专门配置其容器,以便默认情况下允许非 root 进程绑定到特权端口。对于早期版本的 Docker 或其他没有这种特殊行为的容器运行时,Vaultwarden Docker 镜像还在 vaultwarden 可执行文件上设置 cap_net_bind_service 功能,这是另一种允许可执行文件在以非 root 用户身份运行时绑定到特权端口的方法。

挂载数据到容器中

一般来说,只有 Vaultwarden 正常运行所需要的数据才应该被挂载到 Vaultwarden 容器中(通常情况下,这只是您的数据目录,也许还有一个包含 SSL/TLS 证书和私钥的目录)。不要挂载您的整个主目录,例如,/var/run/docker.sock 等,除非您有特定的原因,并且知道您在做什么。

另外,如果您不希望 Vaultwarden 修改您挂载的数据(例如,certs),可以通过在卷规范中添加 :ro只读挂载它(例如,docker run -v /home/username/vaultwarden-ssl:/ssl:ro)。

杂项

暴力破解

当不使用双重身份验证时,(理论上)有可能对用户的密码进行暴力破解,从而获得对其账户的访问权限。缓解此问题的一种相对简单的方法是设置 fail2ban,设置后,在过多的失败登录尝试后将阻止访问者的 IP 地址。但是在许多反向代理(例如 cloudflare)后面使用此功能时,应格外注意。参阅:Fail2Ban 设置

隐藏在子目录下

通常,Bitwarden 实例驻留在子域的根目录下(即 bitwarden.example.com,而不是 bitwarden.example.com/some/path)。上游的 Bitwarden 服务器目前只支持子域根目录,而 Vaultwarden 则增加了对备用基本目录的支持。对于某些用户来说,这很有用,因为他们只能访问一个子域,并希望在不同的目录下运行多个服务。在这种情况下,他们通常可以做一些显而易见的选择,比如使用 mysubdomain.example.com/bitwarden。然而,您也可以通过把 Vaultwarden 放在类似 mysubdomain.example.com/vaultwarden/<mysecretstring> 这样的目录下来提供额外的保护,其中 <mysecretstring> 有效地充当一个密码。也许有人会说这是通过隐藏实现安全,但实际上这是深度防御 -- 子目录的隐蔽性只是额外的一层安全保护,而不是为了成为主要的安全手段(用户主密码的强度仍然是主要的安全手段)。

有关安全性子路径托管的一般性讨论,请参阅:https://github.com/debops/debops/issues/1233

如果你想让 Caddy 断开除 vaultwarden 之外的所有连接:

mysubdomain.example.com {
	route {
		reverse_proxy /my-custom-path/* 10.0.0.150:8083 {
			header_up X-Real-IP {remote_host}
		}
		handle /* {
			abort
		}
	}
}

最后更新于