Alain's Blog

  1. 首页
  2. 软件工具
  3. 正文

Docker部署Vaultwarden,并使用Nginx反向代理实现Https

2023年1月8日 7805点热度 1人点赞 3条评论

Docker Vaultwarden

转载请注明出处,本文仅用于学习交流,不对之处,恳请指正 ,部分图片摘取网络,如有侵权请联系

Vaultwarden是一个搭建Bitwarden服务器的第三方项目,其客户端均使用Bitwarden官方客户端(浏览器插件,手机应用等)。

1. 前言

Vaultwarden Docker Hub链接: https://hub.docker.com/r/vaultwarden/server
Vaultwarden Github仓库链接: https://github.com/dani-garcia/vaultwarden
Bitwarden官网链接: https://bitwarden.com/
请注意,不要使用任何Bitwarden官方渠道反馈关于Vaultwarden的问题,他们之间并没有隶属关系!

网上有很多关于Vaultwarden部署的文章,以及相关的官方wiki的翻译,为什么我还会写这篇文章,主要原因还是我更多的将我的博客当成一种公开笔记进行分享的方式。Docker部署的方式同样适用于拥有Docker程序的NAS,比如群晖等。

2. 部署

2.1 部署数据库

Vaultwarden默认使用SQLite,但是由于我本地运行着Postgresql数据库,不利用起来感觉有点浪费。当然你也可以自由选择MySQL或者其他数据库,这里就不展开讨论了。

2.1.1 创建PostgreSQL容器

docker run -d \
  --name postgres \
  --restart always \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e TZ=Asia/Shanghai \
  -v postgresql:/var/lib/postgresql \
  -v postgresql_data:/var/lib/postgresql/data \
  postgres:14.5

需要自行修改字段

  1. POSTGRES_USER : 数据库用户
  2. POSTGRES_PASSWORD : 数据库密码

2.1.2 新增Vaultwarden数据库

支持现有的Postgresql容器实例

  1. 进入容器

    docker exec -it postgres bash
  2. 切换系统中运行数据库的用户

    su - postgres
  3. 登陆数据库

    # 注意![POSTGRES_USER]为前面创建的用户名
    psql -U [POSTGRES_USER] -W
    # 输入密码
  4. 新增用户

    请根据实际情况修改用户名以及密码

    CREATE USER vaultwarden WITH PASSWORD 'vaultwarden';
  5. 新增数据库

    请根据实际情况修改数据库名

    CREATE DATABASE vaultwarden OWNER vaultwarden;
  6. 赋予权限

    GRANT ALL PRIVILEGES ON DATABASE vaultwarden to vaultwarden;
  7. 退出数据库

    # 退出数据库
    \q
    # 退出数据库用户
    # ctrl+d
    # 退出容器
    # ctrl+d

2.2 部署Vaultwarden容器

docker run -d --name vaultwarden \
  --link postgres:postgres \
  -e TZ=Asia/Shanghai \
  -e DOMAIN=https://alainlam.cn \
  -e ADMIN_TOKEN='please generate via: openssl rand -base64 48' \
  -e ROCKET_PORT=12304 \
  -e WEBSOCKET_PORT=12305 \
  -e WEBSOCKET_ENABLED=true \
  -e DATABASE_URL='postgresql://vaultwarden:vaultwarden@postgres:5432/vaultwarden' \
  -v vaultwarden_data:/data/ \
  -p 12304:12304 \
  -p 12305:12305 \
  vaultwarden/server:latest

!!注意!!, 第一次运行运行使用容器内的root账号运行,因为创建公钥私钥需要权限

参数说明

  1. -e TZ=Asia/Shanghai : 设置时区为上海
  2. --link postgres:postgres : 连接到现有的postgres数据库,注意其中postgres为容器名,需要根据实际使用情况进行调整
  3. -e DOMAIN=https://alainlam.cn :启用 U2F 和 FIDO2 WebAuthn 身份验证需要将 DOMAIN 环境变量设置为与访问 Vaultwarden 相同的地址
  4. -e ADMIN_TOKEN : 启用管理页面,推荐运行openssl rand -base64 48来生成你的token
  5. -e ROCKET_PORT=12304 : Rocket的运行端口,因为后续我们将使用非Root账号应用,所以我们将Rocket端口修改为非特权端口
  6. -e WEBSOCKET_ENABLED=12305 : Websocket的运行端口,默认为3012,实际上你可以不修改它,因为它并非一个特权端口,我只是为了看起来好看点...
  7. -e DATABASE_URL : 连接数据库,注意,密码如果带有特殊字符,如!@#$%^等,需要进行百分号转义,参考使用postgresql数据库
  8. WEBSOCKET_ENABLED : 开启Websocket功能
  9. -v vaultwarden:/data/ : 挂载的持久化数据虚拟盘
  10. -p : 端口映射

