趁现在自己还记忆犹新,赶紧记录下搭建Vaultwarden的过程。

安全加固措施

  • MySQL替换默认SqLite数据库,并配置异地主从数据库备份
  • 在Nginx配置location / {return 403;}屏蔽WEB访问,只保留必要配置
  • 启用OpenVpn+SSL自签证书

搭建开源Vaultwarden

通过Docker容器安装

docker run -d \
--rm \
--name vaultwarden \
-p 8787:80 \
-p 3012:3012 \
-v /home/docker/vaultwarden:/data \
-e DATABASE_URL=mysql://user:password@127.0.0.1:8888/database \
-e SIGNUPS_ALLOWED=false \
-e WEBSOCKET_ENABLED=true \
-e INVITATIONS_ALLOWED=false \
-e ADMIN_TOKEN=your_admin_token \
-e DOMAIN=https://your_url.com \
vaultwarden/server:latest

80 web访问端口

3012 webscoket端口

/home/docker/vaultwarden建立目录用于容器外映射配置文件

  • -d 后台运行
  • --rm 当容器停止时自动删除容器
  • -e DATABASE_URL 配置数据源,默认SqLite(可选)
  • -e SIGNUPS_ALLOWED 允许用户注册(首次用户注册后,删除容器,指定SIGNUPS_ALLOWED=false重新配置容器)
  • -e WEBSOCKET_ENABLED 开启webscoket
  • -e INVITATIONS_ALLOWED 允许邀请用户注册
  • -e ADMIN_TOKEN 管理员令牌
  • -e DOMAIN 配置访问域名(需要是https://xxx.com域名,不能配置ip地址),

切换MySQL数据库

在docker配置容器命令添加:

示例配置:DATABASE_URL=mysql://[[user]:[password]@]host[:port][/database]

# 上述docker命令已包括
-e DATABASE_URL=mysql://vaultwarden_user:strong_password@11.1.0.9:8888/vaultwarden

配置私有IP自签名证书

注意:低版本的openssl执行会失败

11.1.0.1是我自签名SSL证书的的IP地址,执行命令得到:ca.crt证书、 ca.key密钥

openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
-keyout ca.key -out ca.crt -subj "/CN=11.1.0.1" \
-addext "subjectAltName=IP:11.1.0.1"

由于是使用的自签名证书,需要导入浏览器、安卓手机才能正常访问

配置openVPN

openvpn加固:

  • ca.crt 根证书
  • x.crt 签发证书
  • x.key 签发证书密钥
  • 用户名+密码

openvpn导入连接

在移动端导入openvpn配置,需要将.ovpn文件和依赖的根证书ca.crt、自签证书.crt.key一并选择进行导入


附录

安卓APP相关软件

安卓手机app软件(谷歌商店)

  • OpenVpn Connect (3.7.1) :开启OpenVPN连接
  • Bitwarden (2025.06.1):密码管理软件
  • Bitwarden Authenticator (2025.6.0) 密码管理软件-两步验证工具

手机导入自签名证书

小米14的证书安装路径如下:

设置->隐私与安全->安全->更多安全设置->更多安全设置->加密与凭据->安装证书->CA证书

选择自签名IPca.crt证书安装到手机即可。

Nginx配置

server {
listen 8443 ssl;
# 监听ovpn地址
server_name 11.1.0.1;
# 自签名证书
ssl_certificate /usr/local/nginx/conf/cert/vpn/11.1.0.1.crt;
ssl_certificate_key /usr/local/nginx/conf/cert/vpn/11.1.0.1.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

# 仅允许openvpn下IP地址访问
allow 11.1.0.0/24;
deny all;


# 屏蔽web端访问
location /{
return 403;
}

# 允许 API 访问(确保客户端仍能同步)
location /api/ {
proxy_pass http://127.0.0.1:8787;
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;
}

# 登录接口(web、pc、浏览器插件、app依赖)
location /identity/ {
proxy_pass http://127.0.0.1:8787;
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;
}

# 允许 WebSocket(用于实时同步)
location /notifications/hub {
proxy_pass http://127.0.0.1:3012/notifications/hub;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

# 其他
location /notifications/hub/negotiate {
proxy_pass http://127.0.0.1:8787;
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;
}

启用MTLS双向认证

配置双向认证后,在公网环境也可以

创建CA根证书(证书颁发机构)

# 创建CA私钥
openssl genrsa -out ca.key 2048

# 创建CA根证书(有效期10年)
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
# 需要填写国家、组织等信息

创建客户端证书

# 创建客户端私钥
openssl genrsa -out client.key 2048

# 创建证书签名请求(CSR)
openssl req -new -key client.key -out client.csr
# 需要填写与CA证书类似的信息

# 使用CA签署客户端证书(有效期1年)
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt

# 将客户端证书转换为PKCS12格式(方便浏览器导入)
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
# 需要设置导出密码

nginx配置


server {
listen 9003 ssl;
server_name 11.1.0.9;

# 标准SSL配置
ssl_certificate /usr/local/nginx/conf/cert/11.1.0.9/ca.crt;
ssl_certificate_key /usr/local/nginx/conf/cert/11.1.0.9/ca.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
#ssl_ciphers HIGH:!aNULL:!MD5;

# 启用客户端证书验证
ssl_client_certificate /usr/local/nginx/conf/cert/CA/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
# 调整加密套件
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305';

location /{
root /usr/local/nginx/html/snowyweb/dist;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
}