集成 Vault 实现容器启动时动态挂载加密卷

解读

国内云原生面试中,该题考察候选人能否把 HashiCorp Vault动态密钥 能力与 Docker 的 卷挂载生命周期 打通,做到“容器启动才解密、停止即销毁明文”,既满足等保/密评对 密钥不落盘 的要求,又兼顾镜像无侵入、研发零改造。高频痛点是:

  1. 裸镜像里写死密钥被扫描审计扣分;
  2. 传统 K8s Secret 在 etcd 里静态存储,无法自动轮转;
  3. 金融、政务项目要求 国密算法双层门禁(人 + 机)。
    面试官期望听到 “Vault Agent 自动认证→渲染 tmpfs→主进程启动” 的闭环,并能给出 国密版改造失败熔断 方案。

知识点

  1. Vault 认证链: Kubernetes SA TokenAppRole国密 SM2 证书
  2. 响应式挂载: Vault Agent Agent-inject sidecar 与 consul-template 模板渲染
  3. 卷类型: tmpfsramfssecretfs(国密扩展驱动)
  4. 生命周期钩子: Docker --inittinidumb-init 收割僵尸,确保 Vault 退出前清理明文
  5. 最小镜像: distrolesschainguard 基础镜像,非 root 用户(UID ≥10000)运行
  6. 国密合规: SM4-CBC 加密卷,SM2 做密钥协商,Vault Transit + 国密插件
  7. 零信任网络: mTLS 双向校验,SPIFFE IDVault Namespace 绑定
  8. 审计与熔断: Vault Audit Device → Kafka → 内部 SOCmax_ttllease_threshold 自动重启容器

答案

步骤 1:Vault 侧准备

  • 启用 KV-v2Transit 引擎,创建 SM4 密钥(国密场景)
  • 配置 AppRole
    vault write auth/approle/role/docker-sm4 \
      secret_id_ttl=10m token_max_ttl=30m token_policies=sm4-policy \
      bind_secret_id=true secret_id_num_uses=1
    
  • 策略 sm4-policy 仅允许读取 sm4/keysm4/decrypt 接口,最小权限

步骤 2:构建“Vault-Agent” sidecar 镜像
Dockerfile:

FROM alpine:3.18
RUN apk add vault=1.15.4-r0 tini
COPY vault-agent.hcl /etc/vault/
ENTRYPOINT ["tini", "--", "vault", "agent", "-config=/etc/vault/vault-agent.hcl"]

vault-agent.hcl 核心片段:

auto_auth {
  method "approle" {
    config = { role_id_file_path="/run/secrets/role_id" }
  }
}
template {
  source      = "/run/vault/in.tpl"
  destination = "/vault/secrets/key.bin"
  command     = "sh -c 'mount -t tmpfs -o size=32m tmpfs /data && openssl enc -d -sm4-cbc -in /vault/secrets/key.bin -out /data/plain.key -K $(cat /vault/secrets/key.bin)'"
}

说明:模板渲染后自动触发 国密解密 并写入 tmpfs宿主机无残留

步骤 3:业务主镜像改造

  • 使用 多阶段构建 把二进制与 Vault 无关文件提前编译,最终镜像不含任何密钥
  • ENTRYPOINT 改为 docker-entrypoint.sh,先阻塞等待 /data/plain.key 存在:
    until [ -f /data/plain.key ]; do sleep 0.5; done
    exec gosu 10000:10000 /app/server
    

步骤 4:Compose 编排(单节点演示)

services:
  vault-agent:
    image: registry.cn-hangzhou.aliyuncs.com/your/vault-agent:1.15.4
    environment:
      VAULT_ADDR: https://vault.intra
      VAULT_CACERT: /run/secrets/vault-ca.crt
    secrets:
      - role_id
      - secret_id
    volumes:
      - type: tmpfs
        target: /data
        tmpfs:
          size: 32m
          mode: 0700
  app:
    image: registry.cn-hangzhou.aliyuncs.com/your/app:sm4
    depends_on:
      - vault-agent
    volumes:
      - type: tmpfs
        source: /data
        target: /data
        read_only: true
    user: "10000:10000"
secrets:
  role_id:
    file: ./role_id
  secret_id:
    file: ./secret_id

关键点

  • 两个容器共享 同名 tmpfsvault-agent 写、app 只读
  • secret_id 一次性,容器重启后失效,防重放
  • Compose 3.9 以上支持 tmpfs 模式生产环境用 Swarm 或 K8s 的 emptyDir+memory 介质

步骤 5:启动与验证

docker compose up -d
docker exec app sh -c 'ls -l /data'   # 仅看到 plain.key,权限 0400
docker stop vault-agent               # 停 sidecar,tmpfs 随容器销毁,**明文瞬间消失**

审计:Vault 侧查看 lease_id30 分钟未续租自动吊销SOC 收到审计日志

国密增强

  • 把 Vault Transit 的 aes256-gcm96 换成 国密插件 gmssl-transit算法标识 1.2.156.10197.1.104.3
  • 宿主机内核加载 secretfs 模块,SM4 硬件加速(鲲鹏 / 海光)

失败熔断

  • 若 Vault 不可达,Agent 重试 3 次后 exit 1,Compose restart_policy: none 让容器直接失败,防止明文降级写盘

拓展思考

  1. 大规模场景
    Vault Operator 在 K8s 里跑 Raft 三节点HSM 国密卡SM2 根密钥保护,Docker 节点通过 SPIFFE CSI Driver 动态注入 tmpfs,单集群 5k Pod 级 仍保持 30 秒全量轮转
  2. 混合云合规
    阿里云 KMSVault 联邦 打通,北京区 Vault 作为 国密密钥源上海区 Docker Swarm 通过 专线 + mTLS 拉取密钥,满足《数据出境评估办法》
  3. Debug 技巧
    在 vault-agent 侧加 -log-level=trace把渲染模板重定向到 stderrFluent-bit 采集进 LokiGrafana 大盘关联 trace-id三分钟定位“密钥未渲染”根因