Pod 的名字不是它在网格中的身份

orders → inventory 需要回答的问题不是“库存 Pod 的 IP 是什么”,而是“连接对端以什么身份出现,以及谁担保了这个身份”。Pod IP、Deployment 名称和容器镜像都可能变化;在 Istio 的 Kubernetes 部署中,工作负载通常以命名空间和 ServiceAccount 表示身份。典型的 SPIFFE 形式是 spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>。例如实验信任域为 cluster.local、命名空间为 service-mesh-lab 时,orders 和 inventory 若使用不同 ServiceAccount,就应呈现不同的身份;cluster.local 是示例值,不是所有集群固定使用的值。

身份字符串与证明身份的凭据不是一回事。创建 Pod 时指定 ServiceAccount 是期望身份;实际代理获得并使用的短期 X.509 证书、证书链和对端在握手时的验证结果,才是另一组可核查的证据。反过来,持有一张有效证书也不自动获得所有服务的调用权限:第 21 篇仍要单独做授权。

ServiceAccount 标识身份,agent 经 CA 申请证书并通过 SDS 交给代理;签发和使用均需实测

从 ServiceAccount 到代理证书

Istio 的身份供应路径涉及工作负载的 Kubernetes 凭据、同 Pod 中的 Istio agent、CA 与 Envoy。agent 以工作负载凭据向签发服务申请证书;CA 在验证请求后签发,agent 经 SDS 向代理提供证书材料,代理用于 mTLS。具体 CA、信任域、证书有效期和轮换参数都取决于实际安装配置,不能用概念图推导本环境已经签发了哪张证书。CSR 中包含申请公钥,私钥不应当为了文章取证而导出。

轮换也有两个不同的事实:控制面/agent 具备自动轮换机制,与一次实验中观察到新的证书,不能互相替代。验收时对同一工作负载在轮换前后采集只含公开信息的证书摘要、序列号、SAN、签发者和有效期,并核对握手仍成功、旧证书确实退出活跃配置。Pod 被重建后拿到新证书不等于观察了运行中轮换;为节省时间缩短有效期只能在隔离测试配置中进行,须记录改变过的参数。

用累计服务链验明身份

先在隔离集群部署 examples/service-mesh/ 的 frontend → orders 调用链(orders 先调用 inventory,成功后再调用 external-stub)。应用进程默认监听 127.0.0.1,累计 Kubernetes 清单已显式配置 0.0.0.0:8080;进程基线不能直接当成 Kubernetes 验收。orders、inventory-v1 和 inventory-v2 使用各自 ServiceAccount,保留 Deployment、Pod 实际 serviceAccountName 与命名空间;向 inventory 发一个带唯一 X-Request-ID 的请求,再核对代理使用的证书公开元数据及对端身份。

负例先只改变 orders 的 ServiceAccount:预期身份随之改变,但没有授权限制时,请求未必失败。若要进一步验证不可信证书被拒绝,必须在隔离环境另建错误信任根的客户端,记录握手失败和目标代理证据,不用“换了 ServiceAccount 后正常失败”冒充 CA 拒绝。不要把私钥、ServiceAccount token、JWT 或未经脱敏的 Envoy secret dump 放入证据目录。

待验收项 判据 本文记录
正例:主体与信任链 实际 Pod、公开证书 SAN/链与代理连接中的身份一致 NOT_RUN:无 Kubernetes/Istio/Envoy
负例:更换 ServiceAccount 身份确实改变;是否被拒绝按策略和连接证据另判 NOT_RUN
运行中轮换 同一工作负载前后公开证书信息改变,调用持续可核对 NOT_RUN

实验必须先确认隔离 context,并在 writing-plans/service-mesh/VERSIONS.md 冻结 Kubernetes、Istio、镜像与信任域;本文不提供虚构的证书序列号、过期时间或运行日志。

比较两个 ServiceAccount 的实际证书

以下命令在 examples/service-mesh 目录执行,使用 04 章 Kubernetes 1.33.4 / Istio 1.27.0 sidecar 基线,应用容器名 app。先清理前章授权、故障与出口策略;下列预期值尚不是集群实测结果。

