阶段一 · 容器与集群安全

网络策略与零信任

一句话总结

K8s 默认所有 Pod 之间网络全通, 任何 Pod 可以访问任何其他 Pod.
NetworkPolicy 实现"默认拒绝 + 白名单放行", 限制攻击者在集群内的横向移动能力.
配合 mTLS 实现零信任: 不信任任何网络位置, 每个请求都需要身份验证.

K8s 默认网络: 全平面互通

K8s 网络模型的设计原则: 每个 Pod 有独立 IP, 所有 Pod 之间直接可达(无 NAT). 这对开发者很方便, 但对安全是灾难:

默认状态:
前端 Pod ──→ 任意 Pod
后端 Pod ──→ 任意 Pod(包括数据库)
数据库 Pod ──→ 任意 Pod(甚至对外)
攻击者入侵前端 Pod → 可以直接访问数据库 Pod

期望状态:
前端 Pod ──→ 只能访问后端
后端 Pod ──→ 只能访问数据库
数据库 Pod ──→ 不能主动对外连接
攻击者入侵前端 → 只能横向到后端, 无法直达数据库

默认网络 = 开放式办公室, 任何人都能走到任何工位.
NetworkPolicy = 门禁系统, 每个区域需要刷对应的门禁卡.
零信任 = 不仅要刷卡, 进门后每次操作还要验证身份证(mTLS).

NetworkPolicy 基础

基本结构

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector: # 这条策略作用于哪些 Pod
matchLabels:
app: backend
policyTypes:
- Ingress # 控制入站流量
- Egress # 控制出站流量
ingress: # 允许谁访问我
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
protocol: TCP
egress: # 我能访问谁
- to:
- podSelector:
matchLabels:
app: database
ports:
- port: 5432
- to: # 允许 DNS 查询
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP

关键逻辑

  • 没有 NetworkPolicy → 全通(默认允许)
  • 一旦有 NetworkPolicy 选中了 Pod → 该方向默认拒绝, 只有规则里列出的才放行
  • 多条 NetworkPolicy 是累加关系(OR), 不是覆盖
  • 如果只写了 Ingress 规则没写 Egress → Egress 仍然全通(反之亦然)

必须先确认 CNI 支持 NetworkPolicy

NetworkPolicy 只是 K8s API 对象, 实际执行靠 CNI 插件. 并非所有 CNI 都支持: Flannel 不支持, Calico / Cilium / Weave 支持.

如果集群用 Flannel, 写了 NetworkPolicy 不会报错, 但也不会生效, 这是最危险的"安全错觉".

默认拒绝策略

安全的第一步: 先全部拒绝, 再按需放行.

# 默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 选中 namespace 内所有 Pod
policyTypes:
- Ingress
# ingress 字段为空 = 不允许任何入站
---
# 默认拒绝所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
# egress 字段为空 = 不允许任何出站

默认拒绝 Egress 会断 DNS

Pod 连 DNS 查询都做不了, 服务发现直接瘫痪. 部署 default-deny-egress 后必须立刻添加允许 DNS 出站的规则(如上面 backend-policy 中的 DNS egress 规则).

建议先在非生产环境验证.

Namespace 隔离

典型的微服务集群有多个 Namespace: production / staging / monitoring / istio-system. 不同 Namespace 之间应该限制互访.

# 只允许同 Namespace 内的 Pod 互相访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # 同 namespace 的任何 Pod

---
# 允许来自 monitoring namespace 的 Prometheus 抓取
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-prometheus-scrape
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- port: 9090

常见网络策略模式

场景 策略
数据库只接受后端访问 Ingress: from podSelector app=backend, port 5432
前端只能访问后端和外部 CDN Egress: to podSelector app=backend + to ipBlock CDN 地址段
禁止 Pod 访问云 Metadata API Egress: deny to ipBlock 169.254.169.254/32
允许 Ingress Controller 访问所有后端 Ingress: from namespaceSelector ingress-nginx

务必阻止访问云 Metadata API

AWS/GCP/Azure 的 Instance Metadata API(169.254.169.254)包含 IAM 临时凭证. 如果 Pod 被攻破且未限制出站 → 攻击者直接获取云平台权限, 这是真实攻击中最常见的提权路径.

# 阻止访问云 Metadata API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-metadata-api
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # 阻止 Metadata API

零信任网络

NetworkPolicy 基于网络层(IP/Port)控制, 但攻击者可能冒充 Pod IP. 零信任要求每个请求都验证身份, 不信任网络位置.

mTLS(Service Mesh)

在微服务 08 篇中讲过, Istio 自动为每个 Pod 签发证书, 服务间通信全部 mTLS 加密:

  • 通信加密(防窃听)
  • 双向身份认证(我知道对方是谁, 对方知道我是谁)
  • 配合 AuthorizationPolicy 实现身份级别的访问控制

网络安全分层

第 1 层: NetworkPolicy(网络层 L3/L4)
→ 限制哪些 IP/端口可以通信

第 2 层: mTLS(传输层)
→ 加密 + 身份验证

第 3 层: AuthorizationPolicy(应用层 L7)
→ 基于身份 + 路径 + 方法的细粒度控制

三层组合 = 零信任网络

NetworkPolicy vs Service Mesh AuthorizationPolicy

两者不冲突, 而是互补.
NetworkPolicy 是网络层防火墙, 即使没有 Service Mesh 也能用, 但粒度只到 IP/Port.
AuthorizationPolicy 是应用层策略, 基于服务身份(ServiceAccount)和 HTTP 属性(路径/方法), 但需要 Mesh.

最安全的做法: 两者都配置.

快速回顾

  • K8s 默认全通: 任何 Pod 可以访问任何 Pod, 必须主动限制
  • 默认拒绝 + 白名单: 先 default-deny, 再按需放行, 是安全基本原则
  • CNI 支持: 确认集群 CNI 支持 NetworkPolicy(Calico/Cilium), Flannel 不支持
  • DNS 出站: 默认拒绝 Egress 后必须单独放行 DNS(53/UDP)
  • 阻止 Metadata API: 169.254.169.254 是云平台最高风险的提权路径
  • 零信任: NetworkPolicy + mTLS + AuthorizationPolicy 三层叠加

动手练习

  1. 验证默认全通: 在测试 Namespace 部署 3 个 Pod(frontend/backend/database), 验证默认可以互相 curl
  2. 默认拒绝策略: 部署 default-deny-ingress + default-deny-egress, 验证所有通信断开
  3. 白名单放行: 逐步添加 NetworkPolicy 只允许 frontend → backend → database 的链路, 验证前端无法直连数据库
  4. 阻止 Metadata API: 添加阻止 169.254.169.254 的 Egress 规则, 从 Pod 内 curl metadata API 验证被拒绝