Kubernetes NetworkPolicy 实战:默认全通的坑与用标签做零信任隔离

Kubernetes NetworkPolicy 实战:默认全通的坑与用标签做零信任隔离

很多人第一次意识到 K8s 网络的默认行为时都会愣一下:同一个集群里,任意 Pod 都能访问任意其他 Pod。你的前端 Pod 能直连数据库 Pod,一个被攻破的边缘服务能横向扫遍整个命名空间。生产环境里这几乎等于没有网络隔离。

NetworkPolicy 就是给 Pod 之间的流量加防火墙规则的。这篇从「默认全通」的现场讲起,一步步收敛到「默认拒绝 + 白名单放行」的零信任模型。

前提:你的 CNI 得支持 NetworkPolicy

先泼一盆冷水:NetworkPolicy 是个「声明」,真正执行它的是 CNI 插件。如果你的集群用的是不支持 NetworkPolicy 的 CNI(比如默认配置的 flannel),你写的策略会被 API Server 乖乖收下,然后完全不生效------这是最坑的地方,你以为隔离了,其实没有。

支持的 CNI:Calico、Cilium、Weave Net 等。快速自查:

bash 复制代码
# 看用的什么 CNI
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave|flannel'

如果是 flannel,要么换 CNI,要么叠加 Calico 的 policy-only 模式。下面的例子假设你用的是 Calico/Cilium。

先看默认全通的现场

建两个 Pod,一个当「数据库」,一个当「攻击者」,验证默认能不能通:

bash 复制代码
kubectl create ns demo
kubectl run db --image=nginx --labels="app=db" -n demo
kubectl run attacker --image=busybox -n demo --command -- sleep 3600

拿到 db 的 IP,从 attacker 去连:

bash 复制代码
DB_IP=$(kubectl get pod db -n demo -o jsonpath='{.status.podIP}')
kubectl exec -n demo attacker -- wget -qO- --timeout=3 http://$DB_IP

会正常返回 nginx 首页 HTML。attacker 和 db 毫无关系,却能直连------这就是默认全通。

第一步:命名空间级默认拒绝

零信任的第一原则是「先关死,再开口子」。给整个命名空间加一条「拒绝所有入站流量」的兜底策略:

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: demo
spec:
  # podSelector 为空 = 选中命名空间内所有 Pod
  podSelector: {}
  policyTypes:
    - Ingress   # 只管入站;不写 Ingress 规则 = 拒绝所有入站

关键理解 NetworkPolicy 的语义:

  • 一旦有任何策略选中了某个 Pod,该 Pod 就从「默认全通」切换到「默认拒绝,只放行被明确允许的」。
  • podSelector: {} 匹配命名空间内所有 Pod。
  • policyTypes: [Ingress] 且没有 ingress: 规则 → 所有入站被拒。

应用后再连一次:

bash 复制代码
kubectl apply -f default-deny.yaml
kubectl exec -n demo attacker -- wget -qO- --timeout=3 http://$DB_IP
# wget: download timed out ------ 通了,现在被拒了

第二步:只放行该放行的

现在 db 谁都连不上,包括合法的后端服务。我们只想让app=backend 标签的 Pod 访问 db 的 80 端口。用标签精确开口子:

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-db
  namespace: demo
spec:
  podSelector:
    matchLabels:
      app: db        # 这条策略作用在 db 上
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend   # 只允许 app=backend 的 Pod
      ports:
        - protocol: TCP
          port: 80

验证:attacker(没有 backend 标签)仍然连不上,而新建一个带 app=backend 的 Pod 就能连:

bash 复制代码
kubectl run backend --image=busybox -n demo --labels="app=backend" \
  --command -- sleep 3600
kubectl exec -n demo backend -- wget -qO- --timeout=3 http://$DB_IP
# 正常返回 nginx 首页 ------ 白名单放行成功

这就是零信任:基于身份(标签)而非 IP 放行。Pod IP 会随重建变化,但标签是稳定的语义身份。

坑一:from 里 namespaceSelector 和 podSelector 的「与/或」

跨命名空间放行时最容易踩的坑------下面这两种写法含义完全不同:

yaml 复制代码
# 写法 A:一个 from 元素里同时有两个 selector = 「与」
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            env: prod
        podSelector:
          matchLabels:
            app: backend
# 含义:必须是 env=prod 命名空间里、且 app=backend 的 Pod

# 写法 B:两个 from 元素 = 「或」
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            env: prod
      - podSelector:
          matchLabels:
            app: backend
# 含义:env=prod 命名空间的任意 Pod,或 本命名空间里 app=backend 的 Pod

写法 B 里第二个 podSelector 只作用于本命名空间,不会扩到 prod 命名空间。想「prod 命名空间里的 backend」必须用写法 A 那种同一元素内的组合。这个「短横线位置决定与/或」的坑,配错了要么放行过宽、要么该通的不通。

坑二:别忘了出站(Egress)和 DNS

上面只管了 Ingress。如果你还要加 default-deny-egress 锁死出站,会立刻发现一个惊喜:Pod 连 DNS 都解析不了了,因为 DNS 查询(到 kube-dns/CoreDNS 的 53 端口)也是出站流量,被一起拒了。

加 egress 默认拒绝时,记得放行 DNS:

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: demo
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector: {}   # 允许到 kube-system 里的 CoreDNS
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

不放行 53 端口的话,应用里所有 http://service-name 这种域名访问都会失败,而且报错常常是「connection timeout」而非「DNS 错误」,排查起来很误导。

小结

  • K8s 默认全通,同集群任意 Pod 互访;NetworkPolicy 是给 Pod 间流量加防火墙,但必须 CNI 支持(Calico/Cilium 可,默认 flannel 不可,且不生效还不报错)。
  • 零信任套路:先 default-deny-ingress 关死,再用 podSelector 按标签开白名单;基于标签身份放行,而非易变的 Pod IP。
  • 记牢「from 里短横线决定与/或」:同一元素内多个 selector 是「与」,多个元素是「或」,配错直接导致放行范围错。
  • 加 egress 默认拒绝时,第一件事是放行 53 端口的 DNS,否则所有域名访问静默超时。
  • 一句话记忆:NetworkPolicy 是白名单模型------只要有一条策略选中了 Pod,没被明确允许的流量就一律拒绝。
相关推荐
Crazy________1 小时前
Redis02:库切换、监控与安全配置
linux·redis·云原生·mybatis
Eloudy1 小时前
给 Linux 加新硬盘的挂载命令
linux·运维·服务器
三言老师2 小时前
ss高效查看网络连接端口实操
linux·运维·服务器·网络
三言老师2 小时前
traceroute路由追踪实操
linux·运维·服务器·网络
Nayxxu2 小时前
Gemini Computer Use 安全清单:浏览器、桌面、移动端自动化怎么控权限
运维·安全·自动化
愚公搬代码2 小时前
【愚公系列】《WorkBuddy从上手到变现》017-用AI Agent实现公众号自动化运营(从1个号到矩阵:规模化的可能和边界)
运维·人工智能·自动化·小龙虾·workbuddy
XH华2 小时前
Linux系统第四章:工具的学习
linux·运维·学习
刚入门的大一新生2 小时前
Linux-基础开发工具1
linux·运维·服务器
ai_coder_ai3 小时前
平台工程(Platform Engineering)在企业数字化中的应用
云原生