在几年前我初次接触密码管理器时,最早使用的是开源的 KeePassX,体验不尽如人意,唯一的优点可能是开源。所以后来转向在安全性和用户体验的平衡上拿捏得比较好的替代品。当时写了一篇《密码管理工具 Safe In Cloud》来介绍最先使用的 SafeInCloud 这款产品。

SafeInCloud 当时吸引我的,除了在用户体验上比 KeePassX 系列项目做得更好外,还有一点就是:实现了密码数据库的跨设备同步,并且可以选择同步使用的云服务提供商。这是 SafeInCloud 与其他提供密码数据库托管服务的产品(例如 1Password)的重要区别:1Password 之流是只能将密码数据存储在他们自己的服务器上的。

在这之后,基本上有新的密码管理工具出现,只要是带有图形界面的,我基本都会关注一下。之后尝试过其他一些产品,转向了在移动端上体验较为便捷的 Enpass。Enpass 在数据同步的稳定性上比当时的 SafeInCloud 更胜一筹,并且支持使用私有云进行同步。

今年春节期间,在例行的季度安全审查中,我无意间发现一个新的开源密码管理器 Bitwarden。这个密码管理器使用自行开发的服务端来存储和同步数据,服务端使用 .Net 开发,浏览器扩展和桌面客户端使用 Javascript,移动端使用 C#

当时最吸引我的几点是:这个项目的几个客户端体验都做得非常出色,自动填充功能很流畅;并且支持一次性密码(OTP 二次验证)。此外,一些高级特性只需要 10 美元每年的订阅套餐。因此当时立即购买了一年的高级订阅,在自己服务器上安装测试,并快速切入使用。

在持续使用几周之后,发现有两个明显的缺点:由于 Bitwarden 服务器使用 .Net 开发,如果使用 Docker 来部署,镜像体积过大;此外它使用 MSSQL 数据库,部署这个数据库对服务器要求比较高。因此在使用期间我一直在注意社区有没有其他替代产品可用,但同时也在纠结,如果再次切换到其他密码管理器,可能会失去流畅的客户端体验。

在 3 月份时,我发现社区有人使用 Ruby 开发了一个兼容的 Bitwarden 服务器 bitwarden-ruby(后来改名叫 rubywarden)。这个服务器使用 SQLite 服务器来存储数据,对于个人来说已经足够,对比官方服务器来说大幅降低了对机器配置的要求。但是我没有部署 Ruby web 项目的经验,因此一直没有尝试。

7 下旬,在 GitHub 上搜索时发现有人用 Rust 实现了 Bitwarden 服务器,项目叫做 bitwarden_rs,并且提供了 Docker 镜像。这个实现更进一步降低了对机器配置的要求,并且 Docker 镜像体积很小,部署非常方便。此外,官方服务器中需要付费订阅的一些功能,在这个实现中是免费的。部署之后,打算记录一下,也算给有意自己部署的读者留一个参考。

要求

你需要有一个域名。 机器上需要有 Docker 环境。具体参考 Docker 官方文档来安装 Docker-CE。

此外,本文使用 docker-compose 来管理服务,参考 docker-compose 的官方文档来安装。在 Linux 上可以一行命令安装:

1
sudo curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

如果要更新 docker-compose,也可以直接使用这条命令,只需要修改版本号为最新的 docker-compose 版本号。

配置文件

假设你准备在主目录中存放数据,新建一个目录:

1
2
3
cd ~ && mkdir bitwarden && cd bitwarden
pwd
# 应当输出 /home/username/bitwarden

准备一个配置文件:

1
2
3
4
5
6
7
cat >> config.env <<EOF
SIGNUPS_ALLOWED=true
DOMAIN=https://example.com
DATABASE_URL=/data/bitwarden.db
ROCKET_WORKERS=10
WEB_VAULT_ENABLED=true
EOF

以上配置文件的说明:

  • SIGNUPS_ALLOWED 控制是否开放用户注册,因为你必须先注册才能存储数据,所以暂且先打开;
  • DOMAIN 填入你准备分配给 Bitwarden 服务使用的域名;
  • DATABASE_URL 设置数据库在容器内的路径,你可以不设置,默认位于 /data/db.sqlite3
  • ROCKET_WORKERS 设置服务器使用几个线程。10 是默认值,你可以根据机器性能和个人需求适当调整;
  • WEB_VAULT_ENABLED 设置是否开启 Web 客户端。如果开启,可以通过访问你的域名来打开 Web 客户端,用户登录后即可通过网页管理密码。因为注册用户需要,所以也暂且先打开;

准备服务描述文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
cat >> docker-compose.yml <<EOF
version: '3'

services:
  bitwarden:
    image: mprasil/bitwarden:latest
    container_name: bitwarden
    restart: always
    volumes:
      - ./data:/data
    env_file:
      - config.env
    ports:
      - "127.0.0.1:6666:80"
      - "127.0.0.1:6667:3012"
