部署兼容原版 Bitwarden 的后端

Last updated on 2022-02-11, Fri, 06:44 PM

View in English

Update:

vaultwardenwiki 最近基本完工了,内容更详细。

前言及参考资料

从前,我都是在自己的设备上明文存储密码。

每次忘掉登录凭证的时候,我就得解锁加密的分区,然后复制粘贴密码。从各种意义上都很危险。

我也不知道怎样才能找到一个可信的托管。有些大厂,比如谷歌和苹果,提供了密码管理服务。可是你敢用吗?反正我不。我也不大敢用 1passwordLastPass 之类的付费/私有密码管理器。

然后咱从 ous50 那里听说了 Bitwarden 的存在。

这玩意开源,可以在自己的机器上部署。虽然他们也提供托管,但我还是想自己来。

关于这玩意的教程很多,我则是直接对着 ous50 的教程照搬。

附上他写的文章。我这篇文章的大部分内容,你都可以在他那里看到。

https://blog.ous50.moe/2021/03/12/vaultwarden%E6%90%AD%E5%BB%BA/

这一回,我不部署原版的后端,而是一个用 Rust 重写的实现,名叫 vaultwarden, 曾用名 bitwarden_rs. 这玩意可以白嫖大部分的付费版功能,比如重要的二步验证手段 TOTP.

前置需求

如果你的机器带不动 docker, 请直接去看 vaultwarden 在 GitHub 的 wiki . 那里有直接安装二进制程序的方法。

  • 一台可以分出 200MiB 运行内存和 1GiB 硬盘空间的 Linux 服务器。
  • 一个有效的域名。(这边我用 bitwarden.example.com 来示例,记得换成你自己的。)

若你想自行编译 docker 镜像,那需要 500MiB 的 RAM 和至少 5GiB 的硬盘空间。

安装依赖

要事先安装一些软件依赖。

这里你需要 nginxdocker.

当然,还有一个你喜欢的文本编辑器。

Debian 10

为了添加 stream_ssl_preread 模块,我们需要 nginx 的版本高于 1.19.2[1].

把 NGINX 自己维护的镜像源添加进去,以追上主线版本。

wget https://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key
sudo echo "deb https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list
sudo echo "deb-src https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list
sudo apt update
sudo apt install docker nginx -y

Arch Linux

sudo pacman -Syy
sudo pacman -S nginx docker

部署防火墙和 TLS 规则

记得先签好证书

防火墙

挡住爬虫机器人。

Rule

Rule

TLS

把所有能开的优化都开出来。

(自行选择)把最低TLS版本设置成 1.3 以防止降级。

俺们觉得 HSTS 有点麻烦,就放最后做。

配置反代

俺们只会 nginx 了。用 apache 或者 caddy 也没问题,选你最熟悉的引擎。

如果你的 443 端口空闲的话,直接起一个 http server 就行,否则需要复用一下端口。

使用 443 端口

可以用 stream 模块来复用端口。另外记得加一个 server.

# nginx.conf
stream {
    map $ssl_preread_server_name $name {
       sub1.example.com svc1;
       sub2.example.com svc2;
       bitwarden.example.com bw;
    }
    upstream svc1 {
        server 127.0.0.1:PORT_TO_USE_1; 
    }
    upstream svc2 {
        server 127.0.0.1:PORT_TO_USE_2; 
    }
    upstream bw {
        server 127.0.0.1:PORT_FOR_BW;
    }
    server {
        listen 443 reuseport;
        listen [::]:443 reuseport;
        proxy_pass	$name;
        ssl_preread on;
    }
}
    server {
            listen 127.0.0.1:PORT_FOR_BW; ssl http2;
            server_name  bitwarden.example.com;

或者直接加一个 server 监听 443 端口就成。

}
    server {
            listen      443 ssl http2;
            listen [::]:443 ssl http2;
            server_name  bitwarden.example.com;

其他的属性配置

判断一下 host, 防止有人直接填 IP 访问。这还可以避免证书不匹配的问题。

老样子,记得屏蔽 bot. 另外,注意一下 SSL 证书和密钥的路径。

顺便分流了一下 websocket 连接,可以用来做即时通知。proxy_pass 的端口随你设置。当然,最好是监听回环,localhost.

    if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot|^$") {  
        return 404;
    }

    # Block Direct Access via IP
    if ($host != "bitwarden.example.com") {
        return 404;
    }

    # HSTS    
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; 
  
    ssl_certificate /etc/nginx/ssl/bitwarden.example.com_ecc/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/bitwarden.example.com_ecc/bitwarden.example.com.key;

    keepalive_timeout   70;

    # OCSP stapling
    ssl_stapling        on;
    ssl_stapling_verify on;
    resolver 1.0.0.1; ## DNS

    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers on; 

    location / {
        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 $scheme;
        proxy_pass http://localhost:8080; 
    }

    location /notifications/hub {
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://localhost:3012;
    }

    location /notifications/hub/negotiate {
        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 $scheme;
        proxy_pass http://localhost:8080;
    }

    # Optionally add extra authentication besides the ADMIN_TOKEN
    # If you don't want this, leave this part out
    location /admin {
        # See: https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/
        auth_basic "Private";
        auth_basic_user_file /etc/nginx/bwauth;

        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 $scheme;
        proxy_pass http://localhost:8080;
    }
}