2.3 安全优化

  1. vaultwarden的数据虚拟硬盘是不可以删除的
  2. 因为我们做了数据持久化处理,以及我们的相关密码数据都是保存到我们的数据库之中,所以我们可以直接删除原来的容器进行重建
  3. 我们需要保持容器是一个随时可以删除重建的好习惯
  1. 停止正在运行的容器

    docker stop vaultwarden
  2. 删除容器

    docker rm vaultwarden
  3. 新建容器

    docker run -d --name vaultwarden \
      --link postgres:postgres \
      -u 1000:1000 \
      -e TZ=Asia/Shanghai \
      -e DOMAIN=https://alainlam.cn \
      -e ROCKET_PORT=12304 \
      -e WEBSOCKET_PORT=12305 \
      -e DATABASE_URL='postgresql://vaultwarden:vaultwarden@postgres:5432/vaultwarden' \
      -e WEBSOCKET_ENABLED=true \
      -e SIGNUPS_ALLOWED=false \
      -e INVITATIONS_ALLOWED=false \
      -e SHOW_PASSWORD_HINT=false \
      -v vaultwarden_data:/data/ \
      -p 12304:12304 \
      -p 12305:12305 \
      -e SMTP_HOST=SMTP服务器地址 \
      -e SMTP_FROM=你的邮箱 \
      -e SMTP_PORT=SMTP端口 \
      -e SMTP_SECURITY=force_tls \
      -e SMTP_USERNAME=你的邮箱 \
      -e SMTP_PASSWORD=你的密码 \
      vaultwarden/server:latest

    参数说明:

    1. -u 1000:1000 :使用非root用户运行,即使使用的是非特权容器
    2. -e SIGNUPS_ALLOWED=false : 关闭注册功能,个人使用推荐关闭
    3. INVITATIONS_ALLOWED=false : 关闭邀请功能,个人使用推荐关闭
    4. SHOW_PASSWORD_HINT : 禁用密码提示

3. 使用Nginx 反向代理

Vaultwarden要求使用HTTPS访问,推荐使用Nginx对服务进行反向代理,当然你也可以使用Rocket的默认方式,但是并不推荐该做法。
以下反向代理使用子路径来进一步提高安全性,其中你需要修改your-sub-path,修改后需要使用: https://your_domain/your-sub-path 来访问,但你仍然需要使用强密码,两步认证等安全措施

server {
    # 监听80端口
    # 监听IPv4
    listen 80;
    # 监听IPv6
    # listen [::]:80;

    # 监听443端口
    # 监听IPv4
    listen 443 ssl http2;
    # 监听IPv6
    # listen [::]:443 ssl http2;

    # 强制HTTPS
    if ($server_port !~ 443) {
        rewrite ^(/.*)$ https://$host$1 permanent;
    }

    # SSL证书相关
    ssl_certificate /etc/ssl/certificates/alainlam.cn.pem;
    ssl_certificate_key /etc/ssl/certificates/alainlam.cn.key;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 监听的域名
    server_name alainlam.cn;

    # 开启排除IP功能
    real_ip_recursive on;
    set_real_ip_from 127.0.0.1;
    set_real_ip_from 192.168.1.0/24;
    set_real_ip_from 10.0.0.0/8;
    # set_real_ip_from Frp 服务器IP地址;
    # set_real_ip_from CDN 服务器IP地址

    proxy_headers_hash_max_size 512;
    proxy_headers_hash_bucket_size 128;

    # 直接访问则重定向,注意如果你不希望使用子路径隐藏的方式,记得注释以下代码!
    location / {
        return 301 https://www.alainlam.cn;
    }
    # 注意如果你不希望使用子路径隐藏的方式,记得注释以上代码!

    # 反向代理
    location ^~ /your-sub-path {
        # Pass to server
        proxy_pass http://127.0.0.1:12345/;

        # Host
        proxy_set_header Host $host;

        # XF
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $remote_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        ## same as XXF
        proxy_set_header Forwarded $remote_addr;

        # Original URI
        proxy_set_header X-Original-URI $request_uri;

        # ReadIP
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Real-Port $remote_port;

        # Websocket
        # proxy_http_version 1.1;
        # proxy_set_header Upgrade $http_upgrade;
        # proxy_set_header Connection "upgrade";

        # Cache
        add_header X-Cache $upstream_cache_status;
        add_header X-Cache-Status $upstream_cache_status;
    }

    location ^~ /your-sub-path/notifications/hub/negotiate {

        # Pass to server
        proxy_pass http://127.0.0.1:12345/notifications/hub/negotiate;

        # Host
        proxy_set_header Host $host;

        # XF
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $remote_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        ## same as XXF
        proxy_set_header Forwarded $remote_addr;

        # Original URI
        proxy_set_header X-Original-URI $request_uri;

        # ReadIP
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Real-Port $remote_port;

        # Websocket
        # proxy_http_version 1.1;
        # proxy_set_header Upgrade $http_upgrade;
        # proxy_set_header Connection "upgrade";

        # Cache
        add_header X-Cache $upstream_cache_status;
        add_header X-Cache-Status $upstream_cache_status;
    }

    location ^~ /your-sub-path/notifications/hub {

        # Pass to server
        proxy_pass http://127.0.0.1:12345/notifications/hub;

        # Host
        proxy_set_header Host $host;

        # XF
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $remote_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        ## same as XXF
        proxy_set_header Forwarded $remote_addr;

        # Original URI
        proxy_set_header X-Original-URI $request_uri;

        # ReadIP
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Real-Port $remote_port;

        # Websocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        proxy_send_timeout 12s;

        # Cache
        add_header X-Cache $upstream_cache_status;
        add_header X-Cache-Status $upstream_cache_status;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|json|bmp|swf|svg|woff|woff2|ttf|map)$ {
        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 REMOTE-HOST $remote_addr;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Forwarded-Proto https;

        add_header X-Cache $upstream_cache_status;
        expires 12h;
    }
}

