使用 HashiCorp Vault Agent 在容器启动前拉取机密
解读
在国内金融、运营商、政务云等合规场景里,明文写死密码、把密钥打到镜像里都是红线。Docker 容器启动时必须拿到数据库口令、第三方 API Token、证书私钥等敏感信息,而 Vault 作为“统一机密中心”能动态签发、自动轮转、审计留痕。面试官问“容器启动前拉取机密”,实质考察三件事:
- 镜像本身零机密,Build 阶段不泄露;
- Runtime 阶段最小权限,容器只能拿到自己需要的 Secret;
- 整个流程对 Kubernetes 友好,也能在裸 Docker 或 Swarm 落地,并符合国内等保 2.0、关基、密评要求。
知识点
- Vault Agent 的 auto-auth、templating、renew 机制
- Vault Agent Sidecar 与 InitContainer 两种注入模式差异
- AppRole、JWT、K8s SA、AliCloud RAM 等国内常用认证后端
- Docker 的 --init、ENTRYPOINT 信号传递、tmpfs 挂载
- 镜像多阶段构建、distroless 与 chown 65534 非 root 实践
- 国内云厂商 KMS 与 Vault 的 transit 统一加解密 对接
- 审计日志落盘到 LTS(如阿里云 SLS、腾讯 CLS)满足 6 个月可回溯
- 等保 2.0 对“剩余信息清除”要求:容器停止后 tmpfs 自动销毁
答案
生产级落地分五步,可直接在 Docker 单节点或 Swarm 集群复现,也能平滑迁移到 Kubernetes。
-
镜像侧零机密构建
使用多阶段构建,把 Vault Agent 二进制(官方 1.16.x linux/amd64)COPY 到最终镜像,业务代码与配置文件均不出现密码。FROM alpine:3.19 AS vault RUN apk add --no-cache wget && \ wget -O- https://releases.hashicorp.com/vault/1.16.2/vault_1.16.2_linux_amd64.zip | funzip >/vault FROM gcr.io/distroless/java17-debian12 COPY --from=vault --chown=65534:65534 /vault /vault/bin/vault COPY --chown=65534:65534 app.jar / ENTRYPOINT ["/vault/bin/vault", "agent", "-config=/vault/config/agent.hcl"] -
Vault 侧最小权限策略
为应用创建独立 namespace 与 AppRole,policy 只读指定路径:path "secret/data/myapp/{{identity.entity.metadata.env}}" { capabilities = ["read"] }国内合规要求双人复核才能修改 policy,可在 Vault Enterprise 打开 MFA。
-
启动侧InitContainer 模式(Docker 单节点可用 docker-compose v3.9 的
init语法模拟)
compose 片段:services: vault-agent: image: vault:1.16.2 volumes: - ./agent.hcl:/vault/config/agent.hcl:ro - vault-token:/vault/token - secrets-store:/secrets environment: VAULT_ADDR: https://vault.intra:8200 VAULT_CACERT: /vault/tls/vault-ca.crt command: ["vault", "agent", "-config=/vault/config/agent.hcl"] networks: - internal app: image: myapp:zero-secret depends_on: - vault-agent volumes: - secrets-store:/run/secrets:ro entrypoint: ["/bin/sh", "-c", "source /run/secrets/db.env && exec java -jar /app.jar"] networks: - internal volumes: vault-token: secrets-store:agent.hcl 关键段落:
auto_auth { method "approle" { config = { role_id_file_path = "/vault/role-id" secret_id_file_path = "/vault/secret-id" } } } template { source = "/vault/template/db.env.tpl" destination = "/secrets/db.env" command = "pkill -HUP vault-agent" # 国内部分老内核需要信号触发 }模板文件 db.env.tpl:
export DB_USER="{{ .Data.data.username }}" export DB_PASS="{{ .Data.data.password }}" -
安全加固
- 把
/run/secrets挂载为 tmpfs,容器停止自动清零,满足等保“剩余信息清除”条款。 - Vault Agent 与业务进程非 root 运行,镜像内提前
chown 65534。 - 开启 TLS 双向认证,Vault 服务端使用国密双证书(RSA+SM2)过密评。
- 审计日志通过 file_sink 写到宿主机,由 Logtail 实时采集到阿里云 SLS,保存 180 天。
- 把
-
故障排查 checklist
- 若 agent 日志出现
permission denied,优先检查 AppRolesecret_id_num_uses是否耗尽; - 国内云主机时间不同步会导致 Vault TLS 握手失败,用 Chrony 强制同步;
- 当
template输出为空,确认 policy 路径与secret/data/前缀是否匹配,Vault KV2 必须带/data; - 容器重启后旧 tmpfs 消失属正常,不要误以为是 Docker 卷泄漏。
- 若 agent 日志出现
拓展思考
- 如果未来迁移到 Kubernetes,可把 Vault Agent 注入器换成 Vault Agent Injector 或 CSI Provider,但策略模板与 AppRole 可以复用,保证混合云平滑过渡。
- 国内部分银行要求国密算法对机密再做一层信封加密,可在 Vault 的 transit 引擎用 SM4 加密,业务容器拿到的只是密文,启动后再调用 softhsm 解密,实现“双重控制”。
- 当 Vault 出现单点故障,Docker Swarm 侧可配置 Consul Template + 本地缓存,在 Vault 不可用时降级读取本地 AES-256 加密文件,但需通过 KMS 托管密钥 解密,保证高可用与合规不冲突。