快速部署Bitwarden_rs,搭建属于自己的跨平台密码管理方案

前言

从LastPass转入Bitwarden一段时间,综合来说体验还是不错的,基本功能免费、开源、全平台、好看的UI、中文面板、比较好用的自动保存和自动填充功能,让我喜欢上了这个密码管理器。免费的普通账户其实已经能满足大多数人的日常需求了,而付费的高级会员功能主要包含大容量附件存储、2FA(两步验证)支持、密码安全检测、客户支持...
因为开源的原因,出现了使用Rust实现并兼容官方客户端的项目Bitwarden_RS,其提供了Bitwarden基本功能的完整实现,还支持组织、2FA等高级功能,并且制作为Dokcer镜像,方便快速部署,也有比较完善的相关文档。
其实服务本身只需要简单的两条命令就能部署完毕。

docker pull vaultwarden/server:latest
docker run -d --name vaultwarden -v /vw-data/:/data/ -p 80:80 vaultwarden/server:latest

但是这么部署后并不能直接使用,我们需要将自己的域名解析到IP地址上,并申请和部署域名的SSL证书,因为主机上还有其他服务,因此还可以使用Nginx进行反向代理,让子域名成为访问页面,还可以进行强制HTTPS跳转和禁止IP访问等配置...
刚好手上有一台空闲的机子,正好搭个服务,虽然官方wiki已经写的比较详细了,但是部署时还是遇到了一些小问题,因此记录一下详细步骤。
我的这台主机配置是1C2G 60GB 6M的轻量服务器,操作系统是Ubuntu Server 20.04 LTS 64bit

主机信息

准备阶段

进行自建Bitwarden之前,需要准备以下必要条件:

  1. 用于运行服务的主机
  2. 域名
  3. SSL证书
  1. 以下命令多数以root用户或以sudo提权进行执行
  2. 超链接基本上为官方文档或说明的对应链接
  3. 假设我们的域名是example.com,以下均以该域名指代真实域名

注册域名

随便找一个域名注册商进行注册自己喜欢的域名即可,目前最便宜的域名基本上是6位以上纯数字的.xyz域名,新入和续费价格基本上在$0.99/yr(6元人民币)左右,有些注册商曾经还有活动,算上优惠券之类的折扣,十年的xyz域名只需要二三十元。xyz的优点是续费价格便宜,缺点就是如果要享受低价折扣,只能用长位纯数字,并且xyz这个域名本身也不咋样,不过用于自用的服务倒是无所谓,反正在好记的情况下,越便宜越好。

申请SSL证书

Bitwarden Web vault使用了与HTTPS上下文相关的网络加密API(SubtleCrypto),为了使其正常运行,同时保证自己的数据安全,必须启用HTTPS,因此需要SSL证书,可以使用付费证书,也可以使用Let's Encrypt等机构颁发的免费证书。

如果能够自行进行申请配置或已配置完毕,可以跳到下一步。
申请免费的Let's Encrypt证书有很多方式,一般使用certbotacme.sh,这里我使用certbot进行申请。

安装certbot

需要安装Nginxcertbot

#更新apt源
sudo apt update
#安装certbot和相关包,如果没安装Nginx,这个步骤会自动安装Nginx
sudo apt install certbot python3-certbot-nginx

成功安装完毕后,可以输入命令进行确认。

root@VM-4-5-ubuntu:~# certbot --version
certbot 0.40.0
root@VM-4-5-ubuntu:~# nginx -v
nginx version: nginx/1.18.0 (Ubuntu)

此时在浏览器中访问主机的IP地址,会出现Nginx的欢迎页面。
然后我们在域名提供商的管理页面,将域名解析到该IP上,因为一般都是用子域名来访问不同服务页面,比如博客页面通过blog.example.com访问,RSS服务通过rss.example.com访问...

域名解析

