Repository Reading Site
本轮操作记录:NetworkPolicy 与零信任网络实验
上一轮已经讲清了: 这一轮顺着网络主线继续往下,目标变成: 因此,这轮工作的重点是: 1. 先看集群里已有的 NetworkPolicy 现状 2. 再创建隔离实验环境 3. 逐步验证: 4. 最后把实验结果整理成第五课 --- 因为我不希望新实验建立在“未知现状”上。 如果集群里已经有很多策略,你不先看清楚: 已有策略包括: 当前集群已经存在真实的策略案例
本轮操作记录:NetworkPolicy 与零信任网络实验
本轮目标
上一轮已经讲清了:
- Service
- DNS
- kube-proxy
- CNI
- VXLAN / IPIP / WireGuard
这一轮顺着网络主线继续往下,目标变成:
把“网络怎么通”变成“网络该不该通”。
因此,这轮工作的重点是:
- 先看集群里已有的 NetworkPolicy 现状
- 再创建隔离实验环境
- 逐步验证:
- 默认全通
default-deny-ingressallow-same-namespacenamespaceSelectordefault-deny-egressallow DNSallow business traffic
- 最后把实验结果整理成第五课
Step 1: 查看集群里已经有哪些策略
实际命令
KUBECONFIG=~/.kube/config-k8s-lab kubectl get networkpolicy -A -o wide
为什么先看现状
因为我不希望新实验建立在“未知现状”上。
如果集群里已经有很多策略,你不先看清楚:
- 容易误把旧策略效果当成新实验效果
- 容易在错误 namespace 做实验
我看到了什么
已有策略包括:
argocd中多条组件自带策略gitea中 PostgreSQL 相关策略prod中default-deny-ingressprod中allow-same-namespace
我得到的结论
当前集群已经存在真实的策略案例,这说明:
- Calico 的 NetworkPolicy 能力是活的
- 集群并不是“只有教学文件,没有现实策略”
但为了避免和已有环境混淆,我仍然决定创建专门实验 namespace。
Step 2: 读取已有文档和 API 能力
实际命令
sed -n '1,240p' phase-2/03-ingress-networkpolicy.md
KUBECONFIG=~/.kube/config-k8s-lab kubectl get ns --show-labels
KUBECONFIG=~/.kube/config-k8s-lab kubectl api-resources | rg 'networkpol|globalnetworkpolicy|networkset|globalnetworkset'
为什么看这三类内容
现有文档
为了保证本轮实验和仓库主线一致。
namespace label
因为 namespaceSelector 会直接依赖 namespace label。
API 资源
为了确认你的集群里除了标准 NetworkPolicy 外,是否还有 Calico 扩展策略资源。
我看到的关键信息
API 里同时存在:
networking.k8s.io/v1 NetworkPolicycrd.projectcalico.org/v1 NetworkPolicyGlobalNetworkPolicyNetworkSetGlobalNetworkSet
我得到的结论
这说明:
- 标准策略能讲
- Calico 扩展能力也存在
所以第五课里我可以很清楚地区分:
- 标准语义
- Calico 增强语义
Step 3: 为什么要创建两个 namespace
新增对象
我新建了:
np-demonp-external
以及:
np-demo/web+web-svcnp-demo/same-ns-clientnp-external/cross-ns-client
对应文件在:
为什么至少要两个 namespace
因为如果只有一个 namespace,你最多只能演示:
- 同 namespace 放行/拒绝
但你讲不清:
- 跨 namespace 流量
namespaceSelector- 零信任里的环境边界
为什么还要同 namespace 客户端
因为我要同时对比:
- 同 namespace 客户端
- 跨 namespace 客户端
这样你才能真正理解:
- 哪条策略只影响同 namespace
- 哪条策略影响跨 namespace
Step 4: apply 基础实验对象,但先不加策略
实际命令
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply \
-f manifests/05-networkpolicy/00-namespace-np-demo.yaml \
-f manifests/05-networkpolicy/01-namespace-np-external.yaml \
-f manifests/05-networkpolicy/10-web-deployment.yaml \
-f manifests/05-networkpolicy/11-web-service.yaml \
-f manifests/05-networkpolicy/20-same-ns-client.yaml \
-f manifests/05-networkpolicy/21-cross-ns-client.yaml
为什么先只创建工作负载
因为我要先拿到“默认网络行为”的基线。
如果你一上来就把策略一起 apply 了,后面就不知道:
- 默认到底是通还是不通
- 是策略生效了,还是本来就不通
这一步体现的工程原则
做实验时,先建立 baseline,再逐步改单个变量。
Step 5: 等待工作负载 Ready
实际命令
kubectl -n np-demo rollout status deployment/web --timeout=120s
kubectl -n np-demo wait --for=condition=Ready pod/same-ns-client --timeout=120s
kubectl -n np-external wait --for=condition=Ready pod/cross-ns-client --timeout=120s
为什么这一步不能跳
因为如果 Pod 还没 ready,你去测连接:
- 失败了也不知道是策略问题还是 Pod 没起来
这就是典型的变量污染。
我得到的结果
所有基础对象都准备就绪,可以开始做网络访问实验。
Step 6: 先验证默认全通
实际命令
kubectl -n np-demo get pods,svc -o wide
kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc | head -n 1 && echo same-ns=success'
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local | head -n 1 && echo cross-ns=success'
kubectl -n np-external exec cross-ns-client -- nslookup web-svc.np-demo.svc.cluster.local
为什么这组命令同时测三件事
同 namespace HTTP
验证最基本路径。
跨 namespace HTTP
验证默认是否真的跨 namespace 全通。
跨 namespace DNS
把“解析”和“连接”分开验证。
结果
我看到了:
same-ns=successcross-ns=successnslookup正常返回10.107.52.92
我得到的结论
默认情况下,这两个 namespace 之间没有隔离。
这为后面的 default-deny 实验建立了非常清晰的对照组。
Step 7: 应用 default-deny-ingress
实际命令
kubectl apply -f manifests/05-networkpolicy/30-default-deny-ingress.yaml
为什么先从 ingress deny 开始
因为它是最经典、最基础的一刀。
这条策略的意义是:
- 先把目标 namespace 保护起来
应用后怎么验证
我执行了:
kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc >/dev/null && echo same-ns=success || echo same-ns=blocked'
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo cross-ns=success || echo cross-ns=blocked'
kubectl -n np-demo get networkpolicy
结果
我看到了:
same-ns=blockedcross-ns=blocked- 且
wget都是超时
我得到的结论
default-deny-ingress 一旦选中 np-demo 的所有 Pod:
- 同 namespace 也不再天然被允许
- 跨 namespace 更不会被允许
这和很多人的直觉相反,但正好体现了策略语义:
- 被选中的方向,默认不再全通
Step 8: 应用 allow-same-namespace
实际命令
kubectl apply -f manifests/05-networkpolicy/31-allow-same-namespace.yaml
然后再次测试:
kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc >/dev/null && echo same-ns=success || echo same-ns=blocked'
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo cross-ns=success || echo cross-ns=blocked'
kubectl -n np-demo get networkpolicy -o yaml
为什么还要看完整 YAML
因为我要把“策略是累加的”这件事写进文档,而不是只凭现象说。
看完整 YAML 可以确认:
default-deny-ingress还在allow-same-namespace也在
结果
我看到了:
same-ns=successcross-ns=blocked
我得到的结论
这完美证明了:
default-deny没有被覆盖- 新增规则只是往允许集合里加了一项
这就是标准 NetworkPolicy 的“并集允许”模型。
Step 9: 动态给 namespace 打标签,再用 namespaceSelector 放行
实际命令
kubectl label namespace np-external access=allowed --overwrite
kubectl apply -f manifests/05-networkpolicy/32-allow-from-labeled-namespace.yaml
然后验证:
kubectl get ns np-external --show-labels
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo cross-ns=success || echo cross-ns=blocked'
kubectl -n np-demo get networkpolicy
为什么先 label namespace
因为 namespaceSelector 不是按 namespace 名字直接匹配,而是按:
- namespace 的 label
这点必须在实验里让用户真正看见。
结果
我看到了:
np-external标签变成access=allowedcross-ns=success
我得到的结论
这说明:
- namespace 身份边界可以通过 label 动态表达
namespaceSelector的规则不是死写死记,而是可治理的
这在平台设计里非常重要。
Step 10: 进入 egress 实验,先加 default-deny-egress
实际命令
kubectl apply -f manifests/05-networkpolicy/40-default-deny-egress-client.yaml
然后验证:
kubectl -n np-external exec cross-ns-client -- nslookup kubernetes.default.svc.cluster.local
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo http=success || echo http=blocked'
kubectl -n np-external get networkpolicy -o wide
为什么这里先测 DNS
因为 egress deny 最容易先打挂的就是 DNS。
这是生产里极高频的坑。
结果
我看到了:
nslookup
失败:
connection timed out; no servers could be reached
wget
失败,但错误不是 timeout,而是:
bad address 'web-svc.np-demo.svc.cluster.local'
我得到的结论
这里有两个非常关键的教学点:
- DNS 本身也是网络流量,会被 egress policy 拦住
- 应用访问失败不一定是业务端口被挡,也可能是名字根本解析不出来
这一步对建立排障分层特别重要。
Step 11: 只恢复 DNS
实际命令
kubectl apply -f manifests/05-networkpolicy/41-allow-dns-egress-client.yaml
然后测试:
kubectl -n np-external exec cross-ns-client -- nslookup web-svc.np-demo.svc.cluster.local
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo http=success || echo http=blocked'
kubectl -n np-external get networkpolicy -o yaml
为什么只恢复 DNS,而不一起恢复 HTTP
因为我要让“DNS 和业务流量是两种独立白名单”这个事实变得非常清楚。
结果
我看到了:
nslookup恢复成功wget仍然 timeout
我得到的结论
DNS 白名单恢复后:
- 名字能解了
- 但业务流量仍出不去
这正好说明:
- DNS 放行 ≠ 应用流量放行
Step 12: 再恢复业务 HTTP 出站
实际命令
kubectl apply -f manifests/05-networkpolicy/42-allow-web-egress-client.yaml
然后验证:
kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local >/dev/null && echo http=success || echo http=blocked'
kubectl -n np-external get networkpolicy
kubectl get networkpolicy -A | rg 'np-demo|np-external'
为什么最后还看全局策略列表
因为我要把这轮实验最终形成的“策略全景”固化下来,方便教学总结:
- 目标端有哪些 ingress 策略
- 源端有哪些 egress 策略
结果
我看到:
http=successnp-demo中 3 条 ingress 相关策略np-external中 3 条 egress 相关策略
我得到的结论
到这里,完整零信任实验链条已经闭环:
- 默认全通
- ingress default deny
- ingress allow same namespace
- ingress allow labeled namespace
- egress default deny
- egress allow dns
- egress allow web
Step 13: 这轮为什么没有直接使用 Calico 的 GlobalNetworkPolicy
你的集群确实支持:
GlobalNetworkPolicy
我在 API 资源里也确认到了。
但这一轮我故意没有一上来就用它,原因是:
先把标准语义吃透更重要
如果你连标准 NetworkPolicy 都还没吃透,就上:
- 显式 deny
- order
- global scope
只会把认知复杂度抬太高。
工程上也应该先掌握最小通用能力
标准 networking.k8s.io/v1 是:
- 更通用
- 更容易迁移
- 更容易面试和团队沟通的基础能力
所以这一轮是故意先打牢地基。
Step 14: 将结果写成第五课文档
基于这一轮所有实验和证据,我写入了:
这份文档里,我重点讲清了:
- 默认全通
- 被策略选中后才进入隔离状态
- 顶层
podSelector的真正含义 - 标准 NetworkPolicy 的“累加允许”模型
- Ingress / Egress 的方向语义
- DNS 为什么必须单独考虑
namespaceSelector/podSelector的组合语义- 标准策略和 Calico 扩展策略的区别
本轮命令清单
KUBECONFIG=~/.kube/config-k8s-lab kubectl get networkpolicy -A -o wide
sed -n '1,240p' phase-2/03-ingress-networkpolicy.md
KUBECONFIG=~/.kube/config-k8s-lab kubectl get ns --show-labels
KUBECONFIG=~/.kube/config-k8s-lab kubectl api-resources | rg 'networkpol|globalnetworkpolicy|networkset|globalnetworkset'
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply \
-f manifests/05-networkpolicy/00-namespace-np-demo.yaml \
-f manifests/05-networkpolicy/01-namespace-np-external.yaml \
-f manifests/05-networkpolicy/10-web-deployment.yaml \
-f manifests/05-networkpolicy/11-web-service.yaml \
-f manifests/05-networkpolicy/20-same-ns-client.yaml \
-f manifests/05-networkpolicy/21-cross-ns-client.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo rollout status deployment/web --timeout=120s
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo wait --for=condition=Ready pod/same-ns-client --timeout=120s
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external wait --for=condition=Ready pod/cross-ns-client --timeout=120s
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo get pods,svc -o wide
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo exec same-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc ...'
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external exec cross-ns-client -- sh -c 'wget -qO- -T 3 http://web-svc.np-demo.svc.cluster.local ...'
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external exec cross-ns-client -- nslookup web-svc.np-demo.svc.cluster.local
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/30-default-deny-ingress.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/31-allow-same-namespace.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl label namespace np-external access=allowed --overwrite
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/32-allow-from-labeled-namespace.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/40-default-deny-egress-client.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/41-allow-dns-egress-client.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl apply -f manifests/05-networkpolicy/42-allow-web-egress-client.yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo get networkpolicy
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-demo get networkpolicy -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external get networkpolicy -o wide
KUBECONFIG=~/.kube/config-k8s-lab kubectl -n np-external get networkpolicy -o yaml
KUBECONFIG=~/.kube/config-k8s-lab kubectl get networkpolicy -A | rg 'np-demo|np-external'
本轮最重要的结论
结论一:默认全通是现实,不是理论
我已经用跨 namespace 客户端验证过了。
结论二:策略一旦选中 Pod,该方向就进入“默认拒绝 + 允许列表”
这是 NetworkPolicy 最核心的语义。
结论三:标准 NetworkPolicy 是“允许并集”,不是顺序覆盖
这一点已经通过 default-deny-ingress + allow-same-namespace 实验验证。
结论四:Egress 不只是“能不能访问业务”,还直接决定 DNS 会不会活着
这点已经通过 nslookup 和 wget bad address 的真实现象验证。
结论五:真正的连接放行,要同时考虑源侧 egress 和目标侧 ingress
只看一边是不够的。
下一步建议
继续推进时,最自然的下一课应该进入“身份和权限”:
- ServiceAccount
- RBAC
- ConfigMap / Secret
- API 访问身份
因为网络边界只是平台安全的一部分。
你要走向专家,就必须把:
- 网络边界
- 身份边界
- 权限边界
三条线一起串起来。*** End Patch