共计 7389 个字符,预计需要花费 19 分钟才能阅读完成。
前言
如果你的 bitwarden 只托管了一些不重要的密码的话,可以看一下本站之前写的一篇博客(保姆级免 Nginx 反代配置带 SSL 证书 Bitwarden 服务端 ),基本也够安全的了,因为 Bitwarden_RS/Vaultwarden(下文简称 Bitwarden) 底层采用的 rocket tls 最低支持版本是 1.2,而不是 1.0 或 1.1,具体看这里的 官方文档。
但是如果你的自建 bitwarden 托管了你的支付宝微信、银行卡、信用卡等全部身家,甚至几百个比特币的钱包的私钥,那前面提到的博客就不行了,我们必须再提高安全性,毕竟我们的服务暴露在了公网,一旦被不怀好意的人渗透了可就真的麻烦了。
终极加固实战
首先声明一下,一定一定一定要首先优先配置两步验证,这是最基础的安全措施,本文不再赘述~
另外本人遵循 "eat your own dog food" 原则,本文全篇介绍的东西在本人自建的 Bitwarden 服务器上全部有应用到。
本人作为一名渣渣 Linux 运维小学生,着实才疏学浅,本文不能涵盖所有加固措施,遗漏的地方还望大家在评论区留言指出来,本人再补充进正文。
我们的加固涉及到了以下方面:
- 禁止注册和邀请用户
- 禁止显示密码提示
- 基于 Nginx 实现禁止 ip 访问(防止类似于 shedon 的邪恶搜索引擎搜索到,以及脚本小子扫描 ip 段)
- 基于腾讯云控制台和 Nginx 讲解如何配置 ECC 算法的 ssl 证书,舍弃掉 RSA 算法的证书(ECC 算法的破解难度要更高,比 RSA 算法要更上一个台阶),网上搜到的博客试了好几个都不行,我这里的一步到位
- 让 docker 进程以非 root 用户运行,这里踩了一个坑,折腾了个把小时
- 最后一步几乎将自建 Bitwarden 的安全性更上了一个台阶,那就是给后台的 URL 加上随机字符串,使得使用默认的后台地址无法访问
- 最最后,由于我们的前端流量由 Nginx 承载,所以 Nginx 也要加固一下, 下述共涉及 屏蔽 Nginx 的版本号减少攻击面、对 Http 请求方法进行限制、防止点击劫持、启用 HSTS 强制 https 访问、防止跨站攻击和摒弃不安全的 ssl cipher 且 server 端主动选择 cipher 共计 6 个方面。
基于某些考虑我们没有涉及的:
- 配置 fail2ban,自动封锁暴力破解者,这个有时候很容易在自己连续输入错几次密码后把自己给封了,即使你可以在手机上访问 vps 解封也比较繁琐,如果你按照本篇博客设置下来的话,配置 fail2ban 的意义也不是很大了。
- docker 的 ssl 挂载目录设置只读权限,因为我们采用了 ECC 算法的证书,而 Bitwarden 底层采用的 rocket tls 实现是不支持解析的,所以我们要移除掉相关设置,不用担心,本篇博客将会介绍如何用 Nginx 来实现解析 ECC 算法的证书
- 舍弃掉你的主域名来作为服务入口,而是配置一个含有随机字符串的二级域名,这也是一个很不错的加固方式,本文也不再赘述
上述所有的加固措施在 Bitwarden_rs/vaultwarden 的官方 wiki 中都有提到,大神没必要再付费看我下面的描述了,不过如果你对上述加固措施不太熟悉的话,亦或者自己踩了很多坑搞不定的话,还是墙裂建议看看下面的描述的。
Tips:下述命令和 Nginx 指令比较零星,对技术不太熟悉的朋友很容易弄混淆,本人贴心的把 docker 的完整启动命令和关键指令含有注释的 Nginx 的完整配置分别贴了出来。
[$]
1. 禁止注册和邀请用户
这个很简单,在 docker 启动命令中加入如下环境变量即可:
INVITATIONS_ALLOWED=false -e SIGNUPS_ALLOWED=false
2. 禁止显示密码提示
这个也很简单,加入如下环境变量:
-e SHOW_PASSWORD_HINT=false
3. 基于 Nginx 实现禁止 ip 访问
在你的 NGINX 的 http 块中加入如下配置:
if ($host !~* ^(www\.)?sharpgan.com$) {return 403;}
这一步还有另一种写法,感觉不如上面的直观,我就不写出来了。
注意:上面的域名你自己替换一下
4. 基于腾讯云控制台和 Nginx 讲解如何配置 ECC 算法的 ssl 证书
进入腾讯云的 SSL 控制台,依次按照如下操作:
重新签发后验证一下域名所属权之后等待签发就行了,然后我们把 Nginx 格式的证书下载下来配置一下 Nginx,如下:
ssl_certificate /data/Bitwarden/cert.crt;
ssl_certificate_key /data/Bitwarden/keys.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
ssl_session_cache builtin:1000 shared:SSL:10m;
client_max_body_size 128M;
注意:我们只要.key 和.crt 结尾的文件,上述是放到 /data/Bitwarden 下面,你可以自己指定一个位置
Tips:这里我们为了安全性考虑,ssl 协议移除了 1.0 和 1.1,直接 1.2 起步,即是 ssl_protocols TLSv1.2 TLSv1.3;
5. 让 docker 进程以非 root 用户运行
这个说来也简单,只是当初忽略了一个细节导致折腾了个把小时才搞定,只需要在 docker 的启动命令加入如下参数:
-u 1002:1002 -e ROCKET_PORT=8080
上面的 1002 代表了一个非 root 非 sudo 权限的普通用户的 id,你可以用命令 id + 空格 + 用户名来得到,具体细节不再赘述。
注意:上面的 -e ROCKET_PORT=8080 一定不能没有,我当初就是因为忽略了这个才折腾了好久。
6. 给后台的 URL 加上随机字符串
首先,docker 的启动命令加入如下环境变量:
-e DOMAIN='https://xxxxxx.sharpgan.com:23333/abcdefg'
上述 /abcdefg 就是我们的随机字符串示例,注意上述随机字符串后面不要有斜线 /,否者访问会报错。
然后我们的 Nginx 中加入如下配置:
upstream vaultwarden-default {server 127.0.0.1:8080;}
location /abcdefg/ {
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://vaultwarden-default;
}
7. 加固 Nginx
屏蔽 Nginx 的版本号,在 Nginx 的 http 块中加入如下指令:
server_tokens off;
对 Http 请求方法进行限制,在 Nginx 的 location 块中加入如下指令:
limit_except GET POST {deny all;}
防止点击劫持,在 Nginx 的 virtual server 块中加入如下指令:
add_header X-Frame-Options "SAMEORIGIN";
启用 HSTS 强制 https 访问,在 Nginx 的 virtual server 块中加入如下指令:
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
防止跨站攻击,在 Nginx 的 virtual server 块中加入如下指令:
add_header Content-Security-Policy "default-src'self'http: https: data: blob:'unsafe-inline'" always;
add_header X-XSS-Protection "1; mode=block";
摒弃不安全的 ssl cipher 即 tls 1.0 1.1,另外加入 server 端主动选择 cipher 的指令,不过前文已经提到过,如下:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
总结
由于上述命令和 Nginx 指令比较零星,对技术不太熟悉的朋友很容易弄混淆,所以下面分别把 docker 的完整启动命令和 Nginx 的完整配置贴出来。
docker 的完整启动命令如下:
docker run -d --name bitwarden -u 1002:1002 -e ROCKET_PORT=8080 -e INVITATIONS_ALLOWED=false -e SIGNUPS_ALLOWED=false -e SHOW_PASSWORD_HINT=false -e DOMAIN='https://xxxxx.sharpgan.com:23333/abcdefg' -e SMTP_HOST='smtp.qq.com' -e SMTP_FROM='[email protected]' -e SMTP_PORT=587 -e SMTP_SSL=false -e SMTP_USERNAME='[email protected]' -e SMTP_PASSWORD='xxxxxxxxxx' -v /data/bw-data/:/data/ -p 127.0.0.1:8080:8080 vaultwarden/server:latest
Nginx 的完整配置如下:
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {worker_connections 1024;}
http {
# 屏蔽 Nginx 版本号减少攻击面
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local]"$request"''$status $body_bytes_sent "$http_referer" ''"$http_user_agent""$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
client_body_buffer_size 1024k;
client_max_body_size 100m;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
upstream vaultwarden-default {server 127.0.0.1:8080;}
server {
listen 23333 ssl http2;
# 禁止 ip 访问,防止类似于 shedon 的邪恶搜索引擎搜索到,以及脚本小子扫描 ip 段
server_name xxxxxx.sharpgan.com;
if ($host != 'xxxxxx.sharpgan.com'){return 403;}
ssl_certificate /data/bw-ssl/cert.crt;
ssl_certificate_key /data/bw-ssl/keys.key;
ssl_session_timeout 5m;
# 摒弃不安全的 ssl cipher 且 server 端主动选择 cipher
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# 优先采用 ECC 算法而不是 RSA,注意下述 cipher 的顺序很重要,如果 RSA 相关的在前的话,浏览器可能识别不了 ECC 算法的证书
ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
ssl_session_cache builtin:1000 shared:SSL:10m;
client_max_body_size 128M;
# 防止点击劫持
add_header X-Frame-Options "SAMEORIGIN";
# 启用 HSTS 强制 https 访问
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
# 防止跨站攻击
add_header Content-Security-Policy "default-src'self'http: https: data: blob:'unsafe-inline'" always;
add_header X-XSS-Protection "1; mode=block";
## Using a Sub Path Config
# Path to the root of your installation
# Be sure to add the trailing /, else you could have issues
# 后台 url 采用随机字符串
location /abcdefg/ {limit_except GET POST { deny all;}
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://vaultwarden-default;
}
}
}
[/$]
最简单备份指南
其实就是新建一个 git 私有仓库,然后写一个每天打包并自动 push 到远程仓库的脚本,再配置成定时任务就行了,国内 vps 推荐使用的 gitee,国外的可以用 github,
注意!!!仓库一定要建成私有的哦 注意!!!
脚本如下:
cd /data && tar czvf bitwarden_backup_$(date '+%Y%m%d_%H%M').tgz bw-data/ && mv bitwarden_backup_$(date '+%Y%m%d_%H%M').tgz bitwarden_backup/ && cd bitwarden_backup/ && git add . && git commit -m 'update' && git push -u origin master
上述脚本的前提是你的 git 控制台添加了你本机的公钥,这里不再赘述。
还有一些前提是:你的 docker 数据卷挂载在了根目录的 /data 下面,叫 bw-data,以及在同级目录新建了一个叫 bitwarden_backup 的文件夹。
如果你满足了上述要求,就可以配置一个如下的定时任务了:
0 5 * * * bash /data/bitwarden_backup/backup.sh
效果如下:
为了减少对公共资源的浪费,建议改造一下上述脚本自动删除超过 30 天的备份。
SMTP 邮箱配置指南
你可能好奇配置这个有啥用,当你需要更换你当前的邮箱账户的时候就知道了,配置了这个后只需要输入邮箱中的验证码就可以把账户分分钟换成任意邮箱了
这个其实很简单,我们以 QQ 邮箱为例,其实就是 docker 的启动命令中加入几个环境变量,如下:
-e SMTP_HOST='smtp.qq.com' -e SMTP_FROM='[email protected]' -e SMTP_PORT=587 -e SMTP_SSL=false -e SMTP_USERNAME='[email protected]' -e SMTP_PASSWORD='xxxxxxxxx'
由于这些环境变量的名字都比较语义化,我就不一一介绍都是啥意思了,完整的命令在上文付费阅读部分有提到。