因此,我们创建一条A Record(A记录),Host的值使用vault,Value设置为主机的IP地址
等待一段时间后,访问该网址http://vault.example.com,成功显示Nginx的欢迎页面即可。
在运行certbot之前,先修改一下Nginx的配置,把server_name改成自己的域名,默认的配置文件去掉注释的结构大致如下。

# /etc/nginx/sites-available/default
server {
        listen 80;
        listen [::]:80;

        root /var/www/html;

        server_name vault.example.com;

        location / {
                try_files $uri $uri/ =404;
        }
}

在保存后通过

sudo nginx -t

校验配置文件,若成功则输入

sudo nginx -s reload

重新加载配置文件。

# 执行结果
root@VM-4-5-ubuntu:/etc/nginx/sites-available# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

root@VM-4-5-ubuntu:/etc/nginx/sites-available# nginx -s reload
root@VM-4-5-ubuntu:/etc/nginx/sites-available#

之后就可以运行certbot获取SSL证书了。

自动申请与续期

一般使用Nginx插件默认方案即可

sudo certbot --nginx -d vault.example.com

执行后会以交互式脚本询问一些问题,依次填写后,会自动申请证书并更新Nginx的相关配置,以该方式进行申请成功后,certbot会自动添加一个定时任务来进行证书的续期。

手动申请

因为一些特殊的原因,我的主机无法进行HTTP challenge,所以也无法使用最方便的方式进行SSL证书的自动申请和自动续期,因此我选择手动方式申请并进行DNS challenge,这种方式并不能够直接自动续订(可以通过提供的Pre and Post Validation Hooks,结合服务商的Open API来进行自动化)。

指定以DNS challenge方式手动申请证书

sudo certbot certonly --manual --preferred-challenges=dns

第一次使用同样以交互式脚本询问一些问题。

root@VM-4-5-ubuntu:/etc/nginx/sites-available# sudo certbot certonly --manual --preferred-challenges=dns
Saving debug log to /var/log/letsencrypt/letsencrypt.log
# 填写你的邮箱
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): your e-mail@xxx.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 同意服务条款
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: a

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 是否要订阅相关的邮件
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: n
# 输入域名,我要为子域名申请通配符证书,所以用 *.example.com,也可以只申请vault.example.com.同时也申请example.com的证书
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel): *.example.com example.com
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y

然后就会要求你在域名解析页面添加一条TXT record,Host的值是_acme-challenge,Value是下方的随机字符串。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.example.com with the following value:
# 随机字符串
9M4vHsQ9A1D2e5123efADSAdsdas-TpY-y8RU

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Linux下可以通过dig命令查询是否已经生效。

dig -t txt _acme-challenge.example.com

Windows则通过nslookup -qt 记录类型 域名进行查询。

nslookup -qt=txt _acme-challenge.example.com

若看到回应包含自己设置的字符串,则说明已生效,按下回车键继续申请证书,若成功,则会出现以下提示。

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.com/privkey.pem
   Your cert will expire on 2022-04-06. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

证书的默认位置在/etc/letsencrypt/live/中。
重新配置Nginx。

# /etc/nginx/sites-available/default
server {
        listen 80 default_server;
        server_name _;
        return 403;
}
server {
        listen 80;
        listen [::]:80;
        # SSL
        listen 443 ssl http2;
        ssl_certificate   /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key    /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;preload" always;
        add_header X-Content-Type-Options nosniff always;
        add_header X-Frame-Options SAMEORIGIN always;
        add_header Referrer-Policy 'strict-origin-when-cross-origin';

        server_name vault.example.com;
        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;

        location / {
                try_files $uri $uri/ =404;
        }

}

保存之后,通过nginx -s reload重新加载Nginx配置。
访问https://vault.example.com后会发现,原来的Nginx欢迎页面的地址栏多了一个锁形标志。

通过Docker部署Bitwarden_RS

