在 Docker 容器之间实现 SPIFFE 身份认证

解读

面试官问“在 Docker 容器之间实现 SPIFFE 身份认证”,并不是让你背 SPIFFE 规范,而是考察三件事:

  1. 你是否理解 Docker 原生网络与进程隔离模型 下“身份”为何难定义;
  2. 你是否能把 SPIFFE/SPIRE 的 SVID、Workload API、信任域 这些概念落地到容器运行时;
  3. 你是否能在 国内真实基础设施(CentOS 7/8、内网 GitLab、Harbor、没有 EKS/AKS 可用)里,用 纯开源方案 做出端到端演示,并说出性能、可用性、回滚、排障细节。

一句话:让容器互相认“身份证”,而不是认 IP 或证书文件。

知识点

  1. SPIFFE 核心对象:Trust Domain、SPIFFE ID、SVID(X.509 或 JWT)、Workload API。
  2. SPIRE 架构:Server 节点负责签发、Agent 节点作为本地守护进程通过 Unix Socket 把 SVID 提供给容器。
  3. Docker 关键点
    • 共享 PID namespace 会打破 SPIRE Agent 的 Unix Socket 隔离
    • overlay/bridge/macvlan 网络对 SVID 完全透明,但需防止 iptables 规则误杀 UDP/8081 Workload API
    • 镜像内不能预置私钥,必须在 entrypoint 阶段通过 sidecar 或 init 容器调用 spire-agent api 拉取
  4. 国内合规
    • 国密场景可把 SPIRE Server 的 UpstreamCA 换成 SM2 证书池,但需自己写插件;
    • 等保 2.0 三级要求 双向 TLS 流量可审计,需把 Envoy 的 SPIFFE 过滤器日志打到 ELK 并保存 180 天。
  5. 性能调优
    • Agent 缓存 SVID 默认 5 min,高并发场景下把 svid_cache_ttl 降到 30 s 并开启 rotate_key_early
    • Docker 19.03+ 支持 cgroup v2,可给 spire-agent 容器绑 0.5 核 512 M 防止 OOM。

答案

落地步骤(可直接在国产麒麟 V10 或 CentOS 7.9 上复现):

  1. 宿主机级准备

    • 所有节点 NTP 同步,否则 SVID 会提前过期;
    • 加载 overlay2 与 br_netfilter 模块,开启 net.bridge.bridge-nf-call-iptables=1,保证 VXLAN 封装后的 mTLS 流量不被丢包
  2. 部署 SPIRE Server

    • Docker Compose 启动单节点 Server,映射 宿主机 8081/8443
    • 配置 trust domain = cluster.localjoin_token 作为 Agent 首次认证方式;
    • root CA 证书写进 Kubernetes Secret 或 HashiCorp Vault,国内无 Vault 可换 阿里云 KMS 插件(开源版已支持 PKCS#11)。
  3. 部署 SPIRE Agent

    • 每个 Docker 节点跑一个 特权容器,挂载 /run/spire/sockets 到宿主机;
    • docker-compose.yml 里指定 pid: host,让 Agent 能读到 /procselector based on docker:label:app
    • 国内云主机常禁 53 端口,把 Agent 的 upstream_bundle 下载端口改成 443
  4. 业务容器改造

    • 基础镜像里只装 spire-agent 客户端二进制(约 18 MB),不预置任何证书
    • entrypoint 脚本
      a. 通过 unix:///run/spire/sockets/agent.sock 调用 FetchX509SVID
      b. 把返回的 cert-chain.pem 与 key.pem 写进内存 tmpfs(/certs 目录挂载 tmpfs,size=10m,mode=700);
      c. 导出 SPIFFE_ENDPOINT_SOCKET 环境变量,供 Envoy 或 gRPC 框架自动热加载。
    • docker-compose 里添加 labels
      labels:
        spiffe.io/app: "payment"
        spiffe.io/env: "prod"
      
      对应 SPIRE Server 注册条目
      spire-server entry create \
        -parentID spiffe://cluster.local/host/docker-node-1 \
        -spiffeID spiffe://cluster.local/payment/prod \
        -selector docker:label:spiffe.io/app:payment
      
  5. 容器间调用

    • Envoy 1.24+ 支持 spiffe_validatordownstream_tls_context 要求 match_subject_alt_names:spiffe://cluster.local/
    • 业务代码零改造,只需 sidecar 容器共享 network 命名空间Envoy 在 15001 端口做透明 mTLS
    • 故障演练
      • 手动吊销 SVIDspire-server entry delete -entryID xxxxxEnvoy 会在 15 s 内断开连接
      • 模拟时钟漂移date -s "+10min"Agent 立即重新签发业务 QPS 无 502
  6. 排障 checklist

    • Agent 日志出现 “no identity issued”:检查 label 拼写注册条目是否一致;
    • openssl s_client 连接报错 “verify return:1”:确认 trust domain 字符串完全一致,国内常见错误是把 cluster.local 写成 cluster.cn
    • Docker Desktop Windows 环境不支持 Unix Socket 挂载,需改用 TCP 模式,但 生产环境禁止

拓展思考

  1. 大规模场景

    • Swarm 模式没有 native CRD,可写 docker-spire-registrator 监听 Docker Events自动注册/注销条目
    • 跨信任域北京主数据中心 trust domain = bj.cluster.local上海容灾中心 = sh.cluster.local用 SPIFFE Federation API 交换 Bundle注意国内 300ms 专线延迟把 bundle_refresh_hint 调到 12 h 减少跨区流量。
  2. 安全加固

    • 非 root 容器需给 entrypoint 加 CAP_DAC_OVERRIDE 才能写 tmpfs,否则 FetchX509SVID 成功但写盘失败
    • 等保要求双向 TLS 流量可解密可把 Envoy 的 -enable-core-dump 打开,配合国密 SSLKEYLOGFILE 插件 把会话密钥落盘但需用 chattr +i 防篡改
  3. 与 Service Mesh 对比

    • Istio 1.17 默认用 SPIFFE但自带 Citadel 与 SPIRE 不兼容国内银行案例去掉 Citadel把 Pilot 的 CA_ADDR 指向 SPIRE Server这样既能用 Istio 流量管理又满足人行《金融行业开源软件测评指南》对证书生命周期的要求