身份不是 IP,是 ServiceAccount
一句话总结
在 Istio 中,工作负载的身份既不是 IP,也不是 Pod 名称,而是 Kubernetes ServiceAccount。mTLS、授权策略以及大多数策略调试,都由这一事实延伸而来。
为什么需要它
集群内部常被视为可信网络,但实际上,只要一个 Pod 被攻陷,攻击者就能在这个扁平网络中以明文访问其他所有 Pod。零信任颠覆了这种假设。问题在于,若要在应用代码中实现它,就必须为每项服务加入证书签发与轮换、对端验证和权限检查逻辑。对于使用多种语言栈的组织而言,几乎不可能始终如一地维护这些逻辑。
因此,Istio 把三项能力——加密、工作负载认证和授权——下沉到了基础设施层,而其基础正是身份体系。IP 会在 Pod 重启后改变,也可能被伪造;基于 ServiceAccount 的身份则写入证书,并在握手期间通过密码学方式验证。
工作原理
身份遵循 SPIFFE 标准格式。
spiffe://cluster.local/ns/payments/sa/payments-api
트러스트 도메인 네임스페이스 서비스어카운트
证书签发流程中必须记住:私钥不会离开 Pod。Pod 内的 istio-agent 生成密钥对,只把 CSR 发送给 istiod。istiod 使用 ServiceAccount token 验证请求者,然后签署把 SPIFFE ID 写入 SAN 的证书并返回。证书默认有效期为 24 小时,约经过生命周期的一半后,agent 会自动重新签发。Envoy 通过 SDS 获取证书,因此轮换证书无需重启 Pod,也无需排空连接。
PeerAuthentication 决定“如何要求入站流量使用 mTLS”。它有 PERMISSIVE(两者都接受,默认值)、STRICT(仅 mTLS)和 DISABLE 三种模式,而且范围越窄,优先级越高,依次为工作负载 > namespace > mesh 全局。mesh 全局策略应放在根 namespace(通常是 istio-system)中,并命名为 default。若只想为健康检查或 mesh 外 Prometheus 抓取的某个端口设置例外,可以使用 portLevelMtls。
AuthorizationPolicy 的评估顺序几乎是考试必考内容。
1. CUSTOM → 외부 인가기가 거부하면 즉시 거부
2. DENY → 하나라도 매칭되면 즉시 거부
3. ALLOW → 대상 워크로드에 ALLOW 정책이 하나도 없으면 허용(기본 개방)
ALLOW 정책이 하나라도 있으면 매칭되어야 허용(기본 거부로 전환)
最后一行是关键特性:一旦添加一条 ALLOW 策略,该工作负载就会切换为白名单模式。 利用这一特性,可以用一条 spec: {} 的空策略实现 namespace 默认拒绝。它表示“存在 ALLOW 策略,但没有可匹配的规则”,因此任何请求都无法通过。
还有一个常见陷阱。principals 来自 mTLS 证书,所以明文请求的 principal 为空,绝不可能匹配。PERMISSIVE 同时接受明文和 mTLS。这意味着明文请求没有 principal,并不意味着 PERMISSIVE 模式下的所有请求都没有身份。mTLS 请求在此模式下仍可拥有 principal,因此调试策略时还要同时确认实际连接方式。STRICT 是另一项决定,它表示不接受明文。
RequestAuthentication 验证最终用户 token(JWT)。最常见的误解是:单独使用该资源时,没有 token 的请求仍会直接通过。因为它只强制“如果存在 JWT,就必须有效”。要强制 token 必填,还需要 AuthorizationPolicy,但不能简单再添加一条带 JWT 条件的 ALLOW。多条 ALLOW 取并集。如果已有允许 orders 身份的 ALLOW,那么没有 JWT 的 orders 仍会通过该策略;而只检查 JWT 的 ALLOW 又可能放行其他身份或路径。
若要同时要求两个条件,应在同一条 ALLOW 规则的同一个 source 中放入 principals 和 requestPrincipals;或者保留现有最小权限 ALLOW,同时使用带 notRequestPrincipals: ["*"] 的 DENY,阻止没有 JWT 主体的请求。请记住:同一 source 内的字段是 AND,不同的 from 项或 ALLOW 策略之间则是 OR。DENY 也可能捕获不具备 HTTP 属性的 TCP 请求,因此必须明确指定适用端口。下一项实验只针对 HTTP 8080 端口。
失败状态码也无法说明全部原因。在隔离的 Istio 1.31.0 实验中,使用不同 audience 的 JWT 会返回 403 和 audience 拒绝消息;身份或路径授权失败则返回 403 和 RBAC 拒绝消息。不要只看 401/403 这个数字,还要一起检查响应正文、已应用的策略以及经过验证的主体。
把策略上线到生产环境时,应先添加 istio.io/dry-run: "true" annotation。它只进行评估而不实际阻断,并通过日志和指标展示“如果启用,会拦截什么”。确认拒绝列表符合预期后再移除 annotation,才是安全的顺序。
现场会遇到的情况
作者的家庭实验室使用 WireGuard 对节点间 Pod 流量进行透明加密。在 cilium status 中显示为 Encryption: Wireguard [cilium_wg0 (Port: 51871, Peers: 2)],Modules Health 为 OK 92 / Degraded 0。
这里会出现一个考试也会涉及的重要区别:WireGuard 是节点间传输链路加密,而 Istio mTLS 是带有工作负载身份证明的应用层加密。 前者保证“即使窃听线路也无法读取”,但不能证明“由谁发送”。AuthorizationPolicy 的 principals 所要求的是后者。两层可以叠加使用,但如果团队没有明确统一由哪一层负责加密,双重策略就会造成调试灾难。
在同一集群中还能观察到 L7 判定的日志形态。违反策略的请求记录为 DROPPED,而针对该请求返回的 403 响应则记录为 FORWARDED。Istio RBAC 拒绝同样表现为“连接已经建立,并返回了 403”。绝不要把它与连接本身无法建立的问题混淆。
下一实验要做什么
先在 kwok API 中保存专用 ServiceAccount 和工作负载清单,然后编写 mesh 全局 STRICT 与工作负载级端口例外,并按默认拒绝 → 最小权限 ALLOW → 管理路径 DENY(dry-run)→ 强制 JWT 的顺序逐层构建策略。
本实验不会实际运行容器或 Envoy。请区分 manifest 设计与真实流量验证。