2021年之前我一直使用的LastPass做为我的首要密码管理工具,当时被他的浏览器拓展吸引,搭配这个插件可以做到自动填充,使用起来十分愉快。不过因为LastPass后台一直没有中文语言,且21年期间还出现了国内偶发性打不开网页的问题(刚刚查资料还发现2022年12月爆发过一次安全事故,不过算是躺锅)。

知名密码管理器 LastPass 此前因 Authy 员工被钓鱼而出现安全事故,这话听着有点绕口,大概是这么个情况:多因素认证器 Authy 员工收到钓鱼邮件后泄露了密码,黑客入侵了 Authy 的服务器窃取了 LastPass 工程师账户的 2FA,当然估计黑客也提前通过其他方式获取了这名工程师的账号密码,最终结果就是 LastPass 被黑了。

那之后我就在V2ex上寻求替代产品,基于Selfhost的理念我最终选了Bitwarden作为自己的密码管理软件,并撰文 docker 自建 bitwarden 服务 记录了搭建过程,一直愉快的使用至今。

不过使用期间发现了一些问题,如:

  1. 所有平台增加完密码记录后如果想所有平台都同步的话,需要先在该平台手动同步密码库,且其他需要使用的平台也要手动同步密码库才能收到更新。
  2. 浏览器端、桌面端的「使用设备登录」功能无法使用,虽然开启了该功能,但是手机上始终接受不到登录请求。
  3. 无法使用webauth功能。

我之前猜测是自己的那里设置没有弄对,期间也查阅过资料,但是都无果而终。

好在最近偶然间查阅Bitwarden版本更新信息时发现了提到「移除以前关于Websocke变量设置」的关键字,便对方面进行了相关资料查询,发现果然是自己的搭建出了问题,Nginx的WebSocket功能没有正常启用,自然相关的实时同步、使用设备登录等功能都无法使用,今天摸索一番后终于修复了该问题。

确认WebSocket是否开启

根据 6.启用 WebSocket 通知 所述,检查WebSocket是否启用的方式有2种

  • 打开浏览器的开发人员工具,转到网络选项卡然后筛选 WS/WebSockets。注销或刷新页面并再次登录,您应该会看到升级后的 WebSocket 连接的 101 响应。如果您单击该行,您应该能够看到消息。如果您没有在 /notifications/hub 上获得状态代码 101,则表示某些配置不正确。消息将显示在浏览器的控制台窗口中:[2023-12-01T00:00:00.000Z] Information: WebSocket connected to wss://HOST_NAME/notifications/hub?access_token=eyJ0eX......
  • 2.打开两个不同的浏览器或隐身/隐私窗口。在两个浏览器上登录您的帐户。创建一个新的条目,或者重命名一个条目,在另一个浏览器中应该会立即收到更改。

我当时根据第一项检查就发现结果是400还是多少来着,完全就没通。所以我查找了一些资料,最终通过修改Nginx配置正确启用了WebSocket

  1. 定义header要用到的参数词典(个人理解,类似定义变量)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
  1. 修改bitwarden在nginx中location内的设置,增加如下配置
# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

最终完整配置如下

# 定义参数
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      "";
}


server {
    listen 443 ssl http2;
    server_name mybitwarden.com;
    
    # ssl配置
    include snippets/ssl-params.conf;
    
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host:$server_port; 
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_pass http://127.0.0.1:123123;

        # webscoket配置
	    proxy_http_version 1.1;
	    proxy_set_header Upgrade $http_upgrade;
	    proxy_set_header Connection $connection_upgrade;
    }
    location ~ /.well-known {
        allow all;
    }
    client_max_body_size 50m;
}

bitwarden的nginx配置文件

运行 nginx -s reload 重载Nginx配置即可,现在可再根据前面的方法测试是否开启成功。

安全加固

同时,我在该wiki中找到了一篇 安全强化指南 ,虽然自己之前也有做过一些安全强化,但是还有不足的地方,遂重新根据该指南做了强化,并挑了几条我做了的作为展示。

高强度主密码

一定要使用一个你从来没在互联网上使用过的密码,大小写符号都带上,长度最好在12位以上。

随机子域名或隐藏在子目录下

  • 随机子域名:也就是没有固定含意的子域名,如:https://fjisjfasjfois.yourdomain.com ,这个比较简单,更改域名绑定即可。
  • 隐藏在子目录下:你可以在随机子域名的情况下再隐藏在子目录,如:https://fjisjfasjfois.yourdomain.com/ofjiosnvoxz ,这有个官方的caddy示例,nginx的可以自己找找。
mysubdomain.example.com {
	route {
		reverse_proxy /my-custom-path/* 10.0.0.150:8083 {
			header_up X-Real-IP {remote_host}
		}
		handle /* {
			abort
		}
	}
}

禁用新用户注册&邀请注册

如果是自己使用则完全可以禁用掉这两个选项,避免居心叵测的人注册后做一些渗透行为。在docker-compose文件中增加环境变量 SIGNUPS_ALLOWED: "false" 即可。或增加在run命令中 -e SIGNUPS_ALLOWED=false

禁用显示密码提示

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

以非 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 中类似操作:

version: '3'

services:
  vaultwarden:
    container_name: vaultwarden
    image: vaultwarden/server:latest
    restart: always
    user: 1000:1000
    ports:
      - 521514:80
    environment:
      DOMAIN: "https://yourdomain.com"
      WEBSOCKET_ENABLED: "true" #是否开启WebSocket
      SIGNUPS_ALLOWED: "false"   #是否开启注册,自用的话自己搭建好注册后改成false
      WEB_VAULT_ENABLED: "true" #是否开启Web客户端
    volumes:
      - /home/data/vaultwarden/:/data/
⚠️
请注意,这里我踩了一个坑:如果你之前已经运行过一次了,那么生成的数据库文件权限默认是root账户,此时如果你再更改用户,则在登录时会出现 Error saving device 的错误提示。
此时你需要执行 docker exec -it vaultwarden bash 进入容器内部,再进入根目录中的 /data 目录中,执行 chown -R 1000:1000 改更文件所属用户。

使用2FA

推荐一定要使用2FA做两步验证,避免暴力破解,并且,我在这里希望大家所有的重要服务最好都能开启2FA,这能极大程度的增加你的账户安全性。如果你不知道什么工具,可以参考我这篇 AuthenticatorPro 开源的本地两步验证工具使用教程

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

End

至此基本上我们能做的都做完,目前市面上的这些数据库密码管理程序都是加密对数据做存储的,只要能保证你的主密码不泄露,即便是密码库被拿走了也不用担心数据泄露的问题。