第28章 Egress 与出网治理:出口网关、registry mirror、审计
学习目标
- 理解为什么要治理 K8s 出网:安全、合规、稳定(固定出口 IP)、成本
- 掌握五种出网治理手段:NetworkPolicy、出口网关、正向代理、FQDN 策略、镜像缓存
- 理解"按域名管控出网"为什么难,及 Cilium FQDN/Istio ServiceEntry 的解法
- 会为第三方白名单需求实现"固定出口 IP"
前置知识
- 第17章 Squid 正向代理、第01章 正向代理、第25章 Docker registry mirror
- 网络手册 · NetworkPolicy、k8sLab · NetworkPolicy 与零信任
原理
为什么治理出网(Egress)
第27章 管"进",本章管"出"。默认情况下 K8s 的 Pod 可以访问任意外部地址——这在生产是危险的:
- 安全:被入侵的 Pod 可向外传数据(数据外泄)、连 C2(命令控制)
- 合规:需要审计"哪个服务访问了哪些外部地址"
- 稳定:第三方 API 要求白名单固定 IP,但 Pod 出网 IP 是节点 IP、不固定
- 成本:重复拉取外部资源(镜像/依赖)浪费带宽——用缓存
手段一:NetworkPolicy egress(L3/L4,按 IP)
K8s 原生的 NetworkPolicy 能限制 Pod 的出站,但只在 L3/L4(IP/CIDR/端口):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: restrict-egress }
spec:
podSelector: { matchLabels: { app: myapp } }
policyTypes: [Egress]
egress:
- to: [{ ipBlock: { cidr: 203.0.113.0/24 } }] # 只允许访问这个外部网段
ports: [{ protocol: TCP, port: 443 }]
- to: [{ namespaceSelector: {} }] # 允许集群内
ports: [{ protocol: UDP, port: 53 }] # DNS
致命局限:NetworkPolicy 不能按域名。因为它工作在 IP 层,而域名对应的 IP 经常变(CDN、云服务)。"只允许访问 api.github.com"这种需求,原生 NetworkPolicy 做不到——这引出了下面的方案。
手段二:按域名管控(FQDN 策略 / ServiceEntry)
要按域名管控出网,需要 DNS 感知的方案:
- Cilium FQDN Policy:Cilium 用 eBPF 拦截 DNS 响应,学习"域名→IP"映射,从而支持
toFQDNs: api.github.com(网络手册 · Cilium/eBPF)
# CiliumNetworkPolicy:按域名放行(原生 NetworkPolicy 做不到)
egress:
- toFQDNs: [{ matchName: "api.github.com" }]
- toEndpoints: [{}] # 配合放行 DNS
toPorts: [{ ports: [{ port: "53", protocol: UDP }] }]
- Istio ServiceEntry:把外部服务"注册"进 mesh,从而能对它做路由、监控、策略——出网也纳入 sidecar 治理
手段三:正向代理出网(呼应 Squid)
让所有 Pod 配 HTTP_PROXY 指向集群内的正向代理(Squid 等,第17章),统一出网口:
Pod(HTTP_PROXY=squid.egress.svc:3128)
│
▼ 所有出网走 Squid
[集群内 Squid 正向代理] ──审计/缓存/ACL──▶ 外部
好处:统一审计(access.log)、域名级 ACL、缓存、认证——把企业出网那套(第17章)搬进集群。注意 NO_PROXY 必须含集群内域名(.svc、.cluster.local、Pod/Service CIDR、169.254.169.254),否则内部调用也走代理 → 全超时(第08章、第25章)。
手段四:出口网关 + 固定出口 IP
第三方 API 要白名单时,需要所有 Pod 出网都从一个固定 IP 出去。做法是 Egress Gateway:把出网流量都汇聚到一组专用网关节点/Pod,在那里做 SNAT 成固定 IP。
各业务 Pod(出网 IP 随机=节点 IP)
│ 路由/mesh 强制走出口网关
▼
[Egress Gateway 节点] ──SNAT 成固定 IP 203.0.113.10──▶ 第三方(白名单认这个 IP)
- Istio Egress Gateway:mesh 内强制出网流量经过专用 gateway,统一出口、统一策略、可观测
- Cilium Egress Gateway:用 eBPF 把选定 Pod 的出网 SNAT 到固定 IP
手段五:镜像与依赖缓存(registry mirror)
重复拉镜像/依赖浪费带宽,用 pull-through cache / registry mirror(第25章)——本质是给 registry 做反向代理+缓存(第17章 缓存思想)。常配合私有 Harbor 的 proxy cache。
一张表:选哪种
| 需求 | 手段 |
|---|---|
| 按 IP/端口限制出网 | NetworkPolicy egress |
| 按域名白名单 | Cilium FQDN / Istio ServiceEntry |
| 统一审计 + 缓存 + 认证 | 集群内正向代理(Squid) |
| 固定出口 IP(第三方白名单) | Egress Gateway + SNAT |
| 镜像/依赖加速 | registry mirror / pull-through cache |
️ 实现 / 命令
实验一:NetworkPolicy 默认拒绝出网 + 白名单
# 1) 默认拒绝所有出站(零信任起点)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny-egress }
spec:
podSelector: {}
policyTypes: [Egress]
egress: [] # 空 = 全拒
kubectl apply -f default-deny-egress.yaml
kubectl exec mypod -- curl -m 3 https://example.com # 超时(被拒)
# 再叠加上面的 restrict-egress 放行特定网段 + DNS
实验二:Pod 走集群内 Squid 出网
# 给 Pod 注入代理环境变量(NO_PROXY 必须含集群内!)
env:
- { name: HTTP_PROXY, value: "http://squid.egress.svc.cluster.local:3128" }
- { name: HTTPS_PROXY, value: "http://squid.egress.svc.cluster.local:3128" }
- { name: NO_PROXY, value: "localhost,127.0.0.1,.svc,.cluster.local,10.0.0.0/8,169.254.169.254" }
# 在 Squid 上审计每个 Pod 的出网
kubectl logs -n egress squid-0 | tail
# ... 10.244.1.5 TCP_MISS/200 GET http://example.com/ ← 哪个 Pod 访问了哪里,一目了然
实验三:Cilium 按域名放行
kubectl apply -f - <<'EOF'
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata: { name: allow-github }
spec:
endpointSelector: { matchLabels: { app: ci } }
egress:
- toFQDNs: [{ matchName: "api.github.com" }]
- toEndpoints: [{ matchLabels: { "k8s:io.kubernetes.pod.namespace": kube-system, "k8s-app": kube-dns } }]
toPorts: [{ ports: [{ port: "53", protocol: UDP }] }]
EOF
# 此后该 Pod 只能访问 api.github.com(按域名),其余被拒
排错
| 现象 | 根因 | 解决 |
|---|---|---|
| 加了 egress 策略后 DNS 全挂 | 没放行 53 端口到 kube-dns | egress 放行 UDP/TCP 53 |
| 按域名策略不生效 | 用了原生 NetworkPolicy(不支持域名) | 换 Cilium FQDN / Istio |
| Pod 配代理后内部调用超时 | NO_PROXY 没含集群内 | 补 .svc/CIDR/元数据(第25章) |
| 第三方仍看到多变 IP | 没走出口网关 | 配 Egress Gateway + SNAT 固定 IP |
| FQDN 策略偶尔漏放行 | DNS TTL/IP 变化窗口 | 调 DNS 策略、缩短缓存 |
| 元数据 169.254.169.254 被滥用 | 出网没限制元数据 | 阻断或限制其访问(第33章 SSRF) |
本章小结
- 治理出网是企业 K8s 的安全刚需:安全、合规、固定出口 IP、成本。
- 五手段:NetworkPolicy(按 IP/L4)、FQDN/ServiceEntry(按域名)、正向代理(审计+缓存)、Egress Gateway(固定出口 IP)、registry mirror(缓存)。
- 原生 NetworkPolicy 不能按域名(IP 会变),按域名要 Cilium FQDN / Istio。
- Pod 走集群内 Squid 时
NO_PROXY必须含集群内域名/网段,否则内部调用全超时。
下一章 第29章 Service Mesh 数据面,把本篇所有要素(sidecar 劫持 + Envoy + mTLS + xDS + L7)串成完整的东西流量治理全链路——容器与 K8s 篇的收官。