配置好以后,记得(重新)起一下服务。

nginx -t
sudo systemctl enable --now nginx
sudo systemctl reload nginx --force

准备 Docker

把编译好的镜像拉下来就能用了。

添加 ADMIN_TOKEN 能打开管理面板 https://bitwarden.example.com/admin

使用尽可能长而随机的 token 来抵抗暴力破解。

sudo docker pull vaultwarden/server:latest

sudo docker run -d \
  --name=vaultwarden \
   -e WEBSOCKET_ENABLED=true \
   -e ADMIN_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
   -e LOG_FILE/data/bitwarden.log \
   -p 127.0.0.1:8080:80 \
   -p 127.0.0.1:3012:3012 \
   -v /vw-data/:/data/ \
  --restart=always  \
vaultwarden/server:latest

参数

给出一些参数的解释。

  • -p 完整写作 --publish, 将容器的一个端口暴露到主机的一个端口上。

    HOST_PORT:CONTAINER_PORT

    绑定到 localhost 确保不会直接暴露到公网。

  • -v, 也就是 --volume, 则是把主机的一个目录连接到容器内的某个路径,就像是挂载了一个外置卷到容器上。

    HOST_DIR:CONTAINER_DIR

    主机和容器都可以读写这个卷,而且删除容器不影响外置卷。

  • --restart-always 保证服务在掉线后自动重启,类似进程守护。

  • --name 是容器的名字,随便取。

  • -e 引入一些环境变量。

自行编译镜像

如果想要自行编译镜像,不妨看看 这里 。但我不推荐这样做。

自行编译非常吃资源。但还是放出示例。

git clone https://github.com/dani-garcia/vaultwarden.git
cd vaultwarden
sudo docker build -t vaultwarden .

sudo docker run -d \
  --name=vaultwarden_built \
   -e WEBSOCKET_ENABLED=true \
   -e ADMIN_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
   -e LOG_FILE/data/bitwarden.log \
   -p 127.0.0.1:8080:80 \
   -v /vw-data/:/data/ \
  --restart=always  \
vaultwarden:latest

应该就能跑起来了。

配置域名解析

服务起来了,就可以暴露到公网了。

bitwarden.example.com 解析到你的机器上。

打开云朵是个好主意,可以加快载入,抵抗攻击并隐藏源站。

如果你没有固定的 IP, 那也可以 CNAME 到你的 DDNS 上面。估计你也会的。

我知道有人担心安全问题。毕竟 Cloudflare 的 CDN 是可以看到数据的。

但别担心。按照 Bitwarden 的 文档 ,所有的数据都是先加密后上传,TLS 只是额外保护罢了。

对于 Send 同理

所有的数据来回都是加密的,Cloudflare 看不到你的密码们,放心吧。

看到原数据的唯一方式是掌握你的主密码。要确保它足够强大,能扛过暴力攻击,这才是最重要的。

配置密码仓库

当然也可以用配置文件 config.json 完成设置。vaultwarden 的作者 dani-garcia 在 GitHub 上提供了一个完整的 config.json.

打开 https://bitwarden.example.com/admin 用之前设定的 ADMIN_TOKEN 登录。

限制注册

若你不希望别人白嫖你自建的服务,那最好就在管理面板设置禁止注册。

拿掉 Allow new signups 的钩子,防止他人注册。

(可选)设置 SMTP 服务

设置一个 SMTP 服务器,可以用来发发验证邮件和密码提示。

Yandex 360 就很不错。它是有免费版的,完全够用。

当然,注册的事情就不放在这里说了。

您也可以开启 Email 二步验证。

切记!务必保存!

修改了设置以后

务必保存!

务必保存!

务必保存!

否则修改的内容将会丢失!

邀请

若您想拉人进来,大可使用邀请功能。

切换到 Users 然后输入对方的邮箱地址。这样就可以在数据库增加新的账户,并且是 Verfied 状态。然后这个新用户就可以无视 Allow new signups 的限制了。

顺手鲨了所有认不得的申必用户。

