从默认拒绝一路垒到 L7
目标
实际应用标准 NetworkPolicy,实现默认拒绝和基于选择器的允许规则;并以清单形式编写只有 CiliumNetworkPolicy 才能实现的 L7、FQDN 和显式拒绝规则。
为什么这很重要
网络策略 apply 成功,并不保证其行为符合预期。尤其有三点会悄无声息地误导人。
第一,各方向相互独立。如果只配置 ingress 策略就认为已经“隔离”,egress 仍然对整个互联网开放。数据泄露路径大多位于 egress 方向。
第二,选择器拼写错误不会报错,而是以“未选择任何对象”的方式静默失败。应用策略后,必须确认实际有多少端点处于强制执行状态。
第三,**toFQDNs 没有 DNS 规则就永远无法匹配。**这是 Cilium 策略中最常见的配置错误,也会在考试中反复出现。
标准 NetworkPolicy 需要实际 apply;CiliumNetworkPolicy 则写成 /root/cca-policy/ 下的文件。这是因为实验集群中没有安装 Cilium CRD,而考试要求的能力同样是准确编写清单。
步骤
- 创建 namespace
cca-shop和cca-mon,并为cca-mon添加标签tier=monitoring。 - 在 namespace
cca-shop中分别部署 1 个副本的 Deploymentfrontend和backend。Pod 标签分别为app=frontend、app=backend,镜像为nginx:1.27-alpine。然后创建 Servicebackend,端口为8080。 - 在 namespace
cca-shop中创建 NetworkPolicydefault-deny。podSelector为空,policyTypes同时填写Ingress和Egress,且不设置任何允许规则。 - 在同一 namespace 中创建 NetworkPolicy
allow-frontend-to-backend。podSelector为app=backend,policyTypes仅为Ingress;ingress 规则只允许来自podSelector为app=frontend的来源访问端口8080/ 协议TCP。 - 在同一 namespace 中创建 NetworkPolicy
allow-monitoring。podSelector为app=backend;ingress 规则的 from 项仅包含一个namespaceSelector,选择tier=monitoring的 namespace,端口为9090。 - 在
/root/cca-policy/cnp-l7.yaml中编写 CiliumNetworkPolicy。apiVersion为cilium.io/v2,metadata.namespace为cca-shop,endpointSelector为app: backend。ingress 规则的fromEndpoints为app: frontend,并在toPorts的端口8080/TCP 下为rules.http设置两个条目:第一个为 methodGET/ path/api/v1/orders,第二个为 methodPOST/ path/api/v1/orders。 - 在
/root/cca-policy/cnp-fqdn.yaml中编写 CiliumNetworkPolicy。endpointSelector为app: frontend,egress 规则必须恰好有两条。第一条为toEndpoints是k8s-app: kube-dns的目标开放端口53/UDP,并在rules.dns中设置matchPattern: "*"。第二条中,toFQDNs的matchName为api.pgprovider.example,并开放端口443/TCP。 - 在
/root/cca-policy/cnp-deny.yaml中编写名为block-metadata的 CiliumNetworkPolicy。endpointSelector为空;在 egress 规则中通过toCIDRSet允许cidr: 0.0.0.0/0,但在except中加入169.254.169.254/32;此外,还要在egressDeny的toCIDR中明确写入169.254.169.254/32。
参考
- 使用
kubectl get netpol -n cca-shop -o yaml重新读取应用结果,确认它与预期一致。 - CiliumNetworkPolicy 的端口通常写成字符串,例如
port: "8080"。 - 常见错误 1:默认拒绝的
policyTypes只写一个方向,导致另一个方向仍然完全开放。 - 常见错误 2:混淆
fromEndpoints: []和fromEndpoints: [{}]。前者不允许任何来源,后者允许同一 namespace 中的所有来源。 - 常见错误 3:只使用 toFQDNs,却遗漏 DNS 规则。如果代理无法获知响应,该名称将永远无法匹配。
创建应用与监控 namespace
创建 namespace cca-shop 和 cca-mon,并为 cca-mon 添加标签 tier=monitoring。
namespaceSelector 只能查看 namespace 的标签,不能按名称选择,因此请事先添加标签。
部署前端和后端
在 namespace cca-shop 中分别部署 1 个副本的 Deployment frontend 和 backend。Pod 标签分别为 app=frontend、app=backend,镜像为 nginx:1.27-alpine。然后创建 Service backend,端口为 8080。
策略通过标签选择对象。Pod 模板中的 app 标签必须准确,后续所有步骤才能正确衔接。
创建双向默认拒绝
在 namespace cca-shop 中创建 NetworkPolicy default-deny。podSelector 为空,policyTypes 同时填写 Ingress 和 Egress,且不设置任何允许规则。
两个方向相互独立。只写一侧时,另一方向仍然完全开放。不要添加任何允许规则。
通过 Pod 选择器只开放一条路径
在同一 namespace 中创建 NetworkPolicy allow-frontend-to-backend。podSelector 为 app=backend,policyTypes 仅为 Ingress;ingress 规则只允许来自 podSelector 为 app=frontend 的来源访问端口 8080 / 协议 TCP。
策略中的 podSelector 选择接收流量的一方,from 中的 podSelector 选择发送流量的一方。此步骤只允许同一 namespace 内的来源,因此不要同时添加 namespaceSelector。
通过 namespace 选择器向其他 namespace 开放
在同一 namespace 中创建 NetworkPolicy allow-monitoring。podSelector 为 app=backend;ingress 规则的 from 项仅包含一个 namespaceSelector,选择 tier=monitoring 的 namespace,端口为 9090。
使用第 1 步添加的 namespace 标签。要开放的是指标端口,而不是应用端口。
按 HTTP 方法和路径进行限制
在 /root/cca-policy/cnp-l7.yaml 中编写 CiliumNetworkPolicy。apiVersion 为 cilium.io/v2,metadata.namespace 为 cca-shop,endpointSelector 为 app: backend。ingress 规则的 fromEndpoints 为 app: frontend,并在 toPorts 的端口 8080/TCP 下为 rules.http 设置两个条目:第一个为 method GET / path /api/v1/orders,第二个为 method POST / path /api/v1/orders。
这是标准 NetworkPolicy 无法实现的领域。请在 toPorts 下设置 rules.http,并列出方法和路径。不匹配的请求不会以连接中断的形式被阻止,而是返回 403。
按域名允许访问外部 API
在 /root/cca-policy/cnp-fqdn.yaml 中编写 CiliumNetworkPolicy。endpointSelector 为 app: frontend,egress 规则必须恰好有两条。第一条为 toEndpoints 是 k8s-app: kube-dns 的目标开放端口 53/UDP,并在 rules.dns 中设置 matchPattern: "*"。第二条中,toFQDNs 的 matchName 为 api.pgprovider.example,并开放端口 443/TCP。
只使用 toFQDNs 绝对无法工作。DNS 代理必须获知响应,才能登记 IP,因此必须同时提供一条允许 DNS 查询并将其纳入观测的规则。
封锁元数据端点
在 /root/cca-policy/cnp-deny.yaml 中编写名为 block-metadata 的 CiliumNetworkPolicy。endpointSelector 为空;在 egress 规则中通过 toCIDRSet 允许 cidr: 0.0.0.0/0,但在 except 中加入 169.254.169.254/32;此外,还要在 egressDeny 的 toCIDR 中明确写入 169.254.169.254/32。
同时使用在允许规则中划出例外的方法,以及再叠加一层显式拒绝的方法。deny 优先于 allow,因此这两层并不相互矛盾。