EOF

这个文件主要描述了这些内容:

  • bitwarden 现在是唯一一个服务;
  • image: mprasil/bitwarden:latest 指定使用 Docker Hub 的 mprasil/bitwarden 最新镜像;
  • volumes 中指定将容器内的 /data 目录挂载到宿主机的当前目录下的 data 目录,这样你可以在宿主机上执行数据库的备份操作;
  • ports 指定将容器内的端口映射到宿主机上,并且这些端口只能在宿主机本地访问:
    • 80 端口映射到了宿主机的 6666 端口。这个端口是 HTTP 服务;
    • 3012 端口映射到了宿主机的 6667 端口。这个端口是 websockets 服务;

以后对 bitwarden 服务做的所有操作,都需要预先进入这两个配置文件所在的目录内。

反向代理和 HTTPS、启动服务

因为服务描述文件中将 bitwarden 服务的 80 端口映射到了宿主机的 6666 端口,你可以在宿主机上使用 nginx 或其他反向代理,为你的域名配置对应的代理规则,使得访问域名时,流量可以经由 bitwarden 服务来处理。

注意:因为 Bitwarden 客户端使用了 websockets 长连接,而 Rocket 框架目前无法原生支持 websockets 连接,bitwarden_rs 在内部其实启动了两个服务器。在设置反向代理时注意根据路径不同,需要代理到不同的端口。

如果你使用 Caddy (V2版本),配置样例大致是这样的:

1
2
3
4
5
6
example.com {
    reverse_proxy /notifications/hub/negotiate 127.0.0.1:6666
    # websockets
    reverse_proxy /notifications/hub 127.0.0.1:6667
    reverse_proxy /* 127.0.0.1:6666
}

当然这只是最基础的反向代理配置,你还可以加入 HSTS / 压缩 / XSS 防护等配置。详细的配置样例请参考 bitwarden_rs 官方的 Wiki

此外,现在是 2020 年,还请为域名配置 HTTPS 证书,并将 HTTP 流量阻挡或重定向到 HTTPS。这是对数据安全的基本尊重。 这方面的配置可以参考《快速配置 HTTPS》

如果你在使用 Caddy,仅需要设置好 ACME 邮箱地址,并在配置文件中使用域名,Caddy 会自动帮你申请和管理 Let’s Encrypt 证书。

如果你使用 traefik 或其他反代方案,请自行参考工具各自的官方文档完成配置。

最后启动服务:

1
docker-compose up -d

用户注册和数据导入

启动服务后,通过浏览器访问你配置好的域名,应当看到下图所示的登录界面。

Bitwarden web vault 登录页面

点击下方链接进入注册页面。

Bitwarden web vault 注册页面

注册完毕之后不需要验证邮箱(bitwarden_rs 目前没有实现邮件相关的功能),直接登录。登录之后在左侧栏的「工具」菜单中找到数据导入页面,如图。

Bitwarden web vault 数据导入页面

在这里进行数据导入就可以啦。

关闭用户注册、关闭 web vault

现在你的 Bitwarden 服务器允许任何人注册帐号使用,你可能希望关闭这个功能。在前面生成的 config.env 中,调整以下两项值:

1
2
SIGNUPS_ALLOWED=false
WEB_VAULT_ENABLED=false

修改之后,需要重启 bitwarden 服务才生效。运行以下命令来删除并重新创建容器。不必担心,因为指定了 volume 映射,你的数据不会被删除。

1
docker-compose down && docker-compose up -d

这样就关闭了用户注册功能,并禁用了 web vault 的访问。密码数据之后还是可以在客户端中进行编辑的。

客户端的使用

安装配置好服务端后,还需要在客户端上登录,将数据同步过来,才能使用自动填充等功能。Bitwarden 的客户端界面都大同小异,这里以 Firefox 扩展为例。

首先在登录界面,点击左上角齿轮图标的设置按钮。

Bitwarden Firefox 扩展的登录界面

在设置界面,只需要在最上面的服务器地址栏中,填写你的 Bitwarden 服务器地址即可。然后点击设置界面右上角的「保存」。接下来使用你的邮箱和主密码登录即可同步数据。

Bitwarden Firefox 扩展的登录前设置界面

如何更新服务端

1
2
3
4
5
6
7
8
# 进入服务配置所在目录
cd ~/bitwarden
# 拉取最新镜像
docker-compose pull bitwarden
# 使用最新镜像重新启动服务
docker-compose up -d bitwarden
# 清理旧的镜像文件
docker image rm $(docker image ls -f dangling=true -q)

注意事项

对于使用任何密码管理工具的人来说,主密码的重要性不言而喻。一旦主密码泄漏,相当于将所有帐号密码拱手相让;一旦主密码丢失,所有数据也都化作随机噪音,变得一文不值。所以,请时常审视、改进你的安全策略,定期对帐号做安全审查,不定期修改并牢记主密码。