等他们创建完帐号,就可以进入自己的仓库,管理面板上的 Invited 标签也会消失。

这样就可以安全地新增用户,包括你自己。

注册新用户

bitwarden.example.com 注册一个账户。

牢记你的密码!这玩意可没法找回或者重置! 万一你忘掉了,就只能建立新账户了。

启用二步验证

然后去 Settings -> Two-step Login 设置 Authenticator App 启用二步验证。基于 TOTP 的六位验证码有很多玩意都支持。很多平台都有很优秀的应用,少用 Authy.

Android 有 andOTPAegis, Google Play 和 F-Droid 上都能下到。

Linux 可以用一个 GTK 写的过气玩意 OTPClient.

备份和还原

你大可完整备份一下自己的数据库。保不准哪天你的机器可能就完蛋,从而丢失数据,就像 OVH 这样

XD

备份

可以用管理面板来备份数据。备份的数据库会被命名为 db_YYYYMMDD_HHMMSS.sqlite3.

也可以调用 sqlite3 命令行。 Reference

sqlite3 ./vw-data/db.sqlite3 "VACUUM INTO './vw-data/db_$(date '+%Y%m%d-%H%M').sqlite3'"

这个数据库是不包括密码管理器配置文件以及网站图标的,只有保存的用户密码和笔记。附件在 /attachments, 如果你在密码仓库里加了附件,那必须备份这个文件夹。

Sends 则被存在 /sends 中。

压缩

可以先打包成一个文件。比如这样:

tar --zstd -cvf backup.tar.zst ./vw-data

若要尽量减小体积,可以这样做:

tar --zstd -cvf 'vw-data.tar.zst' \
    /vw-data/attachments \
    /vw-data/config.json \
    /vw-data/db*.sqlite3 \
    /vw-data/rsa* 

单独备份 /sends 文件夹。

tar --zstd -cvf 'vw-data-sends.tar.zst' /vw-data/sends

然后把这个传输到本地。 强烈建议使用安全的协议来传输,直接下载到已经加密的目录下。比如说,你可以用 veracrypt 起一个加密卷然后挂载上去。用 cd 或者直接在那里打开,把终端的路径切过去。然后用 scp 或者 rsync 通过 SSH 传输。

scp -P$PORT [email protected]:~/backup.tar backup.tar 

不压缩

不想压缩就直接跳过这步。

记得把 $PORT 换成你自己的。

rsync -av --progress \
    --include "attachments" \
    --include "config.json" \
    --include "db*.sqlite3" \
    --include "rsa*" \
    --exclude "*" \
    -e "ssh -p$PORT" [email protected]:/vw-data/ vw-data

仍然单独备份 /sends 文件夹。

rsync -av --progress -e "ssh -p$PORT" [email protected]:/vw-data/sends  vw-data/

或者全量备份,直接同步整个文件夹:

rsync -av --progress -e "ssh -p$PORT" [email protected]:/vw-data/ vw-data

保存

卸载你的设备,存放在安全的地方。当然,也可以把加密卷复制到你能控制的地方。

恢复

这个也很简单。

把你之前备份的文件安全地送上去就行。

scp -P$PORT backup.tar [email protected]:~/backup.tar 

然后解压。

tar -C / --zstd -xvf backup.tar.zst

你也可以选择把 db_YYYYMMDD_HHMMSS.sqlite3 改名叫 db.sqlite3, 以恢复到某个时间的备份。

想少传一点:

rsync -av --progress \
    --include "attachments" \
    --include "config.json" \
    --include "db.sqlite3" \
    --include "rsa*" \
    --exclude "*" \
    -e "ssh -p$PORT" vw-data/ [email protected]:/

/sends 还是单独对待:

rsync -av --progress -e "ssh -p$PORT" vw-data/sends [email protected]:/vw-data/ 

直接全部上传:

rsync -av --progress -e "ssh -p$PORT" vw-data [email protected]:/

要求重新同步

当然,最好还是让用户(说白了也就你自己)重新同步一下数据。

到管理界面选择 Users 然后设定一次强制同步。

总结

这款强大的密码仓库,我暂时也就学了这些基本的部署和维护。

至于其他的玩法,你不妨自己探索。

祝玩得开心。

致谢

再次感谢 ous50 的教程。我在自建过程中遇见许多问题。仰仗了他的帮助,我才得以成功部署。

同时感谢 billchenchina 对本文行文结构的改进。

参考


部署兼容原版 Bitwarden 的后端
https://blog.h3a.moe/src/d07395/
Author
H3arn
Posted on
2021-10-13, Wed, 11:53 PM
Updated on
2022-02-11, Fri, 06:44 PM
Licensed under