1. 为什么要在自己的服务器上搭一套密码管理服务
密码这件事,很多人嘴上说重视,实际做法却是在浏览器里点“记住密码”,或者干脆所有平台共用一个。等到某个小网站被拖库,撞库攻击一上来,邮箱、社交、支付全线告急。商业密码管理服务确实省事,但把全部身家性命托付给第三方,总有人心里不踏实——尤其是那些手里管着服务器、数据库、云账号的运维和开发者,密码一旦泄露,损失不是换个账号那么简单。
自己搭一套密码管理服务,核心诉求就三个:数据完全掌握在自己手里、多端同步不折腾、加密强度经得起推敲。Bitwarden 正好踩在这三个点上。它是开源项目,客户端覆盖浏览器插件、桌面端、手机端,服务端可以自托管,加密模型是端到端设计——主密码不出本地,服务器上存的都是密文。换句话说,就算服务器被拖走,攻击者拿到的也只是一堆解不开的乱码。
这篇内容面向的是手里有一台闲置服务器、懂一点 Linux 基础操作、想把密码管理这件事彻底收归自有的人。CentOS 7 虽然已经进入维护尾声,但大量存量服务器还在跑它,所以这套搭建流程依然有现实意义。我会从方案选型讲到具体落地,把每一步为什么这么做、参数怎么定、坑在哪里都掰开说清楚。整套流程走下来,一台 2 核 4G 的入门级云主机就够用,成本可控,维护量也不大。
需要提前说明的是,下面涉及的具体 IP、域名、密码全部是示例占位,你实际操作时替换成自己的即可。整套方案基于常见的自托管实践补全,Bitwarden 官方也提供了对应的部署脚本和文档,思路是一致的。
2. 方案选型与整体架构拆解
2.1 为什么选 Bitwarden 自托管而不是别的方案
市面上的密码管理方案大致分三类:纯本地离线工具、商业云服务、开源自托管。纯本地工具的问题是同步麻烦,换台设备就得手动导来导去;商业云服务省心但数据不在自己手里;开源自托管是折中路线,兼顾掌控力和便利性。
Bitwarden 在开源自托管这一档里优势明显。第一,客户端生态完整,Chrome、Firefox、Edge 插件齐全,iOS 和 Android 都有官方 App,桌面端也有,日常使用体验和商业产品差距不大。第二,加密模型清晰,主密码经过 PBKDF2 或 Argon2 派生后只在本地参与加解密,服务器端拿到的是已经加密的保险库数据。第三,社区活跃,部署脚本成熟,出问题容易找到参考。
对比其他自托管方案,有的项目只提供命令行客户端,有的同步机制依赖第三方存储,有的加密实现不够透明。Bitwarden 的官方安装脚本把 Docker、数据库、反向代理这些组件串起来了,对有一定 Linux 基础的人来说上手门槛不算高。
2.2 整体架构长什么样
整套服务的骨架可以拆成四层。最底层是操作系统,CentOS 7 提供基础运行环境。往上是容器运行时,Docker 负责把 Bitwarden 的各个服务组件隔离运行。再往上是应用层,包括 Web 保险库、API 接口、身份认证、数据库等。最外层是入口层,Nginx 做反向代理,负责把外部 HTTPS 请求转发到内部服务,同时处理证书和域名。
数据流向是这样的:用户在浏览器或手机 App 里输入主密码,客户端本地完成密钥派生和加解密,只把密文通过 HTTPS 发给服务器;服务器收到密文后存进数据库,返回时也是密文;客户端拿到密文再本地解密展示。整个过程中,明文密码和主密码都不会离开用户设备。这个设计是自托管方案安全性的根基,理解这一点,后面配置 HTTPS 和数据库时就知道哪些环节不能马虎。
2.3 硬件与系统的最低要求
Bitwarden 官方对自托管的最低配置建议是 2 核 CPU、4GB 内存、至少 20GB 磁盘。实测下来,个人或小团队使用(十人以内),这个配置跑起来比较从容。如果只有 1 核 2G,安装过程可能因为内存不足导致容器启动失败,尤其是数据库初始化阶段比较吃内存。
CentOS 7 方面,建议用最小化安装,减少不必要的服务暴露面。系统盘 20G 起步,因为 Docker 镜像和数据库会占空间,后续备份也需要余量。网络方面,需要一个公网 IP 和一个能解析到该 IP 的域名,HTTPS 证书申请依赖域名验证,没有域名的话只能用自签证书,客户端信任会比较麻烦。
提示:CentOS 7 的默认内核版本较老,Docker 安装时要注意选择兼容的版本,后面会具体说。
3. 系统准备与基础环境配置
3.1 系统更新与基础工具安装
拿到一台干净的 CentOS 7 之后,第一件事是更新系统并装好常用工具。这一步看似简单,但顺序有讲究——先更新再装软件,避免依赖版本冲突。
yum update -y yum install -y yum-utils device-mapper-persistent-data lvm2 git curl wget vimyum-utils提供yum-config-manager,后面添加 Docker 仓库要用;device-mapper-persistent-data和lvm2是 Docker 存储驱动的基础依赖,缺了它们 Docker 跑不起来。这几个包看着不起眼,但漏装会导致后续步骤卡住,我见过有人直接跳过更新去装 Docker,结果因为旧版依赖冲突折腾半天。
更新完成后建议重启一次,让内核和库文件生效。重启后用uname -r确认内核版本,CentOS 7 一般是 3.10 系列,够用。
3.2 关闭 SELinux 与防火墙的取舍
CentOS 7 默认开启 SELinux 和 firewalld,这两个东西在正式生产环境里是安全加固手段,但在搭建自托管服务的初期,它们经常成为“服务起不来”的元凶。SELinux 会限制容器访问宿主机文件,firewalld 会拦截 Docker 映射的端口。
我的做法是:搭建阶段先把 SELinux 设为 permissive 模式,firewalld 暂时停用,等服务跑通、端口确认无误后,再按需收紧。这样能把“环境问题”和“配置问题”分开排查,效率高很多。
setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config systemctl stop firewalld systemctl disable firewalldsetenforce 0是临时生效,改配置文件是永久生效,两条都要做。防火墙方面,如果这台机器只跑 Bitwarden,后续用云厂商的安全组来控制入站端口更省事,宿主机层面的 firewalld 可以保持关闭。但如果你对安全要求高,也可以在服务跑通后重新启用 firewalld,只放行 80、443 和 SSH 端口。
注意:SELinux 设为 permissive 只是记录违规不拦截,不是彻底关闭。如果后续遇到权限相关的诡异问题,可以临时用
setenforce 0对比测试,确认是不是 SELinux 在作祟。
3.3 时间同步与主机名设置
时间同步这件事容易被忽略,但密码服务对时间敏感——TLS 证书验证、令牌有效期都依赖准确的时间。CentOS 7 自带 chronyd,确认它正常运行即可。
systemctl enable chronyd systemctl start chronyd timedatectl set-timezone Asia/Shanghai chronyc sources -v时区设成自己所在区域,chronyc sources能看到同步源状态就说明正常。主机名方面,设一个好记的名字,后续证书和配置里会用到。
hostnamectl set-hostname vault主机名不影响功能,但日志和监控里看着清楚,排查问题时省心。
4. Docker 环境搭建与镜像加速
4.1 安装 Docker CE 的正确姿势
CentOS 7 自带的docker包版本太老,必须用 Docker 官方仓库装 CE 版本。添加仓库时要注意,CentOS 7 已经停止维护,部分镜像源可能失效,建议用国内可访问的镜像源。
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io装完后启动并设为开机自启:
systemctl enable docker systemctl start docker docker versiondocker version能同时看到 Client 和 Server 信息,说明 Docker 守护进程正常。如果只显示 Client 部分,说明服务没起来,用systemctl status docker看日志。
4.2 配置镜像加速与日志限制
拉取 Bitwarden 镜像时,默认从 Docker Hub 拉,速度可能很慢甚至超时。配置一个镜像加速地址能显著改善体验。编辑/etc/docker/daemon.json:
{ "registry-mirrors": ["https://your-mirror.example.com"], "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }registry-mirrors填你可用的加速地址,这里用示例占位。日志限制这两项很关键——Bitwarden 的容器日志如果不限制,几个月就能把磁盘写满,我遇到过磁盘被日志撑爆导致数据库写入失败的情况。max-size设 50MB、保留 3 个文件,单个容器日志上限 150MB,够用且安全。
改完配置重启 Docker:
systemctl daemon-reload systemctl restart docker4.3 验证 Docker 网络与存储
Bitwarden 安装脚本会创建自定义网络,容器之间通过服务名通信。确认 Docker 的默认网桥正常:
docker network ls docker info | grep -i storage存储驱动一般是 overlay2,CentOS 7 内核 3.10 支持。如果显示的是 devicemapper,性能会差一些,建议检查是否漏装了lvm2相关包。网络方面,默认的 bridge 网络存在即可,安装脚本会自己建新网络。
5. Bitwarden 服务端部署实操
5.1 获取安装脚本与目录规划
Bitwarden 官方提供了一个bitwarden.sh脚本,负责下载配置、生成密钥、启动容器。先建一个专用目录,把脚本放进去:
mkdir -p /opt/bitwarden cd /opt/bitwarden curl -Lso bitwarden.sh https://raw.githubusercontent.com/bitwarden/server/master/scripts/bitwarden.sh chmod +x bitwarden.sh目录选/opt是惯例,数据、配置、备份都集中在这里,后续迁移或备份时打包整个目录就行。脚本下载地址如果访问不畅,可以换用镜像源或者手动下载后上传,思路一样。
5.2 安装命令与交互式配置详解
执行安装命令:
./bitwarden.sh install脚本会依次询问几个关键参数,每一个都影响后续运行,逐个说清楚:
- Enter the domain name:填你的域名,比如
vault.example.com。这个域名必须已经解析到服务器公网 IP,后面申请证书要用。填错的话,HTTPS 会失败,客户端连不上。 - Do you want to use a self-signed certificate?:没有正式证书就选
yes,但客户端会提示证书不受信任。建议选no,让脚本用 Let's Encrypt 自动申请,前提是域名解析正确且 80 端口可达。 - Enter the email address:填一个真实可用的邮箱,证书到期提醒和紧急通知会发到这里。
- Enter your installation id / key:这两个参数需要从 Bitwarden 官方获取,用于标识你的安装实例。获取方式在官方文档里有说明,按提示操作即可。
配置过程中生成的config.yml和global.override.env都在/opt/bitwarden/bwdata下,前者管域名和端口,后者管数据库密码、SMTP 等敏感配置。这两个文件要妥善保管,尤其是global.override.env里有数据库密码。
5.3 启动服务与状态检查
安装完成后启动:
./bitwarden.sh start首次启动会拉取多个镜像,耗时取决于网络。启动完成后用以下命令检查容器状态:
./bitwarden.sh status docker ps正常情况下能看到bitwarden-nginx、bitwarden-api、bitwarden-web、bitwarden-identity、bitwarden-db等容器在运行。如果某个容器反复重启,用docker logs <容器名>看日志,常见原因是数据库还没初始化完、内存不足、端口冲突。
服务起来后,浏览器访问https://vault.example.com,能看到登录页面就说明部署成功。首次使用需要注册账号,注册时设置的主密码就是你的保险库钥匙,务必牢记——这个密码丢了,数据就真的解不开了,官方也帮不了你。
提示:注册账号后建议立刻在设置里开启两步验证,多一层保护。自托管实例的两步验证支持 TOTP 应用,配置起来不复杂。
6. 反向代理、HTTPS 与访问优化
6.1 Nginx 反向代理的角色
Bitwarden 安装脚本自带了一个 Nginx 容器做入口,但很多人习惯在宿主机再套一层 Nginx,原因有几个:统一管理多个站点的证书、方便加访问日志和限流、宿主机 Nginx 处理静态资源更灵活。如果你只跑 Bitwarden 一个服务,用自带的 Nginx 就够,不必多套一层。
如果确实需要宿主机 Nginx,配置大致如下:
server { listen 443 ssl http2; server_name vault.example.com; ssl_certificate /etc/letsencrypt/live/vault.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem; 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; } }proxy_set_header这几行不能省,Bitwarden 靠这些头判断原始请求的协议和来源,缺了会导致重定向循环或 WebSocket 连接失败。proxy_pass指向 Bitwarden 自带 Nginx 映射的端口,默认是 8080,具体看config.yml里的配置。
6.2 证书申请与自动续期
用 Let's Encrypt 申请证书,certbot 是最省事的工具。CentOS 7 上装 certbot:
yum install -y certbot python2-certbot-nginx certbot --nginx -d vault.example.com按提示操作,certbot 会自动改 Nginx 配置并申请证书。续期用certbot renew --dry-run测试,确认无误后加一个定时任务:
echo "0 3 * * * /usr/bin/certbot renew --quiet" | crontab -每天凌晨 3 点检查续期,证书到期前 30 天会自动更新。这个定时任务别忘了加,我见过证书过期导致客户端集体连不上的事故,排查起来很浪费时间。
6.3 访问速度与安全加固
自托管服务放在公网,访问速度受服务器带宽影响。如果服务器在境外,国内访问可能偏慢,可以考虑在客户端设置里调整同步频率,减少不必要的请求。安全方面,几个基础加固动作值得做:
- 修改 SSH 端口,禁用密码登录,改用密钥认证。
- 云厂商安全组只放行 80、443 和自定义 SSH 端口。
- Bitwarden 管理后台(
/admin)设置强密码,这个入口能改配置,权限很高。 - 定期检查
docker ps和日志,发现异常容器及时处理。
这些动作不复杂,但能挡掉大部分自动化扫描和暴力破解。
7. 数据备份、恢复与日常维护
7.1 备份哪些东西才算完整
自托管服务最怕的是数据丢失。Bitwarden 的数据分两块:数据库里的密文保险库,和bwdata目录下的配置文件。备份必须两者都覆盖,只备数据库不备配置,恢复时域名、密钥对不上,服务照样起不来。
完整的备份对象包括:
| 备份对象 | 路径 | 说明 |
|---|---|---|
| 数据库 | bwdata/mssql/data或通过脚本导出 | 核心数据,必须备 |
| 配置文件 | bwdata/config.yml、bwdata/global.override.env | 含密钥和密码,必须备 |
| 证书 | bwdata/ssl或宿主机证书目录 | 可选,重新申请也行 |
| 附件 | bwdata/attachments(如有) | 用户上传的附件 |
Bitwarden 脚本自带备份命令:
./bitwarden.sh backup备份文件默认放在bwdata/backups下。建议再加一层异地备份,用rsync或对象存储同步到另一台机器,避免单点故障。
7.2 恢复流程与验证
恢复时先停服务,把备份文件放回对应位置,再启动:
./bitwarden.sh stop # 恢复数据库和配置文件 ./bitwarden.sh start恢复后务必验证:登录 Web 端,检查保险库条目是否完整,用客户端同步一次,确认数据一致。我建议每季度做一次恢复演练,把备份恢复到测试环境跑一遍,确认备份真的可用——备份文件损坏或恢复步骤遗漏的情况并不少见,等到真出事才发现就晚了。
7.3 版本升级与回滚策略
Bitwarden 更新比较频繁,升级用脚本一条命令:
./bitwarden.sh update升级前先备份,这是铁律。升级后检查容器状态和日志,确认没有报错。如果新版本有问题,回滚的方式是恢复升级前的备份,所以备份文件要保留至少两个版本。
注意:跨大版本升级时,官方有时会要求先升级到中间版本,直接跳版可能失败。升级前看一眼官方发布说明,确认有没有特殊要求。
8. 常见问题排查与避坑经验
8.1 容器起不来怎么查
容器反复重启是最常见的问题。排查顺序:先看docker logs,再看资源占用,最后看端口冲突。
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 数据库容器退出 | 内存不足 | free -h看可用内存,4G 以下容易出问题 |
| API 容器重启 | 数据库未就绪 | 等几分钟再docker ps,或看 API 日志里的连接错误 |
| Nginx 容器退出 | 端口被占用 | ss -tlnp看 80/443 是否被其他进程占用 |
| 全部容器正常但访问 502 | 反向代理配置错误 | 检查proxy_pass端口和请求头 |
内存不足是最隐蔽的坑。CentOS 7 本身占几百兆,Docker 和几个容器加起来,2G 内存很容易在数据库初始化时 OOM。解决办法是加 swap:
dd if=/dev/zero of=/swapfile bs=1M count=2048 chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo "/swapfile swap swap defaults 0 0" >> /etc/fstab2G swap 能缓解内存压力,但治标不治本,长期用还是建议升到 4G 内存。
8.2 HTTPS 证书相关的坑
证书问题集中在两个场景:申请失败和客户端不信任。申请失败多半是域名解析没生效或 80 端口不通,用dig和curl验证一下。客户端不信任常见于自签证书,浏览器和手机 App 都会拦截,解决办法是换正式证书,或者把自签证书导入客户端信任链(操作繁琐,不推荐)。
还有一个坑是证书续期后服务没重载。certbot 续期只更新文件,Nginx 需要 reload 才生效。在续期命令后加--deploy-hook "systemctl reload nginx"可以自动重载。
8.3 客户端同步失败的排查思路
客户端连不上服务端,先确认服务端本身可访问:浏览器能打开 Web 端,说明 HTTPS 和反向代理没问题。然后检查客户端配置的服务器地址是否正确,有没有多写或漏写https://。再检查客户端版本,过旧的客户端可能不兼容新版服务端 API。
如果 Web 端正常但客户端同步失败,看服务端日志里有没有对应请求记录。没有记录说明请求没到服务端,问题在网络或客户端配置;有记录但报错,看错误码定位。常见的是 401(认证失败)和 502(后端不可用),前者检查账号密码,后者检查容器状态。
8.4 磁盘空间与日志管理
磁盘写满是自托管服务的慢性病。除了前面说的 Docker 日志限制,还要关注数据库日志和备份文件堆积。定期用df -h和du -sh /opt/bitwarden/bwdata/*检查占用,备份文件保留最近几个即可,旧的清理掉。
数据库的mssql日志增长较快,可以在管理后台调整日志级别,或者定期执行数据库维护。这块不用天天盯,但每月看一眼,能避免突然写满导致服务中断。
8.5 我踩过的几个真实坑
第一个坑是安装时域名填错,后面改config.yml重新生成配置,结果证书和配置对不上,折腾了半天才理清。教训是安装前把域名解析确认好,一次填对。
第二个坑是没限制 Docker 日志,跑了三个月磁盘满了,数据库写入失败,客户端同步全部报错。加了日志限制后这个问题再没出现过。
第三个坑是升级前没备份,新版本有个配置项变更导致服务起不来,只能重装。从那以后升级前必备份,这个习惯救了我好几次。
这些坑都不复杂,但都是实际会遇到的。自托管服务的维护成本主要就花在这类细节上,提前知道能省不少时间。
9. 使用体验与后续扩展方向
服务跑起来之后,日常使用体验和商业产品差别不大。浏览器插件登录一次,后续自动填充;手机 App 同步及时;桌面端也能用。主密码建议用一句好记但足够长的话,配合两步验证,安全性有保障。
后续如果想扩展,几个方向可以考虑:接入 SMTP 邮件服务,让密码重置和通知能正常发信;配置多个用户和组织,适合小团队共享部分密码;把备份自动化,用脚本定时导出并同步到异地。这些扩展都不难,按需逐步加就行。
我个人在实际操作中的体会是,自托管密码服务的价值不在于省了多少钱,而在于数据归属清晰、心里踏实。维护上花的时间,一个月也就检查一两次,换来的是对自己数字身份的真正掌控。如果你手里有闲置服务器,又对密码安全有要求,这套方案值得一试。