Day 15 / 共 20 天 · 第 3 周收官

SDS + 认证授权

Day 14 讲了证书怎么签发,今天讲证书怎么分发给 Envoy(SDS),以及两大安全策略:PeerAuthentication(控制 mTLS 模式)和 AuthorizationPolicy(谁能访问谁)。收官第 3 周。Day 14 造好了"工牌",今天讲工牌怎么发到岗(SDS)、门禁开几档(PeerAuth)、谁能进门(AuthzPolicy)——安全策略的完整落地。

📍 你在整门课的位置 · 第 3 周「sidecar 与安全」收官(W1 控制面 ✓ · W2 xDS ✓ · 下周 → 遥测/扩展/安装)
D11 sidecar 注入 D12 pilot-agent D13 bootstrap D14 mTLS·CA D15 SDS·授权
L01

SDS 分发证书

🤔 痛点:证书 24 小时就过期,难道每次都重启 Envoy? Day 14 说证书是"短命"的(几小时到一天就轮转)。如果每换一次证书就要重启 Envoy,那业务连接全断、每天抖动无数次——完全不可接受。怎么做到"换证书但连接不断"?
💡 本质:把证书也做成"可热更新的 xDS" 答案很优雅:复用你早就熟悉的 xDS 流式协议。SDS 就是"证书版的 xDS"——就像管家不是把工牌塞进抽屉,而是用一条常开的传送带把工牌递到岗位:新工牌做好了,直接顺着传送带推过去,员工换牌不用离岗。所以证书能"短命+自动轮转"却对业务零感知,靠的就是这条推送通道。
// security/pkg/nodeagent/sds/sdsservice.go
// generate(resourceNames)(:129):对每个资源名 st.GenerateSecret → toEnvoySecret(:143)
// StreamSecrets(:271):通过 xds.Stream 处理 Envoy 的订阅流
// 证书更新时 Context.Push(:258)推送
// 资源名:ROOTCA(根证)、default(工作负载证书)
SDS = "证书的 xDS" 还记得 Envoy 课的 xDS 吗(LDS/CDS/…)?SDS(Secret Discovery Service)就是"证书的 xDS"——Envoy 通过 SDS 协议向 agent 订阅证书,agent 通过同样的 gRPC 流式协议推送证书。Envoy 订阅两个资源:ROOTCA(根证书,用来验证对端)和 default(自己的工作负载证书)。证书轮转(Day 14)时,agent 主动 Push 新证书,Envoy 热加载——连接不断、无需重启。这就是为什么 mTLS 证书能"短命 + 自动轮转"却对业务无感。
L02

toEnvoySecret

// sdsservice.go:292 toEnvoySecret
//   ROOTCA → Secret_ValidationContext(:304,含 TrustedCa + 可选 CRL)
//   工作负载证书 → Secret_TlsCertificate(:338,含证书链 + 私钥)
//   证书以 InlineBytes 直接内联(:307/341),不落盘
读法:把内部证书格式转成 Envoy 的 tls.Secret proto。根证转成"验证上下文"(用于校验对端),工作负载证书转成"TLS 证书"(自己出示的)。私钥以 InlineBytes 内联进 SDS 响应——全程内存、不落盘(Day 12 的安全设计)。还支持 cryptomb/qat 硬件加速私钥运算。
L03

PeerAuthentication

// pilot/pkg/model/authentication.go
// type MutualTLSMode(:32):MTLSUnknown/MTLSDisable/MTLSPermissive/MTLSStrict
// addPeerAuthentication(:126):区分 mesh 级/namespace 级/workload 级
//   mesh 级 UNSET 默认 PERMISSIVE(:154);namespace UNSET 继承 mesh
PeerAuthentication = "mTLS 开关的三档" PeerAuthentication 这个 CRD 控制服务"要不要求 mTLS":STRICT(严格)——只接受 mTLS 流量,明文拒绝;PERMISSIVE(宽容)——既接受 mTLS 也接受明文(迁移期用,逐步切换);DISABLE——不用 mTLS。可以在 mesh 级(全网格默认)、namespace 级、workload 级分别设,越具体越优先(workload 覆盖 namespace 覆盖 mesh)。默认 PERMISSIVE——让你能平滑地从"无 mTLS"迁移到"全 mTLS",不用一刀切。
📝 举个例子:门禁三档遇到不同来客
模式mTLS 客户端(刷卡)明文客户端(没卡)
STRICT 严格✅ 放行❌ 拒绝
PERMISSIVE 宽容✅ 放行✅ 也放行(迁移期)
DISABLE 关闭—(不启用 TLS)✅ 放行
典型迁移路径:老系统先全 PERMISSIVE(新老客户端共存)→ 客户端逐个升级到 mTLS → 确认没有明文了 → 切 STRICT 收口。
⚠️ 常见误解:把 PeerAuthentication 和 AuthorizationPolicy 混为一谈。记住分工:PeerAuth 管"加不加密、验不验身份"(门禁开几档),AuthzPolicy 管"验完身份后谁能进哪个门"(访客名单)。前者是"能不能进楼",后者是"能进哪些房间"。
L04

模式合成

