
1. 答案先说默认全放行策略靠“建”不靠“默认”先把结论摆在这里Kubernetes 中 Pod 之间的默认网络策略是“允许所有”也就是全部放行。只要你没有显式创建任何 NetworkPolicy 资源集群内任意两个 Pod 之间都可以自由通信不管它们在哪个节点、哪个命名空间。这个问题我在面试里被人问过也在生产环境踩过坑。很多人一听到“防火墙”“网络策略”这些词下意识会以为 Kubernetes 天生自带一套类似 VMware 安全组、AWS Security Group 那样的默认拒绝机制。但这个理解是错的。Kubernetes 本身只是一个编排平台它定义了 NetworkPolicy 这套 API 资源但“策略是否真正执行”取决于你装了什么 CNI 网络插件而“默认是否拒绝”则取决于你有没有创建策略对象。打个比方Kubernetes 就像小区物业NetworkPolicy 就是物业贴出的管理规定。规定没贴出来之前业主之间互相串门不受限制规定贴出来了才按照白名单放行。如果你想让 Pod 之间默认互不相通必须主动创建策略把“默认允许”变成“默认拒绝”。什么都不写就等于裸奔。所以这个问题的完整答案其实是两句话默认行为Kubernetes 集群内 Pod 之间允许所有通信。安全建议生产环境务必通过 NetworkPolicy 或服务网格等手段把默认策略收紧为白名单制。接下来我会把“为什么默认是允许”“策略是怎么生效的”“怎么一步步收紧策略”“踩过的坑有哪些”全部拆开讲一遍。2. 为什么默认是“允许”这要从 Kubernetes 的网络模型说起2.1 扁平网络每个 Pod 都是“一台独立主机”Kubernetes 的网络模型有一条硬性规定所有 Pod 共享一个扁平的、无 NAT 的地址空间。也就是说Pod 的 IP 在集群内部都是真实可达的不需要做地址转换也不需要经过什么网关代理。这个设计的好处是服务发现和流量编排变得极其简单。A Pod 要访问 B Pod源 IP 直接就是 A 的 IPB 返回的包也能直接回到 A。负载均衡、监控、链路追踪这些工具在这种模型下都能天然工作。但代价就是网络层面没有任何天然的隔离屏障。Pod 和 Pod 之间“裸奔”是默认状态。你可以把 Kubernetes 集群想象成一个大型局域网每台服务器都直接接在同一台交换机上所有端口互通。默认情况下没有 ACL没有 VLAN 隔离任何两台机器都能互相 ping 通、互相访问端口。Kubernetes 的默认网络就是这个样子。2.2 为什么不默认加一条“拒绝所有”规则这是一个很关键的“为什么”。很多安全导向的设计者第一次接触 Kubernetes 时都会问为什么默认不是 deny all难道不应该像防火墙那样先拒绝一切再逐步放行吗答案涉及到 Kubernetes 早期的设计哲学和生态兼容性。Kubernetes 在 1.3 版本引入 NetworkPolicy 时社区面对的现实是市面上有大量 CNI 插件Flannel、Weave、Calico、Cilium、Kube-Router各自实现方式完全不同。有的插件天生不支持策略有的插件只支持部分策略。如果 Kubernetes 默认就带一条“拒绝所有”规则那么所有不支持 NetworkPolicy 的插件都会直接瘫痪——集群里任何流量都走不通。所以 Kubernetes 选择了最稳妥的方案默认不给任何限制策略由用户显式声明由 CNI 插件执行。你创建了 NetworkPolicy支持策略的插件就来执行你不创建那大家就保持互通。这是为了兼容生态、保证集群开箱即用的妥协也是今天这个“默认允许”的根源。2.3 一个常见误区命名空间不等于隔离略微接触过 Kubernetes 的人还会有一个误解既然有命名空间Namespace这个概念是不是把 Pod 放在不同命名空间里它们之间天然就隔离了并不是。命名空间只是逻辑上的分组用于资源管理和权限控制不提供网络隔离。不同命名空间里的 Pod 默认依然可以互相访问。你创建一个 Pod 在 ns-a另一个 Pod 在 ns-b只要你知道对方的 IP 或 Service 名称直接就能访问。想通过命名空间做网络隔离还是得靠 NetworkPolicy 里的 namespaceSelector 去声明。3. NetworkPolicy 到底怎么工作核心机制一次讲透3.1 策略对象长什么样NetworkPolicy 是一个 Kubernetes API 资源最简形式长这样apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: default spec: podSelector: {} policyTypes: - Ingress这个 YAML 的含义是在 default 命名空间内对所有 PodpodSelector 为空对象表示匹配全部应用一条 Ingress 策略。policyTypes 里只写了 Ingress但注意只要 policyTypes 里包含 Ingress且没有填写任何 ingress 规则这条策略就会拒绝所有入站流量。这里有一个重要的语义要理解NetworkPolicy 不是“防火墙规则列表”那种“先匹配先执行”的模式而是白名单模型。每一条 ingress 规则相当于一个“允许条件”只要流量匹配任意一个条件就放行如果策略存在但没有任何允许条件那所有流量都被拒绝。3.2 匹配字段podSelector、namespaceSelector、ipBlockNetworkPolicy 的规则匹配有三种维度掌握了这三种就能拼出绝大部分场景podSelector选择当前命名空间内的某个或某组 Pod。写法是标准的 label selector比如app: nginx就只匹配打了这个标签的 Pod。namespaceSelector选择来源Ingress或目标Egress所在的命名空间。这个字段配合同命名空间内某个 Pod 选择器可以实现“只允许来自某个命名空间的流量访问我”。ipBlock直接按 IP 段匹配。这个字段比较底层一般用于匹配集群外部的 IP 段、节点 IP 段或者某些无法通过标签选中的场景。注意ipBlock 匹配的是源 IP 或目标 IP在 Kubernetes 网络模型下Pod 的 IP 是多变的所以能用标签解决的就别用 IP。3.3 Ingress 和 Egress两个方向分开控制NetworkPolicy 支持两个方向Ingress入站和 Egress出站。入站策略控制“谁能访问我”出站策略控制“我能访问谁”。以防火墙来类比Ingress 相当于入站规则限制外部发往本机的流量Egress 相当于出站规则限制本机发往外部的流量。很多人在配置时只关注了 Ingress忘了 Egress结果被各种“诡异”的访问失败折腾半天。举个真实例子我给一个后端服务配了“只允许前端访问”的 Ingress 策略后端 Pod 的入站流量确实被限制住了。但随后我发现后端 Pod 一直报错无法连接数据库。排查了半天才意识到我只限制了入站没有限制出站。但这里的核心问题是——当你为一个 Pod 创建了任何一条包含 Egress 的 NetworkPolicy 时这个 Pod 的所有出站流量默认都会被拒绝除非你在 egress 规则里显式列出允许的目标。这个行为和 iptables 的默认策略非常像一旦某条链上有规则了未匹配的包就走默认策略而 Kubernetes 里“有策略即默认拒绝”。3.4 policyTypes 的隐藏含义再说一个容易被忽略的点。policyTypes 字段里的 Ingress / Egress不仅仅表示“我要配置这个方向的策略”更关键的是它决定了默认兜底行为如果 policyTypes 里包含 Ingress那么未匹配任何 ingress 规则的入站流量全部拒绝。如果 policyTypes 里包含 Egress那么未匹配任何 egress 规则的出站流量全部拒绝。如果 policyTypes 里两个都没写插件会默认按 spec 中是否有 ingress / egress 字段来推断。这也就解释了为什么很多“策略不生效”或者“网络完全断掉”的故障都是因为多写了或漏写了 policyTypes 导致的。你本来只想加一条白名单规则但一旦 policyTypes 里出现了 Egress你就等于同时把出站全部封锁了。4. 实操从“默认全放行”一步步收紧到白名单制4.1 环境准备确认你的 CNI 支持策略动手之前先确认一个关键前提你的 CNI 插件必须支持 NetworkPolicy。常见的支持情况如下CNI 插件是否支持 NetworkPolicy备注Calico支持策略能力最强支持全局策略、命名空间策略Cilium支持基于 eBPF支持 L3-L7 层策略Weave Net支持支持基础策略Flannel不支持默认仅提供 Overlay 网络无策略能力Kube-Router支持基于 iptables 实现如果你的集群用的 Flannel而且没装额外组件那后面的所有配置都不会生效。你创建 NetworkPolicy 不会报错但流量照样全放行——因为底层插件根本不认识这套 API直接忽略掉。这是生产环境最容易被忽视的隐患之一。验证方法也很简单创建一条“拒绝所有入站”的策略后从一个 Pod 去访问另一个 Pod如果依然能通就说明 CNI 不支持或者组件没装对如果立刻不通说明策略生效了。4.2 第一步建立全命名空间默认拒绝策略在给业务配置精细白名单之前我的习惯是先铺一层“安全网”——所有命名空间先默认拒绝入站。这样即使有人忘了给新服务配策略至少它不会被外部随意访问。apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: your-namespace spec: podSelector: {} policyTypes: - Ingress把这个 YAML apply 进去your-namespace下的所有 Pod 就都“关上门”了。此时任何入站流量都会被拒绝包括来自同命名空间其他 Pod 的流量、来自其他命名空间的流量、来自 Service 的流量这一点尤其要注意。不过集群内部访问 kubelet 的健康检查探针流量有时不在 NetworkPolicy 管辖范围内但这不是你该依赖的。4.3 第二步放行某组 Pod 给另一组 Pod 访问现实中常见的拓扑是前端 Nginx 要访问后端 API后端 API 要访问数据库前端不能直接访问数据库。我们用三个 Deployment 和一套策略把它模拟出来。先部署测试服务kubectl create deployment nginx-front --imagenginx kubectl create deployment api-backend --imagenginx kubectl create deployment db-backend --imagenginx给每个 Deployment 打上标签方便策略匹配kubectl label deployment nginx-front appfront tierweb kubectl label deployment api-backend appapi tierbackend kubectl label deployment db-backend appdb tierdata现在创建一条策略允许appfront的 Pod 访问appapi的 Pod 的 80 端口apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-front-to-api namespace: default spec: podSelector: matchLabels: app: api policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: front ports: - protocol: TCP port: 80这条策略的语义是匹配appapi的 Pod只允许来自appfront的 Pod 访问其 80 端口。其他来源、其他端口一律拒绝。验证连通性。进入 front Podkubectl exec -it deploy/nginx-front -- curl http://api-backend-svc:80如果通了说明策略放行生效。再试着从 db Pod 去访问 api Pod 的 80 端口kubectl exec -it deploy/db-backend -- curl http://api-backend-svc:80如果这个请求超时或被拒绝说明默认拒绝确实生效了。4.4 第三步精细化——限制访问特定端口很多人会把 NetworkPolicy 的 port 字段忽略掉但生产环境强烈建议加上。原因很简单如果你只限制来源、不限制端口那等于告诉对方“你只要能连上我什么服务都能直接用”。比如上一步的规则如果去掉ports字段appfront的 Pod 就不仅能访问你的 80 端口还能访问 Redis 的 6379、MySQL 的 3306哪怕这些端口根本没有对外提供 Service只要 Pod IP 直达就都能碰到。把端口加入策略后即使来源匹配端口不匹配也会被拒绝。这就像防火墙的“五元组”规则源 IP、目标 IP、源端口、目标端口、协议全部匹配才算放行。4.5 第四步跨命名空间隔离如果要实现“只允许前端命名空间访问后端命名空间”就需要用到 namespaceSelectorapiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-web-ns-to-api namespace: backend spec: podSelector: matchLabels: app: api policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: env: web podSelector: matchLabels: app: front注意这里的写法from下面既有 namespaceSelector 又有 podSelector它们之间是AND关系。也就是“来自 envweb 这个命名空间并且 pod 标签为 appfront 的 Pod”。如果你在 from 下面写两个独立条目那就是 OR 关系。这个细节特别容易出错。很多人想表达“来自某个 namespace 的某个 Pod”结果把两个字段写成了两条独立规则导致放行范围远超预期。我实际遇到过有人因为这里写错把整个命名空间下所有 Pod 都放行到了生产数据库。4.6 第五步出站策略Egress——控制 Pod 能访问谁出站策略通常用在两个场景一是防止 Pod 被入侵后向内网横向扩散二是防止 Pod 主动外连比如矿池、数据外传。一个严格控出站的 Pod等于既锁了门又锁了窗。下面这条策略允许appapi的 Pod 只访问appdb的 Pod 的 5432 端口以及 kube-system 命名空间下 kube-dns 的 53 端口域名解析其他出站全拒绝apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: api-egress namespace: default spec: podSelector: matchLabels: app: api policyTypes: - Egress egress: - to: - podSelector: matchLabels: app: db ports: - protocol: TCP port: 5432 - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53这里有一个很多人第一次配置时必踩的坑如果没有放行 DNSPod 就解析不了域名所有依赖 Service 名称的访问都会失败。因为 Service 域名解析依赖 CoreDNSCoreDNS 跑在 kube-system 命名空间你的出站策略一旦把 Pod 的出站全部掐断DNS 查询的 UDP 53 包也发不出去。所以配置 Egress 时必须记得给 CoreDNS 留一条放行规则。4.7 完整的“三层隔离”策略组合实例把前面几步拼起来一套比较稳妥的隔离策略如下命名空间Pod 标签Ingress 策略Egress 策略defaultappfront仅允许来自 ingress-nginx 或外部 LB允许访问 appapi 的 80 端口 DNSdefaultappapi仅允许来自 appfront允许访问 appdb 的 5432 DNSdefaultappdb仅允许来自 appapi不允许任何出站如需备份再额外加白名单再加上每个命名空间一条default-deny-all作为兜底这个模型已经能把绝大多数生产环境的横向移动风险挡在门外。5. 常见问题与排查技巧实录5.1 明明创建了策略流量却还是全通这是最常见的问题。第一步先确认 CNI 是否支持 NetworkPolicy。如果用的是 Flannel直接换 Calico 或 Cilium然后再测试。第二步看 NetworkPolicy 是否创建成功kubectl get networkpolicy -n namespace如果显示出来了但流量还是通可能是插件组件的 Pod 没有正常运行比如 calico-kube-controllers 或 calico-node 处于 CrashLoopBackOff。查看组件日志kubectl logs -n kube-system calico-pod-name另一个容易被忽视的点策略匹配的是 Pod不是 Service。如果访问方通过 Service 访问目标流量先到达 Service 的 ClusterIP再转发到后端 Pod。此时 NetworkPolicy 看到的是转发后到达 Pod 的源 Pod IP所以你在策略里写的来源标签必须匹配实际发起访问的 Pod而不是匹配 Service。这也是很多人测试时明明来源没问题却总被拒绝的原因。5.2 启用默认拒绝后健康检查挂了如果给 Deployment 配了 livenessProbe / readinessProbe而这些探针的流量属于 Ingress那默认拒绝策略会把探针也挡在外面。这时候有一个好消息kubelet 的探针流量理论上应该绕过 NetworkPolicy但很多 CNI 插件并没有做这个豁免我实际遇到过 Calico 下探针被策略挡掉的案例。解决思路有两种给探针流量的来源单独放行但来源是节点 IP需要查节点网段用 ipBlock 放行。调整策略粒度别做全命名空间默认拒绝而是只对需要保护的高危服务做精细白名单。我个人更推荐后者。全命名空间默认拒绝虽然视觉上很安全但带来的维护成本和排查成本都很高尤其是当集群里应用很多、探针类型各异的时候。生产环境的策略应该按服务重要性和攻击面来分级不要一刀切。5.3 DNS 解析突然失败症状创建了 NetworkPolicy 后Pod 内 curl http://some-service 能拿到 IP 但连不上或者直接报“Temporary failure in name resolution”。原因几乎总是 Egress 策略没有放行 DNS。解决方案就是像上面那样在 egress 里加一条 UDP 53 的放行规则。这里要额外提醒一句DNS 流量既有 UDP 也有 TCP当响应超过 512 字节时走 TCP建议 UDP 53 和 TCP 53 都放行免得遇到大 DNS 响应时卡住。5.4 策略能匹配但端口写错导致总拒绝端口字段只写数字不带协议字段默认是 TCP。如果你的服务跑在 UDP 上只写端口不写 protocol 就会导致策略永远不匹配。我排查过一个 syslog 采集服务怎么都收不到数据最后发现就是 NetworkPolicy 里写的是默认 TCP而 syslog 走的是 UDP。5.5 大规模集群下策略数量爆表怎么管理当集群里微服务数量多、策略条目动辄上百条时人工维护 YAML 会非常痛苦。我的建议是使用 Helm Chart 或 Kustomize 统一管理策略所有策略纳入版本控制。遵循“命名空间默认拒绝 服务级白名单 全局例外”的三层模型避免每条策略都精确到 Pod。用 Cilium 的 CCNPClusterwide NetworkPolicy处理跨命名空间的全局规则减少重复配置。定期用kubectl get networkpolicy -A导出策略清单人工审计哪些策略已无关联 Pod 使用及时清理。5.6 排查异常流量的实操工具当某个 Pod 之间通信被策略挡掉时靠肉眼看 YAML 有时候很难定位。我的标准排查流程# 1. 查看目标 Pod 的策略 kubectl describe networkpolicy -n namespace # 2. 进入源 Pod 测试连通性先确认是策略问题还是应用问题 kubectl exec -it source-pod -- curl -v target-ip:port # 3. 查看 CNI 插件是否对该流量做了 drop以 Calico 为例直接进节点查 iptables iptables -L -n -v | grep target-pod-ip如果看到链里有一条 DROP 规则且匹配计数在增加说明策略在生效是规则没写对。如果计数为零那流量可能根本没到这台节点需要检查网络插件本身的路由。6. 关于“默认允许”这事我的个人体会把整个问题梳理完之后我想说点题外话。很多人把“Kubernetes 默认允许所有流量”当作一个缺陷甚至一个安全事故但我这些年做集群运维下来的感觉是这更像一个“默认信任需要主动加固”的模型而不是“默认安全”的模型。Kubernetes 把网络的自主权完全交给了使用者你可以选择裸奔也可以选择层层加锁。这种设计给了不同规模、不同安全级别的团队极大的灵活性。如果你现在管理的是一个测试环境Pod 就那么几个跑的都是自己的开发代码那默认允许完全够用强行加策略反而给开发流程添乱。但如果你管理的是生产环境哪怕只有几个服务我都建议至少把默认拒绝策略铺上。这不需要很多成本却能避免很多“我以为别人访问不了”的错觉。另外一个小建议网络策略最好跟着应用一起部署而不是事后补。每次上线新服务都顺手把它的白名单策略写进同一个 Helm Chart 或 Kustomize 目录里形成“新服务默认带策略”的规范。我见过太多团队都是出了问题才想起来安全结果一边救火一边现写规则最后规则写成了筛子。最后再分享一个我常用来验证策略的土办法在集群里跑两个临时 Pod一个带appclient标签一个不带分别去访问目标服务。如果带标签的能通、不带标签的不能通说明策略生效了如果两个都能通说明策略没匹配上如果两个都不能通那多半是默认拒绝把正常流量也挡了。这个测试方法简单粗暴但比看一百遍 YAML 都管用。