在 Docker 容器之间实现 SPIFFE 身份认证
解读
面试官问“在 Docker 容器之间实现 SPIFFE 身份认证”,并不是让你背 SPIFFE 规范,而是考察三件事:
- 你是否理解 Docker 原生网络与进程隔离模型 下“身份”为何难定义;
- 你是否能把 SPIFFE/SPIRE 的 SVID、Workload API、信任域 这些概念落地到容器运行时;
- 你是否能在 国内真实基础设施(CentOS 7/8、内网 GitLab、Harbor、没有 EKS/AKS 可用)里,用 纯开源方案 做出端到端演示,并说出性能、可用性、回滚、排障细节。
一句话:让容器互相认“身份证”,而不是认 IP 或证书文件。
知识点
- SPIFFE 核心对象:Trust Domain、SPIFFE ID、SVID(X.509 或 JWT)、Workload API。
- SPIRE 架构:Server 节点负责签发、Agent 节点作为本地守护进程通过 Unix Socket 把 SVID 提供给容器。
- Docker 关键点:
- 共享 PID namespace 会打破 SPIRE Agent 的 Unix Socket 隔离;
- overlay/bridge/macvlan 网络对 SVID 完全透明,但需防止 iptables 规则误杀 UDP/8081 Workload API;
- 镜像内不能预置私钥,必须在 entrypoint 阶段通过 sidecar 或 init 容器调用 spire-agent api 拉取。
- 国内合规:
- 国密场景可把 SPIRE Server 的 UpstreamCA 换成 SM2 证书池,但需自己写插件;
- 等保 2.0 三级要求 双向 TLS 流量可审计,需把 Envoy 的 SPIFFE 过滤器日志打到 ELK 并保存 180 天。
- 性能调优:
- 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 上复现):
-
宿主机级准备
- 所有节点 NTP 同步,否则 SVID 会提前过期;
- 加载 overlay2 与 br_netfilter 模块,开启 net.bridge.bridge-nf-call-iptables=1,保证 VXLAN 封装后的 mTLS 流量不被丢包。
-
部署 SPIRE Server
- 用 Docker Compose 启动单节点 Server,映射 宿主机 8081/8443;
- 配置 trust domain = cluster.local,join_token 作为 Agent 首次认证方式;
- 把 root CA 证书写进 Kubernetes Secret 或 HashiCorp Vault,国内无 Vault 可换 阿里云 KMS 插件(开源版已支持 PKCS#11)。
-
部署 SPIRE Agent
- 每个 Docker 节点跑一个 特权容器,挂载 /run/spire/sockets 到宿主机;
- docker-compose.yml 里指定 pid: host,让 Agent 能读到 /proc 做 selector based on docker:label:app;
- 国内云主机常禁 53 端口,把 Agent 的 upstream_bundle 下载端口改成 443。
-
业务容器改造
- 基础镜像里只装 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:
对应 SPIRE Server 注册条目:labels: spiffe.io/app: "payment" spiffe.io/env: "prod"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
-
容器间调用
- Envoy 1.24+ 支持 spiffe_validator,downstream_tls_context 要求 match_subject_alt_names:spiffe://cluster.local/;
- 业务代码零改造,只需 sidecar 容器共享 network 命名空间,Envoy 在 15001 端口做透明 mTLS;
- 故障演练:
- 手动吊销 SVID:
spire-server entry delete -entryID xxxxx,Envoy 会在 15 s 内断开连接; - 模拟时钟漂移:
date -s "+10min",Agent 立即重新签发,业务 QPS 无 502。
- 手动吊销 SVID:
-
排障 checklist
- Agent 日志出现 “no identity issued”:检查 label 拼写与 注册条目是否一致;
- openssl s_client 连接报错 “verify return:1”:确认 trust domain 字符串完全一致,国内常见错误是把 cluster.local 写成 cluster.cn;
- Docker Desktop Windows 环境不支持 Unix Socket 挂载,需改用 TCP 模式,但 生产环境禁止。
拓展思考
-
大规模场景:
- 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 减少跨区流量。
-
安全加固:
- 非 root 容器需给 entrypoint 加 CAP_DAC_OVERRIDE 才能写 tmpfs,否则 FetchX509SVID 成功但写盘失败;
- 等保要求双向 TLS 流量可解密,可把 Envoy 的 -enable-core-dump 打开,配合国密 SSLKEYLOGFILE 插件 把会话密钥落盘,但需用 chattr +i 防篡改。
-
与 Service Mesh 对比:
- Istio 1.17 默认用 SPIFFE,但自带 Citadel 与 SPIRE 不兼容;国内银行案例是 去掉 Citadel,把 Pilot 的 CA_ADDR 指向 SPIRE Server,这样既能用 Istio 流量管理,又满足人行《金融行业开源软件测评指南》对证书生命周期的要求。