尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

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 等。快速自查:# 看用的什么 CNIkubectl get pods-nkube-system|grep-Ecalico|cilium|weave|flannel如果是 flannel,要么换 CNI,要么叠加 Calico 的 policy-only 模式。下面的例子假设你用的是 Calico/Cilium。先看默认全通的现场建两个 Pod,一个当「数据库」,一个当「攻击者」,验证默认能不能通:kubectl create ns demo kubectl run db--imagenginx--labelsappdb-ndemo kubectl run attacker--imagebusybox-ndemo--command--sleep3600拿到 db 的 IP,从 attacker 去连:DB_IP$(kubectl get pod db-ndemo-ojsonpath{.status.podIP})kubectlexec-ndemo attacker --wget-qO---timeout3http://$DB_IP会正常返回 nginx 首页 HTML。attacker 和 db 毫无关系,却能直连——这就是默认全通。第一步:命名空间级默认拒绝零信任的第一原则是「先关死,再开口子」。给整个命名空间加一条「拒绝所有入站流量」的兜底策略:apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:default-deny-ingressnamespace:demospec:# podSelector 为空 选中命名空间内所有 PodpodSelector:{}policyTypes:-Ingress# 只管入站;不写 Ingress 规则 拒绝所有入站关键理解 NetworkPolicy 的语义:一旦有任何策略选中了某个 Pod,该 Pod 就从「默认全通」切换到「默认拒绝,只放行被明确允许的」。podSelector: {}匹配命名空间内所有 Pod。policyTypes: [Ingress]且没有ingress:规则 → 所有入站被拒。应用后再连一次:kubectl apply-fdefault-deny.yaml kubectlexec-ndemo attacker --wget-qO---timeout3http://$DB_IP# wget: download timed out —— 通了,现在被拒了第二步:只放行该放行的现在 db 谁都连不上,包括合法的后端服务。我们只想让带appbackend标签的 Pod访问 db 的 80 端口。用标签精确开口子:apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:allow-backend-to-dbnamespace:demospec:podSelector:matchLabels:app:db# 这条策略作用在 db 上policyTypes:-Ingressingress:-from:-podSelector:matchLabels:app:backend# 只允许 appbackend 的 Podports:-protocol:TCPport:80验证:attacker(没有 backend 标签)仍然连不上,而新建一个带appbackend的 Pod 就能连:kubectl run backend--imagebusybox-ndemo--labelsappbackend\--command--sleep3600kubectlexec-ndemo backend --wget-qO---timeout3http://$DB_IP# 正常返回 nginx 首页 —— 白名单放行成功这就是零信任:基于身份(标签)而非 IP 放行。Pod IP 会随重建变化,但标签是稳定的语义身份。坑一:from 里 namespaceSelector 和 podSelector 的「与/或」跨命名空间放行时最容易踩的坑——下面这两种写法含义完全不同:# 写法 A:一个 from 元素里同时有两个 selector 「与」ingress:-from:-namespaceSelector:matchLabels:env:prodpodSelector:matchLabels:app:backend# 含义:必须是 envprod 命名空间里、且 appbackend 的 Pod# 写法 B:两个 from 元素 「或」ingress:-from:-namespaceSelector:matchLabels:env:prod-podSelector:matchLabels:app:backend# 含义:envprod 命名空间的任意 Pod,或 本命名空间里 appbackend 的 Pod写法 B 里第二个podSelector只作用于本命名空间,不会扩到 prod 命名空间。想「prod 命名空间里的 backend」必须用写法 A 那种同一元素内的组合。这个「短横线位置决定与/或」的坑,配错了要么放行过宽、要么该通的不通。坑二:别忘了出站(Egress)和 DNS上面只管了 Ingress。如果你还要加default-deny-egress锁死出站,会立刻发现一个惊喜:Pod 连 DNS 都解析不了了,因为 DNS 查询(到 kube-dns/CoreDNS 的 53 端口)也是出站流量,被一起拒了。加 egress 默认拒绝时,记得放行 DNS:apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:allow-dns-egressnamespace:demospec:podSelector:{}policyTypes:-Egressegress:-to:-namespaceSelector:{}# 允许到 kube-system 里的 CoreDNSports:-protocol:UDPport:53-protocol:TCPport: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,没被明确允许的流量就一律拒绝。
返回列表