需要修改的参数

  1. ssl_certificate : SSL证书路径
  2. ssl_certificate_key : SSL证书路径
  3. server_name : 跟你前面配置的domain相同
  4. set_real_ip_from : 过滤IP地址,比如代理服务器的地址,以获取真实IP
  5. proxy_pass : 运行Vaultwarden的服务器地址,比如本机为127.0.0.1
  6. (可选但推荐) your-sub-path: 将Vaultwarden隐藏在子路径下,可以一定程度提高安全性,但你仍然需要使用强密码,两步认证等安全措施

4. 效果

Vaultwarden效果图

5. 插件或程序下载

我们直接使用Bitwarden的应用程序或插件即可

Bitwarden - Download

  1. Chrome Web Store - Bitwarden - Free Password Manager

  2. Google Play - Bitwarden Password Manager

  3. Apple Store - Bitwarden Password Manager


6. 扩展阅读

6.1 使用Websocket通知

参考 https://rs.ppgg.in/configuration/enabling-websocket-notifications

  1. 将 /notifications/hub 端点路由到 WebSocket 服务器,默认在 3012 端口,确保传递 Connection 和 Upgrade 头。(提示:可以使用 WEBSOCKET_PORT 变量来更改端口)

  2. 将所有其他(包括 /notifications/hub/negotiate)路由到标准 Rocket 服务器,默认在 80 端口上。

6.2 使用postgresql数据库

参考 https://rs.ppgg.in/configuration/database/using-the-postgresql-backend
注意特殊字符需要使用百分号编码

语法:

DATABASE_URL=postgresql://[[user]:[password]@]host[:port][/database]

特殊字符编码表:

! # $ % & ' ( ) * +
21% 23% 24% 25% 26% 27% 28% 29% %2A %2B
, / : ; = ? @ [ ]
%2C %2F %3A %3B %3D %3F %40 %5B %5D

示例:

1!2@3#ABC

转换后为

121%240%323%ABC

6.3 配置SMTP

参考 https://rs.ppgg.in/configuration/smtp-configuration

我使用的是Gmail作为SMTP中继服务,请注意,Gmail最好是使用应用专用密码登录来进行配置,生成应用的专属密码需要启用两部认证。

-e SMTP_HOST=smtp.gmail.com
-e SMTP_FROM=<mail-address>
-e SMTP_PORT=465
-e SMTP_SECURITY=force_tls
-e SMTP_USERNAME=<mail-address>
-e SMTP_PASSWORD=<less-secure-app-password>

参考

Vaultwarden Wiki 中文版

标签: Docker NAS Nginx Vaultwarden
最后更新:2024年8月16日

Alain

看了我的文,就是我的人,点个赞再走成不成

点赞
< 上一篇
下一篇 >

文章评论

  • sld

    博主你好,我试着按照你的方法进行了部署,但是部署完了以后非但无法实现强制https而且一旦以http访问就会下载下来一个1kb的名为“下载”的二进制文件,更重要的是我服务器上的所有网站访问都变成这样了,请问该怎么办啊 :lol: ,我使用的是ubuntu18.04.6,网站是用宝塔配置的

    2023年3月24日
    回复
    • Alain

      @sld 您好,不好意思,现在才看到,我看了下你的描述,应该是nginx配置配置问题,所以我重新看了下配置文件,listen 80后面的http2删除应该就好了,你看一下是否可行

      2023年3月25日
      回复
      • sld

        @Alain 成功部署了,非常感谢

        2023年3月25日
        回复
  • 取消回复

    文章目录
    • 1. 前言
    • 2. 部署
      • 2.1 部署数据库
      • 2.2 部署Vaultwarden容器
      • 2.3 安全优化
    • 3. 使用Nginx 反向代理
    • 4. 效果
    • 5. 插件或程序下载
    • 6. 扩展阅读
      • 6.1 使用Websocket通知
      • 6.2 使用postgresql数据库
      • 6.3 配置SMTP
    • 参考

    COPYRIGHT © 2022 Alain's Blog. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang