首页 / 资讯中心 / 文章详情

CentOS 7 自建 Bitwarden 密码服务器:Docker 部署与 Nginx 反向代理实战

CentOS 7 自建 Bitwarden 密码服务器:Docker 部署与 Nginx 反向代理实战 ★ FEATURED ARTICLE
密码管理这件事我踩过的坑比大多数人吃过的盐都多。早些年用记事本存密码后来换浏览器自带同步再后来试过各种在线密码本直到有一次帮朋友恢复一台被勒索软件加密的机器发现他所有密码都躺在一个明文Excel里——那一刻我彻底下定决心密码库必须自己掌控。Bitwarden 是我最终选定的方案原因很简单开源、客户端全平台覆盖、支持自托管而且社区版免费功能就足够个人和小团队用。这篇内容就是把我在一台 CentOS 7 机器上从零搭建 Bitwarden 密码服务器的完整过程拆开讲清楚包括为什么这么选、每一步在干什么、哪些地方容易翻车、以及跑起来之后怎么维护。适合有一定 Linux 基础、想给自己或小团队弄一个私有密码库的朋友参考纯小白也能跟着走但最好先补一下防火墙和 Docker 的基本概念。1. 为什么偏偏选自托管这条路1.1 在线密码本的三条隐性代价很多人觉得密码管理软件嘛装个 App 登录就能用何必折腾服务器。我一开始也这么想直到认真算了一笔账。第一笔是数据主权账你的主密码哈希、加密后的密码库、甚至使用习惯全都存在别人的机房里服务商改个隐私条款你连通知都收不到。第二笔是可用性账某次某在线密码服务大面积故障我整整半天登不进自己的账号偏偏那天要处理一个紧急的服务器迁移。第三笔是成本账家庭版按人头收费人一多费用就上去了而自托管用一台最低配的云主机就能扛住十几个人日常使用。自托管不是没有代价你得自己负责备份、升级、证书续期。但换个角度想这些工作加起来一个月也就花个把小时换来的是密码库文件实实在在躺在你自己的磁盘上随时能打包带走。这个交换在我看来非常划算。1.2 Bitwarden 服务端与客户端的分离设计理解 Bitwarden 的架构是后面所有操作的前提。它采用的是客户端加密、服务端只存密文的模式。你在客户端输入主密码主密码经过密钥派生函数默认 PBKDF2-SHA256迭代次数可调生成主密钥主密钥再派生出加密密钥和 MAC 密钥。真正上传到服务器的是用加密密钥处理过的密文服务器管理员即使把数据库整个导出来没有你的主密码也解不开。这个设计带来一个很关键的实际影响服务端的安全性要求其实没那么高因为它拿不到明文。但这不代表可以随便搭传输层必须上 HTTPS否则中间人可以在你登录时截获主密码。所以后面配置反向代理和证书这一步是整个搭建流程里最不能省的部分。1.3 社区版与官方版的取舍判断Bitwarden 官方提供云托管版也有面向企业的自托管方案但那个方案对个人来说太重了。社区里有一个广泛使用的开源实现功能覆盖了密码库同步、组织共享、附件、TOTP 等核心能力部署方式就是一套 Docker 编排文件。我选它的理由有三条一是部署门槛低一条安装脚本跑完基本就绪二是资源占用可控2 核 4G 的机器跑十几个人毫无压力三是升级路径清晰官方镜像定期更新跟着升就行。需要提醒的是社区版和企业版在审计日志、SSO 等高级功能上有差距个人和小团队完全够用但如果你的场景涉及合规审计就得提前评估清楚。2. 动手之前的机器与环境盘点2.1 CentOS 7 的系统现状与版本确认CentOS 7 是个老系统了内核 3.10默认的软件源里 Docker 版本偏旧glibc 也比较老。这不是坏事老系统稳定但有几个点必须提前确认。先看系统版本和内核cat /etc/centos-release uname -r输出应该是CentOS Linux release 7.x和3.10.0-xxx。接着确认 CPU 架构Bitwarden 的镜像只提供 x86_64 版本ARM 机器跑不了uname -m必须是x86_64。我见过有人拿树莓派折腾半天最后卡在镜像拉取失败白白浪费时间。另外确认内存free -h看一眼建议至少 2G低于这个数跑起来会频繁触发 OOM。2.2 关闭 SELinux 与防火墙的预处理这一步是很多教程一笔带过、但实际最容易卡住新手的地方。CentOS 7 默认开启 SELinuxDocker 挂载卷的时候会被拦表现为容器起来了但数据写不进去。临时关闭setenforce 0永久关闭要改配置文件sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/config改完重启生效。防火墙方面CentOS 7 默认用 firewalld我们后面会用 Nginx 做反向代理只需要放行 80 和 443firewall-cmd --permanent --add-servicehttp firewall-cmd --permanent --add-servicehttps firewall-cmd --reload提示如果你打算用云主机的安全组记得在控制台里也同步放行这两个端口两层防火墙任何一层没开都会导致外网访问不通。2.3 时间同步这件小事为什么不能忽略Bitwarden 的 TOTP 二次验证、JWT 令牌签发都依赖准确的时间。CentOS 7 默认的 chronyd 服务如果没跑起来时间漂移几分钟登录时就会报令牌无效而且报错信息很模糊排查起来极其痛苦。确认时间同步状态timedatectl status看System clock synchronized是否为 yes。如果不是启动服务systemctl enable chronyd systemctl start chronyd再核对一下时区timedatectl set-timezone Asia/Shanghai设成你所在时区。这个细节我吃过亏某次测试环境时间差了七分钟TOTP 死活验证不过查了两个小时才发现是时间问题。3. Docker 与编排文件的落地细节3.1 用官方脚本装 Docker 而不是 yum 源CentOS 7 自带的docker包版本太老直接yum install docker装出来的可能是 1.13跑新版编排文件会报语法不兼容。正确做法是用官方仓库脚本curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh装完启动并设开机自启systemctl enable docker systemctl start docker验证版本docker --version应该看到 20.x 以上。这里有个坑脚本会顺带装 docker-compose 的独立二进制但如果你之前用 pip 装过旧版 compose可能产生冲突建议先pip uninstall docker-compose清理干净。3.2 编排文件里每个环境变量的真实含义Bitwarden 的部署靠一个docker-compose.yml加一个.env配置文件。安装脚本会交互式地问你域名、数据库密码等信息然后生成这两个文件。我建议装完之后打开.env逐行看一遍几个关键项值得说清楚变量名作用我的建议值DOMAIN对外访问的完整域名必须带 https:// 前缀DATABASE数据库类型默认 sqlite小团队够用SIGNUPS_ALLOWED是否开放注册首次装完设 true注册完立刻改 falseADMIN_TOKEN管理后台令牌用长随机串别用弱口令SMTP_HOST邮件服务器不配的话邀请成员会失败SIGNUPS_ALLOWED这个开关特别重要。很多人装完忘了关结果公网上的扫描器发现你的实例后批量注册虽然他们进不了你的库但会污染用户表。我的习惯是装完第一件事就是注册自己的账号然后马上把这项改成 false 并重启容器。3.3 数据卷的持久化与备份路径规划容器是无状态的所有数据都挂在宿主机的卷里。默认安装脚本会把数据放在/opt/bitwarden下其中bwdata目录是核心数据库文件、附件、配置文件都在里面。我强烈建议在装之前就把这个目录规划到一个独立的磁盘分区或者挂载点上原因很简单密码库会随着使用慢慢变大附件尤其占空间如果跟系统盘混在一起哪天磁盘满了整个服务就挂了。备份策略我采用的是每日全量打包加异地同步。写个简单的脚本丢进 cron#!/bin/bash DATE$(date %Y%m%d) tar -czf /backup/bwdata-$DATE.tar.gz /opt/bitwarden/bwdata find /backup -name bwdata-*.tar.gz -mtime 7 -delete这个脚本每天打包一次保留最近七天。异地同步那一步我用的是对象存储的命令行工具把压缩包推到一个独立的存储桶里。注意备份文件本身也是加密数据的集合但里面的config.yml可能包含数据库密码所以备份存储的访问权限要收紧。4. 反向代理与 HTTPS 证书的配置实战4.1 为什么必须自己配 Nginx 而不是用内置方案安装脚本里其实有个选项可以自动申请证书但它依赖的组件在 CentOS 7 上经常因为 Python 版本问题跑不起来。更稳妥的做法是让 Bitwarden 只监听本地的 8080 端口前面用 Nginx 做反向代理并处理 TLS。这样做的好处是证书管理独立出来续期、换证书都不影响密码服务本身而且 Nginx 还能顺手做一些访问限速和请求头过滤。Nginx 的安装走 EPEL 源yum install epel-release -y yum install nginx -y systemctl enable nginx4.2 反向代理配置的关键参数逐行解读配置文件放在/etc/nginx/conf.d/bitwarden.conf核心内容如下server { listen 80; server_name your.domain.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name your.domain.com; ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem; client_max_body_size 128M; location / { proxy_pass http://127.0.0.1:8080; 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; } location /notifications/hub { proxy_pass http://127.0.0.1:3012; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } }逐条说client_max_body_size控制上传附件的大小上限默认 1M 太小密码库传个密钥文件就超了。X-Forwarded-Proto告诉后端真实协议是 https否则 Bitwarden 会以为自己在 http 下运行生成的链接全是 http 的。最后那个/notifications/hub是 WebSocket 通道负责实时推送密码库变更少了这段配置多设备同步会有延迟。4.3 证书申请与自动续期的完整链路用 certbot 申请 Lets Encrypt 证书yum install certbot python2-certbot-nginx -y certbot --nginx -d your.domain.comCentOS 7 的 certbot 是 Python2 版本虽然老但能用。申请成功后 certbot 会自动往 Nginx 配置里插证书路径如果你像我一样手写了配置就选手动模式只拿证书。续期靠系统自带的定时任务systemctl status certbot-renew.timer确认一下状态或者手动加一条 cron0 3 * * * /usr/bin/certbot renew --quiet systemctl reload nginx注意续期后必须 reload Nginx否则 Nginx 还在用内存里的旧证书。我见过有人证书续期成功了但服务还是报过期就是漏了这一步。5. 首次启动后的验证与账号初始化5.1 容器状态检查与日志排错思路启动服务cd /opt/bitwarden ./bitwarden.sh start这条命令会拉起一组容器包括主应用、数据库、WebSocket 服务等。用docker ps看状态正常情况下应该看到几个容器都是 Up。如果某个容器反复重启用docker logs 容器名看日志。最常见的两个错误一是数据库初始化失败通常是.env里的数据库密码包含特殊字符没转义二是端口冲突8080 被别的服务占了netstat -tlnp | grep 8080一查便知。5.2 注册第一个账号与关闭开放注册的时机浏览器访问你的域名应该能看到登录页。点创建账号填邮箱和主密码。这里有个极其重要的提醒主密码一旦设定服务端没有任何找回手段因为服务端根本不知道你的主密码。丢了就是丢了只能靠你提前导出的恢复代码或者备份。所以注册完第一件事去设置里生成并打印一份恢复代码锁进抽屉。注册成功后立刻回到.env把SIGNUPS_ALLOWED改成 false然后./bitwarden.sh restart之后要加人走管理后台的邀请流程而不是开放注册。5.3 客户端连接与同步的实测验证在手机和电脑上装 Bitwarden 客户端登录时把服务器地址改成你的域名。这里有个细节客户端填地址时不要带末尾斜杠有些版本对末尾斜杠处理有 bug会拼出双斜杠导致 404。登录成功后建一条测试密码在另一台设备上刷新看是否秒同步。如果同步有延迟回头检查 WebSocket 那段 Nginx 配置。6. 跑起来之后才暴露的几个真问题6.1 内存不足导致的容器被系统杀掉2G 内存的机器跑一段时间后可能会发现某个容器莫名其妙消失了。查dmesg | grep -i oom能看到 OOM killer 的记录。Bitwarden 的几个容器加起来峰值能到 1.5G 左右系统本身还要占一部分。解决办法有两个加内存或者加 swap。加 swap 的操作fallocate -l 2G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo /swapfile none swap sw 0 0 /etc/fstabswap 不是万能药但能避免容器被直接杀掉给运维留出反应时间。6.2 升级过程中的数据兼容性风险Bitwarden 的镜像更新比较频繁升级命令是./bitwarden.sh updateself然后./bitwarden.sh update。升级前必须备份这不是客套话。我遇到过一次跨版本升级后数据库迁移脚本执行到一半失败容器起不来最后靠回滚备份才恢复。升级的正确姿势是先打包bwdata再执行更新更新完立刻验证登录和同步确认无误再删旧备份。6.3 邮件通知缺失带来的连锁反应不配 SMTP 的话邀请成员、密码提示、两步验证邮件全都发不出去。表面看只是少了个通知实际上新成员根本没法完成注册流程。配 SMTP 时注意端口和加密方式要匹配465 端口对应 SSL587 对应 STARTTLS填错了连接会超时。测试方法很简单在管理后台点发送测试邮件看收件箱有没有收到。7. 日常维护里我固定做的几件事7.1 每周一次的备份恢复演练备份不做恢复演练等于没备份。我每周会挑一个备份包解压到临时目录用sqlite3打开数据库文件确认能正常读取表结构。这一步花不了五分钟但能确保真出事的时候备份是好的。曾经有一次磁盘写入异常导致备份包损坏就是靠这个习惯提前发现的。7.2 监控告警的最小化配置不需要上复杂的监控系统一个简单的 curl 脚本就够#!/bin/bash STATUS$(curl -o /dev/null -s -w %{http_code} https://your.domain.com) if [ $STATUS ! 200 ]; then echo Bitwarden down: $STATUS | mail -s Alert youexample.com fi丢进 cron 每五分钟跑一次。密码服务挂了是大事早发现早处理。7.3 主密码策略与团队使用规范自托管不等于可以放松警惕。我给团队定的规矩是主密码至少 16 位必须包含大小写和符号且不能与其他任何账号复用。开启两步验证优先用 TOTP 而不是邮件验证。新成员加入时第一件事是教他们导出恢复代码并离线保存。这些规范听起来琐碎但密码库是整个团队的数字命门规范比技术更重要。整套流程走下来从裸机到可用状态大概两三个小时其中大部分时间花在等证书签发和调试 Nginx 配置上。真正跑稳之后日常维护的负担很轻。我个人在实际操作中的体会是自托管密码服务最大的价值不在于省了多少钱而在于你对数据的掌控感——知道它在哪里、谁能碰、怎么备份这种确定性是任何在线服务都给不了的。
阅读完成 · 觉得有帮助?
咨询建站