1
2
3
4
5
export SM_CONTEXT=kind-service-mesh-sidecar
k() { kubectl --context="$SM_CONTEXT" -n service-mesh-lab "$@"; }
k apply -f mainline/13/probe.yaml
k rollout status deploy/sm-probe --timeout=120s
OUT="evidence/mainline-19/$(date -u +%Y%m%dT%H%M%SZ)"; mkdir -p "$OUT"

累计部署已经显式使用 orders、inventory-v1、inventory-v2 等 ServiceAccount。sm-probe 使用 orders 身份;mainline/19/alternate.yaml 创建 orders-alt 及 sm-probe-alt。改变的是来源身份,不是伪造 HTTP 头。

1
2
3
4
5
6
7
8
9
10
k apply -f mainline/19/alternate.yaml
k rollout status deploy/sm-probe-alt --timeout=120s
k get pods -l app=sm-probe -o jsonpath='{range .items[*]}{.metadata.uid}{" "}{.spec.serviceAccountName}{"\n"}{end}' > "$OUT/original-pod.txt"
k get pods -l app=sm-probe-alt -o jsonpath='{range .items[*]}{.metadata.uid}{" "}{.spec.serviceAccountName}{"\n"}{end}' > "$OUT/alternate-pod.txt"
k exec deploy/sm-probe -c istio-proxy -- pilot-agent request GET certs > "$OUT/original-certs.json"
k exec deploy/sm-probe-alt -c istio-proxy -- pilot-agent request GET certs > "$OUT/alternate-certs.json"
k exec deploy/sm-probe -c curl -- curl -fsS -H 'x-request-id: ch19-orders' 'http://inventory:8080/quote?sku=paper' > "$OUT/orders.json"
k exec deploy/sm-probe-alt -c curl -- curl -fsS -H 'x-request-id: ch19-alt' 'http://inventory:8080/quote?sku=paper' > "$OUT/alternate.json"
k delete -f mainline/19/alternate.yaml
k exec deploy/sm-probe -c curl -- curl -H 'x-request-id: ch19-recovered' -fsS 'http://inventory:8080/quote?sku=paper' > "$OUT/recovered.json"

certs 管理接口返回公开证书元数据,不取完整 secret dump。需要核对叶证书 SAN 分别含 /sa/orders 和 /sa/orders-alt,并保留实际信任域、序列号、有效期。若 SAN 没有变化,检查读取的是哪个 Pod、代理是否就绪以及安装的信任域;不能以 Deployment 名称代替证书结果。在没有授权限制时,两组请求都应允许。第二组若失败,要区分未签发证书、网络错误和仍残留的授权策略。

运行中轮换另设观察窗口:记录 sm-probe 的 UID 和证书序列号,在同一 UID 存活期间按有效期与刷新策略定期重新执行 certs 请求;只有序列号变化、SAN 不变、请求持续成功才算观察到轮换。普通 Pod 重建会同时改变 UID,不满足这一判据。本文不人为压缩生产证书有效期,也不把等待尚未完成记作 PASS。

签发路径的固定源码入口为 Istio 1.27.0 的 security/pkg/nodeagent/cache/secretcache.go;缓存更新与 SDS 下发不等于对端已经使用新证书,连接侧还须独立核对。

固定源码入口:Istio 1.27.0 security/pkg/nodeagent/cache/secretcache.go。该链接用于核对配置生成或执行机制;完整资源和命令保存在 examples/service-mesh/mainline/19/。静态检查通过不等于集群实验通过。

练习与参考解答

  1. 两个 Pod 都叫 inventory,为什么不能推断它们拥有相同的工作负载身份?参考解答:需要核对命名空间、ServiceAccount、信任域和实际证书 SAN;名称本身不在身份公式里。
  2. 更换 orders 的 ServiceAccount 后仍能访问库存,能证明证书没有更新吗?参考解答:不能。要先读实际 Pod 和代理所用证书;即便身份改变,只要没有相应的拒绝策略,访问仍可能成功。

官方资料与系列导航

系列导航:18 出口路径与强制边界 → 19 工作负载身份和证书怎样建立(本文)→ 20 mTLS 开启后哪些连接受到保护。