此文档从 Kubernetes 官网摘录
中文地址
英文地址
如果你希望针对 TCP、UDP 和 SCTP 协议在 IP 地址或端口层面控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。
Pod 可以与之通信的实体是通过如下三个标识符的组合来辩识的:
在定义基于 Pod 或名字空间的 NetworkPolicy 时, 你会使用选择算符来设定哪些流量可以进入或离开与该算符匹配的 Pod。
另外,当创建基于 IP 的 NetworkPolicy 时,我们基于 IP 组块(CIDR 范围)来定义策略。
网络策略通过网络插件来实现。 要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。 创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的。
Pod 有两种隔离:出口的隔离和入口的隔离。
默认情况下,一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Egress”,则该 Pod 是出口隔离的, 我们称这样的策略适用于该 Pod 的出口。
默认情况下,一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Ingress”,则该 Pod 被隔离入口, 我们称这种策略适用于该 Pod 的入口。
网络策略是相加的,所以不会产生冲突。如果策略适用于 Pod 某一特定方向的流量, Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。 因此,评估的顺序不影响策略的结果。
要允许从源 Pod 到目的 Pod 的某个连接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允许此连接。 如果任何一方不允许此连接,则连接将会失败。
vim networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
说明:
除非选择支持网络策略的网络解决方案,否则将上述示例发送到 API 服务器没有任何效果。
可以在 ingress 的 from 部分或 egress 的 to 部分中指定四种选择器:
podSelector:此选择器将在与 NetworkPolicy 相同的名字空间中选择特定的 Pod,应将其允许作为入站流量来源或出站流量目的地。
namespaceSelector:此选择器将选择特定的名字空间,应将所有 Pod 用作其入站流量来源或出站流量目的地。
namespaceSelector 和 podSelector:一个指定 namespaceSelector 和 podSelector 的 to/from 条目选择特定名字空间中的特定 Pod。 注意使用正确的 YAML 语法;下面的策略:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
...
此策略在 from 数组中仅包含一个元素,只允许来自标有 role=client 的 Pod 且该 Pod 所在的名字空间中标有 user=alice 的连接。但是这项策略:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
...
它在 from 数组中包含两个元素,允许来自本地名字空间中标有 role=client 的 Pod 的连接,或来自任何名字空间中标有 user=alice 的任何 Pod 的连接。
默认情况下,如果名字空间中不存在任何策略,则所有进出该名字空间中 Pod 的流量都被允许。 以下示例使你可以更改该名字空间中的默认行为。
你可以通过创建选择所有 Pod 但不允许任何进入这些 Pod 的入站流量的 NetworkPolicy 来为名字空间创建 “default” 隔离策略。
network-policy-default-deny-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
如果你想允许一个名字空间中所有 Pod 的所有入站连接,你可以创建一个明确允许的策略。
network-policy-allow-all-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
network-policy-default-deny-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
network-policy-allow-all-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
network-policy-default-deny-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
NetworkPolicy 是为第 4 层连接 (TCP、UDP 和可选的 SCTP)所定义的。对于所有其他协议,这种网络流量过滤的行为可能因网络插件而异。
说明:
你必须使用支持 SCTP 协议 NetworkPolicy 的 CNI 插件。
当 deny all 网络策略被定义时,此策略只能保证拒绝 TCP、UDP 和 SCTP 连接。 对于 ARP 或 ICMP 这类其他协议,这种网络流量过滤行为是未定义的。 相同的情况也适用于 allow 规则:当特定 Pod 被允许作为入口源或出口目的地时, 对于(例如)ICMP 数据包会发生什么是未定义的。 ICMP 这类协议可能被某些网络插件所允许,而被另一些网络插件所拒绝。
在编写 NetworkPolicy 时,你可以针对一个端口范围而不是某个固定端口。
这一目的可以通过使用 endPort 字段来实现,如下例所示:
networkpolicy-multiport-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: multi-port-egress
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 32000
endPort: 32768
上面的规则允许名字空间 default 中所有带有标签 role=db 的 Pod 使用 TCP 协议与 10.0.0.0/24 范围内的 IP 通信,只要目标端口介于 32000 和 32768 之间就可以。
使用此字段时存在以下限制:
在这种情况下,你的 Egress NetworkPolicy 使用名字空间的标签名称来将多个名字空间作为其目标。 为此,你需要为目标名字空间设置标签。例如:
kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend
在 NetworkPolicy 文档中的 namespaceSelector 下添加标签。例如:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-namespaces
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchExpressions:
- key: namespace
operator: In
values: ["frontend", "backend"]
说明:
你不可以在 NetworkPolicy 中直接指定名字空间的名称。 你必须使用带有 matchLabels 或 matchExpressions 的 namespaceSelector 来根据标签选择名字空间。
Kubernetes 控制面会在所有名字空间上设置一个不可变更的标签 kubernetes.io/metadata.name。该标签的值是名字空间的名称。
如果 NetworkPolicy 无法在某些对象字段中指向某名字空间, 你可以使用标准的标签方式来指向特定名字空间。
说明:
以下内容适用于使用了合规网络插件和 NetworkPolicy 合规实现的集群。
当新的 NetworkPolicy 对象被创建时,网络插件可能需要一些时间来处理这个新对象。 如果受到 NetworkPolicy 影响的 Pod 在网络插件完成 NetworkPolicy 处理之前就被创建了, 那么该 Pod 可能会最初处于无保护状态,而在 NetworkPolicy 处理完成后被应用隔离规则。
一旦 NetworkPolicy 被网络插件处理,
用户所创建的每个 NetworkPolicy 最终都会被网络插件处理,但无法使用 Kubernetes API 来获知确切的处理时间。
因此,若 Pod 启动时使用非预期的网络连接,它必须保持稳定。 如果你需要确保 Pod 在启动之前能够访问特定的目标,可以使用 Init 容器在 kubelet 启动应用容器之前等待这些目的地变得可达。
每个 NetworkPolicy 最终都会被应用到所选定的所有 Pod 之上。 由于网络插件可能以分布式的方式实现 NetworkPolicy,所以当 Pod 被首次创建时或当 Pod 或策略发生变化时,Pod 可能会看到稍微不一致的网络策略视图。 例如,新建的 Pod 本来应能立即访问 Node 1 上的 Pod A 和 Node 2 上的 Pod B, 但可能你会发现新建的 Pod 可以立即访问 Pod A,但要在几秒后才能访问 Pod B。
针对 hostNetwork Pod 的 NetworkPolicy 行为是未定义的,但应限制为以下两种可能:
网络插件可以从所有其他流量中辨别出 hostNetwork Pod 流量 (包括能够从同一节点上的不同 hostNetwork Pod 中辨别流量), 网络插件还可以像处理 Pod 网络流量一样,对 hostNetwork Pod 应用 NetworkPolicy。
网络插件无法正确辨别 hostNetwork Pod 流量,因此在匹配 podSelector 和 namespaceSelector 时会忽略 hostNetwork Pod。流向/来自 hostNetwork Pod 的流量的处理方式与流向/来自节点 IP 的所有其他流量一样。(这是最常见的实现方式。)
到 Kubernetes 1.30 为止,NetworkPolicy API 还不支持以下功能, 不过你可能可以使用操作系统组件(如 SELinux、OpenVSwitch、IPTables 等等) 或者第七层技术(Ingress 控制器、服务网格实现)或准入控制器来实现一些替代方案。 如果你对 Kubernetes 中的网络安全性还不太了解,了解使用 NetworkPolicy API 还无法实现下面的用户场景是很值得的。
当应用到现有连接的 NetworkPolicy 集合发生变化时,这可能是由于 NetworkPolicy 发生变化或者在现有连接过程中(主体和对等方)策略所选择的名字空间或 Pod 的相关标签发生变化, 此时是否对该现有连接生效是由实现定义的。例如:某个策略的创建导致之前允许的连接被拒绝, 底层网络插件的实现负责定义这个新策略是否会关闭现有的连接。 建议不要以可能影响现有连接的方式修改策略、Pod 或名字空间。