在Ubuntu安装Docker的官方文档Install Docker Engine on Ubuntu,国内安装比较慢可以参照的清华镜像站的安装文档
安装完毕后,输入docker -v能够正常输出版本号,基本上说明安装完成了。
接下来根据Bitwarden_RS官方文档进行拉取镜像并启动。

# 拉取镜像
sudo docker pull Vaultwarden/server:latest

接下来,可以用任意方法生成一个长随机字符串,它最好足够的长和复杂,因为是用于容器环境变量中访问管理页面的token,记录并保管好它。

root@VM-4-5-ubuntu:~# openssl rand -base64 48
Aav/MXoLw3xHvFN6Rz8y6E495M9cqerDbt+gXyY5T6ld1TZV59IFJiBcBvoeM758

然后,用以下命令启动容器

sudo docker run -d --name bitwarden \
-v /bitwarden/data/:/data/ \
-e ADMIN_TOKEN=Aav/MXoLw3xHvFN6Rz8y6E495M9cqerDbt+gXyY5T6ld1TZV59IFJiBcBvoeM758 \
-e LOG_FILE=/data/vaultwarden.log  \
-e LOG_LEVEL=warn \
-e EXTENDED_LOGGING=true \
-e "TZ=Asia/Shanghai" \
-p 12345:80 \
vaultwarden/server:latest

这个命令除了启动以外,还做了以下几件事:

  1. 将该容器命名为bitwarden;
  2. 将主机的/bitwarden/data目录作为共享文件系统,挂载到容器的/data路径;
  3. 设置用于开启管理界面的环境变量
  4. 设置日志记录相关的环境变量
  5. 指定时区(如果不指定时区,容器内的时区和主机可能会不同,导致后面的fail2ban无法生效)
  6. 将容器的80端口映射到主机的12345端口

执行命令后,没有报错,屏幕输出容器ID,执行docker ps查看容器状态,如果显示在列表中,说明部署成功。
主机如果有开放防火墙的12345端口,可以直接尝试通过主机IP:12345访问。
管理页面则为主机IP:12345\admin,注意端口号与路径间并无hash符号(#),登录令牌为上面生成的长字符串。
因为没有通过HTTPS访问,所以在登录的时候会有相关提示,不允许直接使用,因此接下来配置Nginx,进行反向代理,最终配置如下,可以进行一些修改。

# /etc/nginx/sites-available/default
server {
        listen 80 default_server;
        server_name _;
        return 403;
}

server {
        listen 80;
        listen [::]:80;
        # SSL
        listen 443 ssl http2;
        ssl_certificate   /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key    /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;preload" always;
        add_header X-Content-Type-Options nosniff always;
        add_header X-Frame-Options SAMEORIGIN always;
        add_header Referrer-Policy 'strict-origin-when-cross-origin';

        if ($scheme = http) {
                return 301 https://$server_name$request_uri;
        }

        server_name vault.example.com;

        location / {
                proxy_pass http://127.0.0.1:12345;
                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;
        }

}

保存之后,通过nginx -s reload重新加载Nginx配置。
然后就可以访问之前解析的子域名vault.example.com,根据配置文件,它会被反向代理到主机的12345端口上,就能通过子域名成功访问Bitwarden的登录页面。然后我们就可以把上面测试部署情况时开放的12345端口关掉,不需要直接暴露这个端口。
至此,通过Docker成功部署Bitwarden_RS,并申请和配置了SSL证书,通过Nginx反向代理至子域名。

安全相关

fail2ban

虽然登录密码足够的复杂,但是仍有被暴力破解的可能性,因此需要安装和配置fail2ban

# 安装fail2ban
sudo apt-get install fail2ban -y

fail2ban在Ubuntu下一般被默认安装到/etc/fail2ban

网页保险库的设置

首先需要设置过滤规则,进入/etc/fail2ban/filter.d目录,新建一个文件vaultwarden.local,将以下内容复制到其中。

# /etc/fail2ban/filter.d/vaultwarden.local

[INCLUDES]
before = common.conf

[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =

然后再进入/etc/fail2ban/jail.d目录,新建一个文件vaultwarden.local,将以下内容复制到其中,并进行一些修改,其中logpath项的值需要修改为日志文件的路径。

# /etc/fail2ban/jail.d/vaultwarden.local

[vaultwarden]
enabled = true
port = 80,443,8081
filter = vaultwarden
banaction = %(banaction_allports)s
logpath = /bitwarden/data/vaultwarden.log
maxretry = 3
bantime = 8640000
findtime = 86400

管理页面的设置

与网页保险库的设置类似。
进入/etc/fail2ban/filter.d目录,新建一个文件vaultwarden-admin.local,将以下内容复制到其中。

# /etc/fail2ban/filter.d/vaultwarden-admin.local

[INCLUDES]
before = common.conf

[Definition]
failregex = ^.*Invalid admin token\. IP: <ADDR>.*$
ignoreregex =

然后再进入/etc/fail2ban/jail.d目录,新建一个文件vaultwarden-admin.local,将以下内容复制到其中,并进行一些修改,其中logpath项的值需要修改为日志文件的路径。

# /etc/fail2ban/jail.d/vaultwarden-admin.local

[vaultwarden-admin]
enabled = true
port = 80,443
filter = vaultwarden-admin
banaction = %(banaction_allports)s
logpath = /bitwarden/data/vaultwarden.log
maxretry = 3
bantime = 8640000
findtime = 86400

设置完毕后,重新加载fail2ban使配置生效。

sudo systemctl reload fail2ban

测试是否生效

会使当前IP无法登陆主机,最好使用移动数据或有其他能登陆主机的设备上进行测试。
访问网址的主页或管理页面,随意输入错误的账号和密码(无需真实存在),超过3次后,如果发现无法正常访问页面,则说明fail2ban设置成功。
查看暴力破解被ban掉的IP

# 查看web vault是 vaultwarden,查看管理页面则为vaultwarden-admin
fail2ban-client status vaultwarden

解封IP

fail2ban-client set vaultwarden unbanip XXX.XXX.XXX.XXX

虽然安装了fail2ban后默认配置可以同时保护ssh不被暴力破解,不过为了服务器安全,建议停用ssh密码登录,改为使用密钥登录,并禁用root用户登录。

备份与还原

下文的data目录指之前设置的共享文件夹,在文中应该是/bitwarden/data/

备份

参照官方wiki的备份你的保险库说明。
必须备份的文件有:

  1. data目录中的db.sqlite3,它包含了大部分的重要数据。
  2. data目录中的attachments目录,它包含了所有文件附件。如果没有包含附件的记录,这个目录不会被自动创建。
    最好确保在没有正在使用(读写数据库)的情况下进行备份。
    一个简单的备份db.sqlite3的命令,将源路径和目标路径修改为自己想要的路径。
sqlite3 data/db.sqlite3 ".backup '/path/to/backups/db-$(date '+%Y%m%d-%H%M').sqlite3'"

还原

  1. 重新安装/部署Bitwarden_rs
  2. 将备份文件恢复到data目录中

遇到的问题

fail2ban不生效

配置好fail2ban后,能够正常重启服务,但是在页面输入任意账号密码多次错误后并不会ban掉ip。

最初启动的时候没有加时区参数,导致容器内时区和主机时区不同步,所以没有生效,加上一样的时区之后就正常了。

评论

  1. 2 年前
    2022-3-30 11:39:20

    最后nginx配置文件location那边的$符号被吞了

    • hafuhafu
      博主
      dizzy
      2 年前
      2022-3-30 16:02:12

      感谢反馈,因为用WP Editor插件写的,所以没太注意.大概是因为$符号被编辑器插件当成LaTeX公式解析了…目前已禁用科学公式解析

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