// pilot/pkg/security/authn/policy_applier.go:366 ComposePeerAuthentication
//   mesh→namespace→workload→port 级优先级合成,默认 MTLSPermissive
//   PortLevelMtls 建 PerPort 映射(:421)
// pilot/pkg/security/authn/utils/utils.go:40 BuildInboundTLS
//   算出端口生效模式 → 生成 Envoy DownstreamTlsContext
读法:istiod 把多层 PeerAuthentication 合成一个"最终生效模式"(甚至精确到端口级),再翻译成 Envoy 的入站 TLS 配置(DownstreamTlsContext,Envoy 课 Day 08 见过)。MTLSDisable → 返回 nil(不启用 TLS)。这是"高层策略 → Envoy 配置"的又一个翻译(呼应 Day 07-08 的 xDS 生成)。
L05

PERMISSIVE 双链

PERMISSIVE 怎么"既收明文又收 mTLS"? 这是个精妙实现。PERMISSIVE 模式下,istiod 给 Envoy 的 listener 生成两条过滤器链(Envoy 课 Day 05 的 filter chain):一条处理 mTLS 流量、一条处理明文流量,按 istio ALPN 标记自动匹配。mTLS 客户端的流量带特殊 ALPN → 走 mTLS 链;普通明文流量 → 走明文链。所以 PERMISSIVE 不是在单条链里"判断",而是在 listener 层用两条链分流BuildInboundTLS 的注释 utils.go:60-70 说明了这点)。这让迁移期新旧客户端能共存。
mTLS 客户端带 istio ALPN 明文客户端无 ALPN Envoy listener mTLS 链解密+验证书 明文链直接透传 业务容器app ALPN 匹配
PERMISSIVE 不是"在一条链里判断",而是像大门开了两个通道——刷卡道(mTLS 链)和人工道(明文链),靠 ALPN 标记自动分流。
L06

AuthorizationPolicy

PeerAuthentication 管"加不加密",AuthorizationPolicy 管"谁能访问谁"Action 有 ALLOW/DENY/AUDIT/CUSTOM。

授权 = "访问控制清单" 有了身份(Day 14 的 SPIFFE),就能做授权:"只允许 frontend 服务访问 backend/api 路径"、"拒绝来自某命名空间的请求"。AuthorizationPolicy 用规则表达"什么身份、什么来源、什么路径/方法 → 允许还是拒绝"。CUSTOM action 还能把授权决策委托给外部服务(ext_authz)。身份认证(你是谁,mTLS)+ 授权(你能干什么,AuthzPolicy)= 完整的服务间访问控制。这全部在 Envoy 里执行——istiod 只负责把策略翻译下发。
L07

→ Envoy RBAC

// pilot/pkg/security/authz/builder/builder.go
// New(:69)→ BuildHTTP(:95)/ BuildTCP(:100)
// build(:170):分离 enforce 与 dry-run 规则;逐条 authzmodel.New → Generate
//   → 产出 rbacpb.Policy(Envoy RBAC filter 配置)
读法:AuthorizationPolicy 被翻译成 Envoy 的 RBAC filter(一个 L7 过滤器,Envoy 课 Day 08)。istiod 的 builder 把高层规则(principals/permissions)编译成 Envoy RBAC 的匹配器。支持 dry-run(shadow,只记录不拦截,用于灰度验证策略)。CUSTOM 走 ext_authz filter。又一次"Istio 高层 CRD → Envoy filter 配置"的翻译——这正是控制面的核心工作。
L08

第 3 周收官 🎓

🧠 第 3 周(sidecar 与安全)你已掌握

  • Day 11 Sidecar 注入:MutatingWebhook + 模板渲染 + iptables 劫持
  • Day 12 pilot-agent:拉起 Envoy + 本地 SDS + xDS 代理 + 探针改写
  • Day 13 Envoy bootstrap 生成:模板渲染 + node metadata + Higress 模板
  • Day 14 mTLS/CA:SPIFFE 身份 + 内建 CA + JWT 认证 + 证书轮转 + 信任链
  • Day 15 SDS 分发 + PeerAuthentication(mTLS 模式)+ AuthorizationPolicy(→RBAC)

你现在理解了 Istio 数据面的"落地机制":sidecar 怎么进 Pod、agent 怎么托管 Envoy、mTLS 和授权怎么自动实现。第 4 周进入遥测、EnvoyFilter(Higress 重用)、istioctl、安装,并收官。

✋ 动手

cd /Users/bitmart/work/codes/github/higress-group/istio
grep -n 'func toEnvoySecret\|func.*generate' security/pkg/nodeagent/sds/sdsservice.go
grep -n 'ComposePeerAuthentication\|func BuildInboundTLS' pilot/pkg/security/authn/policy_applier.go pilot/pkg/security/authn/utils/utils.go
grep -n 'func New\|func (b Builder) build' pilot/pkg/security/authz/builder/builder.go
明天预告 · Day 16(第4周开始)遥测——Istio 怎么采集 metrics/trace?Telemetry API 怎么翻译成 Envoy 的 stats filter,与 Prometheus 集成。
← Day 14 mTLS/CA Day 16 · 遥测 →