效率工具

用 Bitwarden Secrets Manager 给 Docker Compose 部署注入密钥

用 Bitwarden Secrets Manager 给 Docker Compose 部署注入密钥:数据库密码、SMTP 密码存放在 Secrets Manager 项目里,服务器上运行 bws run -- docker compose up -d,compose 文件里只有变量名。

BINBHO · 图文教程

用 Bitwarden Secrets Manager 给 Docker Compose 部署注入密钥:数据库密码、SMTP 密码存放在 Secrets Manager 项目里,服务器上运行 bws run -- docker compose up -d,compose 文件里只有变量名。附带一个检查脚本,核对 compose 需要的变量是否都已在项目里,并发现写死的密码。

先看清成品

本机安装了 bws 2.1.0。检查脚本 3 个测试通过;docker compose config 在注入变量时校验通过、未注入时按预期报错。用无效令牌运行 bws 返回 Doesn't contain a decryption key。没有用真实机器账号读取密钥,也没有真正启动服务。

注意:计划和价格以 Bitwarden 官方价格页为准。不要共享个人账号或主密码,团队共享请用组织和集合。只导入、导出你自己有权管理的凭据。

做完之后你会得到什么?

成果 说明
Secrets Manager 项目 shop-prod,放 DB_PASSWORD、SMTP_PASSWORD
机器账号 + 访问令牌 只能读这个项目
compose 文件 变量缺失时拒绝启动
检查脚本 缺失变量、写死密码

需要准备哪些环境?

项目 要求 说明
Bitwarden 组织 开通 Secrets Manager 计划以官方价格页为准
bws CLI 本文用 2.1.0 从 GitHub sdk-sm 发布页下载
Docker Compose v2 以上 本文用 v5.6.0
Python 3.9 以上 运行检查脚本

文件结构

24-bitwarden-A-src/
├── compose.yaml
├── check_secrets.py
├── sample_list.json        # bws secret list 输出示例(值已省略)
└── test_check_secrets.py

第 1 步:建项目和机器账号

  1. Secrets Manager 网页版新建项目 shop-prod,添加密钥:键名 DB_PASSWORD、SMTP_PASSWORD(键名要符合环境变量命名规则)。
  2. 新建机器账号 shop-server,在“项目”页签只授予 shop-prod 的读取权限。
  3. 在“访问令牌”页签创建令牌,设置过期时间。令牌只显示一次。

第 2 步:compose 文件

compose.yaml:

services:
  db:
    image: postgres:17
    environment:
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD 未注入,请用 bws run 启动}
    volumes:
      - pgdata:/var/lib/postgresql/data
  app:
    image: ghcr.io/example/shop:latest
    environment:
      DATABASE_URL: postgresql://shop:${DB_PASSWORD:?}@db:5432/shop
      SMTP_PASSWORD: ${SMTP_PASSWORD:?SMTP_PASSWORD 未注入}
    depends_on: [db]
volumes:
  pgdata:

${VAR:?提示} 表示变量为空或未设置时直接报错,不会用空密码启动数据库。

第 3 步:检查脚本

check_secrets.py:

"""核对 compose 需要的变量是否都在 Secrets Manager 项目里,并确认 compose 文件没有写死密码。
用法:bws secret list <项目ID> --output json | python check_secrets.py compose.yaml
"""
import json, re, sys
from pathlib import Path

VAR = re.compile(r"\$\{([A-Z_][A-Z0-9_]*)(?::?[?-][^}]*)?\}")
LITERAL = re.compile(r"^\s*\w*(PASSWORD|SECRET|TOKEN)\w*\s*:\s*(?P<v>\S.*)$", re.I)

def check(compose_text, secrets):
    keys = {s["key"] for s in secrets}
    need = set(VAR.findall(compose_text))
    problems = [f"Secrets Manager 中缺少 {k}" for k in sorted(need - keys)]
    for n, line in enumerate(compose_text.splitlines(), 1):
        m = LITERAL.match(line)
        if m and "${" not in m["v"]:  # 值里没有变量引用,才算写死
            problems.append(f"compose 第{n}行疑似写死了密钥:{line.strip()}")
    return need, problems

if __name__ == "__main__":
    secrets = json.load(sys.stdin)
    need, problems = check(Path(sys.argv[1]).read_text(encoding="utf-8"), secrets)
    print("需要的变量:", ", ".join(sorted(need)))
    print("\n".join("✗ " + p for p in problems) or "检查通过")
    sys.exit(1 if problems else 0)
export BWS_ACCESS_TOKEN=(机器账号令牌,只在当前 shell 中设置)
bws secret list 项目ID --output json | python check_secrets.py compose.yaml

用示例数据的本机实际输出:

需要的变量: DB_PASSWORD, SMTP_PASSWORD
✗ Secrets Manager 中缺少 SMTP_PASSWORD

测试:python -m pytest -q → 3 passed。

第 4 步:注入并启动(本文未执行)

bws run --project-id 项目ID -- 'docker compose up -d'

bws run 把项目里的密钥按键名设为环境变量,只传给这条命令。

预期结果:容器正常启动;docker compose config 不带 bws 运行时报错:

error while interpolating services.app.environment.DATABASE_URL: required variable DB_PASSWORD is missing a value

(这段是本机实际输出。)

令牌不要写进 shell 历史:可以放在只有 root 可读的文件里,由 systemd 的 EnvironmentFile= 加载。

调试示例

第一版“写死密码”的正则是 (PASSWORD|SECRET|TOKEN)\s*:\s*(?!\$\{)\S+,实际运行把三行正常的变量引用都误报了:

✗ compose 第6行疑似写死了密钥:POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD 未注入,请用 bws run 启动}
✗ compose 第12行疑似写死了密钥:DATABASE_URL: postgresql://shop:${DB_PASSWORD:?}@db:5432/shop

原因:\s* 可以匹配 0 个空格,否定前瞻就落在空格上而失效;另外 ${DB_PASSWORD:?} 里也含 PASSWORD:。改成“键名含 PASSWORD/SECRET/TOKEN,且值里不含 ${”后误报消失,并加了 test_literal 测试。

出错了怎么办?

现象 原因 解决
Doesn't contain a decryption key 令牌不完整或错误 重新复制完整令牌
401 / 令牌失效 令牌过期或被撤销 新建令牌
变量没注入 键名不符合环境变量规则(有空格、横杠) 改键名,或用 --uuids-as-keynames
读到别的项目的密钥 没加 --project-id 且机器账号能访问多个项目 加 --project-id,并收紧权限
自建 Bitwarden 连不上 默认连官方云 用 --server-url 指向你的服务器

模板规则

  • compose 里只写 ${VAR:?},不写值。
  • 一个环境一个项目、一个只读机器账号。
  • 令牌设过期时间,定期轮换。

项目资源

用途 地址
Secrets Manager 概览 https://bitwarden.com/help/secrets-manager-overview/
Secrets Manager CLI https://bitwarden.com/help/secrets-manager-cli/
访问令牌 https://bitwarden.com/help/access-tokens/
bws 下载(sdk-sm) https://github.com/bitwarden/sdk-sm
Compose 变量插值 https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/
Bitwarden 价格 https://bitwarden.com/pricing/

服务器上不再有 .env 明文文件,密钥集中在 Secrets Manager。换密码时只改一处,重新 bws run 部署即可;令牌泄露了,撤销它就切断访问。