Kubernetes 中的网络策略用于指定 Pod 组之间以及其与外部网络端点之间的通信,它就像是 Kubernetes 的防火墙。与大多数 Kubernetes 对象一样,网络策略非常灵活,功能也很强大。如果了解应用程序中服务的确切通信模式,我们就可以通过网络策略将通信限制在我们想要范围之内。
本文主要简绍egress网络隔离策略的用法,更多详细内容请参看Kubernetes Egress 网络策略指南
Istio 是可配置的开源服务网格层,用于连接、监视和保护 Kubernetes 集群中的容器。Istio中国官网
Kubernetes.io详解
如果你希望在 IP 地址或端口层面(OSI 第 3 层或第 4 层)控制网络流量,则你可以考虑为集群中特定应用使用 Kubernetes 网络策略。(NetworkPolicy)。 NetworkPolicy 是一种以“应用”为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络“实体” (我们这里使用实体以避免过度使用诸如“端点”和“服务”这类常用术语, 这些术语在 Kubernetes 中有特定含义)通信。
Pod 可以通信的 Pod 是通过如下三个标识符的组合来辩识的:
✔ 1.其他被允许的 Pods(例外:Pod 无法阻塞对自身的访问)
✔ 2.被允许的名字空间
✔ 3.IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)
在定义基于 Pod 或名字空间的 NetworkPolicy 时,你会使用 选择算符来设定哪些流量 可以进入或离开与该算符匹配的 Pod。
同时,当基于 IP 的 NetworkPolicy 被创建时,我们基于 IP 组块(CIDR 范围) 来定义策略。
Pod 有两种隔离: 出口的隔离和入口的隔离。它们涉及到可以建立哪些连接。 这里的“隔离”不是绝对的,而是意味着“有一些限制”。 另外的“非隔离方向”意味着在所述方向上没有限制。这两种隔离(或不隔离)是独立声明的, 并且都与从一个 Pod 到另一个 Pod 的连接有关。
前置条件
网络策略通过 网络插件 来实现。要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。 创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的。
Ingress 与 Egress
网络策略可用于指定 Pod 的入口(Ingress)流量和出口(Egress)流量,其规则如下:
✔ 如果允许集群外部网络端点到 Pod 的通讯,那么该端点可以访问 Pod。
✔ 如果允许 Pod 到集群外部网络端点的通讯,那么 Pod 也可以访问该端点。
✔ 如果 Pod(A)到 Pod(B)的流量被允许,那么流量可以通过 A 的出口以及 B 的入口。注意这是单向的,流量从B到A,那么只能从 B 的出口到 A 的入口。
默认情况下,一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Egress”,则该 Pod 是出口隔离的, 我们称这样的策略适用于该 Pod 的出口。当一个 Pod 的出口被隔离时,唯一允许的来自 Pod 的连接是适用于出口的 Pod 的某个 NetworkPolicy 的 egress 列表所允许的连接。 这些 egress 列表的效果是相加的。
默认情况下,一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的。如果有任何的 NetworkPolicy 选择该 Pod 并在其 policyTypes 中包含 “Ingress”,则该 Pod 被隔离入口, 我们称这种策略适用于该 Pod 的入口。 当一个 Pod 的入口被隔离时,唯一允许进入该 Pod 的连接是来自该 Pod 节点的连接和适用于入口的 Pod 的某个 NetworkPolicy 的 ingress 列表所允许的连接。这些 ingress 列表的效果是相加的。
网络策略是相加的,所以不会产生冲突。如果策略适用于 Pod 某一特定方向的流量,Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。因此评估的顺序不影响策略的结果。
要允许从源 Pod 到目的 Pod 的连接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允许连接。 如果任何一方不允许连接,建立连接将会失败。
下面是一个 NetworkPolicy 的示例:
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
Note: 除非选择支持网络策略的网络解决方案,否则将上述示例发送到API服务器没有任何效果。
♥ 必需字段:与所有其他的 Kubernetes 配置一样,NetworkPolicy 需要 apiVersion、 kind 和 metadata 字段。
♥ spec:NetworkPolicy 规约中包含了在一个名字空间中定义特定网络策略所需的所有信息。
♥ podSelector:每个 NetworkPolicy 都包括一个 podSelector,它对该策略所 适用的一组 Pod 进行选择。示例中的策略选择带有 “role=db” 标签的 Pod。 空的podSelector选择名字空间下的所有 Pod。
♥ policyTypes: 每个 NetworkPolicy 都包含一个 policyTypes 列表,其中包含 Ingress 或 Egress 或两者兼具。policyTypes 字段表示给定的策略是应用于 进入所选 Pod 的入站流量还是来自所选 Pod 的出站流量或两者兼有。 如果 NetworkPolicy 未指定 policyTypes 则默认情况下始终设置 Ingress; 如果 NetworkPolicy 有任何出口规则的话则设置 Egress。
♥ ingress: 每个 NetworkPolicy 可包含一个 ingress 规则的白名单列表。 每个规则都允许同时匹配 from 和 ports 部分的流量。示例策略中包含一条 简单的规则: 它匹配某个特定端口,来自三个来源中的一个,第一个通过 ipBlock 指定,第二个通过 namespaceSelector 指定,第三个通过 podSelector 指定。
♥ egress: 每个 NetworkPolicy 可包含一个 egress 规则的白名单列表。每个规则都允许匹配 to 和 port 部分的流量。该示例策略包含一条规则, 该规则将指定端口上的流量匹配到 10.0.0.0/24 中的任何目的地。
所以,该网络策略示例:
1、隔离 “default” 名字空间下 “role=db” 的 Pod (如果它们不是已经被隔离的话)。
2、(Ingress 规则)允许以下 Pod 连接到 “default” 名字空间下的带有 “role=db” 标签的所有 Pod 的 6379 TCP 端口:
▷ “default” 名字空间下带有 “role=frontend” 标签的所有 Pod
▷ 带有 “project=myproject” 标签的所有名字空间中的 Pod
▷ IP 地址范围为 172.17.0.0–172.17.0.255 和 172.17.2.0–172.17.255.255 (即除了 172.17.1.0/24 之外的所有 172.17.0.0/16)
3、(Egress 规则)允许 “default” 命名空间中任何带有标签 “role=db” 的 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口的连接。
可以在 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 的连接。如有疑问,请使用 kubectl describe 查看 Kubernetes 如何解释该策略。
✔ ipBlock:此选择器将选择特定的 IP CIDR 范围以用作入站流量来源或出站流量目的地。这些应该是集群外部IP,因为Pod IP存在时间短暂且随机产生。
集群的入站和出站机制通常需要重写数据包的源 IP 或目标 IP。在发生这种情况时,不确定在 NetworkPolicy 处理之前还是之后发生,并且对于网络插件、云提供商、Service 实现等的不同组合,其行为可能会有所不同。
• 对入站流量而言,这意味着在某些情况下,你可以根据实际的原始源 IP 过滤传入的数据包,而在其他情况下,NetworkPolicy 所作用的 源IP 则可能是 LoadBalancer 或 Pod 的节点等。
• 对于出站流量而言,这意味着从 Pod 到被重写为集群外部 IP 的 Service IP 的连接可能会或可能不会受到基于 ipBlock 的策略的约束。
在完成入口网络策略的设置并成功运行后,我们再设置出口网络策略。这样做的原因有两个:首先,一次执行两项操作比较困难,而且我们很难知道是由于入口还是出口配置导致的网络连接失败;其次,出口网络策略通常更难以实施。限制出口可能会引发各种错误,从而影响应用程序运行。
虽然确定网络端点到 Pod 的通讯非常简单,但要确定 Pod 到网络端点的连接方式会比较复杂。之所以出现这一问题,这是因为:
• 作为常规功能的一部分,Deployment 通常会查询一堆外部服务,根据它们在访问这些服务时处理超时的方式,功能可能会受到一些细微而又难以观察的影响。
• Deployment 需要能够与 DNS 服务器通信,以便和其他任何服务器通信,除非这些服务器直接通过 IP 与服务联系。
✔ 每个网络策略都有一个 podSelector 字段,该字段会选择一组(0 个或多个)Pod。当网络策略选择了一个 Pod 时,就称该网络策略适用于该 Pod。
✔ 此外,每个网络策略都可以根据 policyTypes 字段的值应用于入口和出口。如果 YAML 中未指定此字段,它的值会基于策略中的入口、出口规则默认设置,但默认设置并不靠谱,我们最好进行明确的设置。
✔ 一般情况下,如果没有任何出口网络策略适用于 Pod,那么它就是非隔离的出口。这里要注意,隔离是针对入口和出口独立评估的。一个 Pod 的入口和出口可以都隔离,也可以都不隔离,甚至进行单个隔离。如果一个 Pod 的出口没有进行隔离,那么所有的流量都可以从这个 Pod 中出来。
✔ 当一个出口网络策略适用于某个 Pod 时,该Pod的出口就会被隔离。对于隔离的Pod,只有在网络策略允许的情况下才允许网络出口,也就是网络策略白名单。
★ 设置出口网络策略的第一步是对 Pod 进行出口隔离。我们最好先应用“默认拒绝所有”的策略,将所有 Pod 的出口都隔离了。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all-egress
spec:
podSelector: {}
egress:
- to:
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
policyTypes:
- Egress
不过要注意,默认情况下这个策略允许流量连接到任意 IP 上的 53 端口,以方便 DNS 查找。因此尽管它可以防止出口出现问题,但不能防止数据泄露,因为攻击者可以将数据发送到 53 端口。
如果想知道 Pod 使用的是哪些 DNS 服务器,我们可以通过缩小访问范围进行确定。例如,要将 DNS 范围缩小到仅提供 kube-dns 服务,可以执行以下操作:
// 标记 kube-system 命名空间:
kubectl label namespace kube-system networking/namespace=kube-system
// 应用以下网络策略:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all-egress
spec:
podSelector: {}
egress:
- to:
- namespaceSelector:
matchLabels:
networking/namespace: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
policyTypes:
- Egress
重要提示:由于网络策略是带命名空间资源,因此每个命名空间都需要创建此策略。我们可以运行以下命令:
kubectl -ncreate -f
另外,最好不要将其应用于 kube-system 命名空间,因为它可能会影响集群功能。
默认策略默认情况下,如果名字空间中不存在任何策略,则所有进出该名字空间中 Pod 的流量都被允许。 以下示例使你可以更改该名字空间中的默认行为。
默认拒绝所有入站流量你可以通过创建选择所有容器但不允许任何进入这些容器的入站流量的 NetworkPolicy 来为名字空间创建 “default” 隔离策略。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
这样可以确保即使容器没有选择其他任何 NetworkPolicy也仍然可以被隔离。 此策略不会更改默认的出口隔离行为。
默认允许所有入站流量如果要允许所有流量进入某个名字空间中的所有 Pod(即使添加了导致某些 Pod 被视为 “隔离”的策略),则可以创建一个策略来明确允许该名字空间中的所有流量。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
默认拒绝所有出站流量
你可以通过创建选择所有容器但不允许来自这些容器的任何出站流量的 NetworkPolicy 来为名字空间创建 “default” 隔离策略。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。此策略不会更改默认的入站流量隔离行为。
默认允许所有出站流量如果要允许来自名字空间中所有 Pod 的所有流量(即使添加了导致某些 Pod 被视为“隔离”的策略),则可以创建一个策略,该策略明确允许该名字空间中的所有出站流量。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
默认拒绝所有入口和所有出站流量
你可以为名字空间创建“默认”策略,以通过在该名字空间中创建以下 NetworkPolicy 来阻止所有入站和出站流量。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被 允许入站或出站流量。
SCTP 支持作为一个稳定特性,SCTP 支持默认是被启用的。 要在集群层面禁用 SCTP,你(或你的集群管理员)需要为 API 服务器指定--feature-gates=SCTPSupport=false,...来禁用 SCTPSupport 特性门控。 启用该特性门控后,用户可以将 NetworkPolicy 的 protocol 字段设置为 SCTP。
针对某个端口范围Note:
你必须使用支持 SCTP 协议网络策略的 CNI 插件。
在编写 NetworkPolicy 时,你可以针对一个端口范围而不是某个固定端口。
这一目的可以通过使用 endPort 字段来实现,如下例所示:
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 之间就可以。
使用此字段时存在以下限制:
✔ 作为一种 Beta 阶段的特性,端口范围设定默认是被启用的。要在整个集群 范围内禁止使用 endPort 字段,你(或者你的集群管理员)需要为 API 服务器设置 --feature-gates=NetworkPolicyEndPort=false,… 以禁用 NetworkPolicyEndPort 特性门控。
✔ endPort 字段必须等于或者大于 port 字段的值。
✔ 两个字段的设置值都只能是数字。
基于名字指向某名字空间Note:
你的集群所使用的 CNI 插件 必须支持在 NetworkPolicy 规约中使用 endPort 字段。 如果你的网络插件 不支持 endPort 字段,而你指定了一个包含 endPort 字段的 NetworkPolicy, 策略只对单个 port 字段生效
只要NamespaceDefaultLabelName被启用,Kubernetes 控制面会在所有名字空间上设置一个不可变更的标签kubernetes.io/metadata.name。该标签的值是名字空间的名称。
如果 NetworkPolicy 无法在某些对象字段中指向某名字空间,你可以使用标准的 标签方式 来指向特定名字空间。
到 Kubernetes 1.24 为止,NetworkPolicy API 还不支持以下功能,不过 你可能可以使用操作系统组件(如 SELinux、OpenVSwitch、IPTables 等等) 或者第七层技术(Ingress 控制器、服务网格实现)或准入控制器来实现一些 替代方案。 如果你对 Kubernetes 中的网络安全性还不太了解,了解使用 NetworkPolicy API 还无法实现下面的用户场景是很值得的。
✔ 强制集群内部流量经过某公用网关(这种场景最好通过服务网格或其他代理来实现);
✔ 与 TLS 相关的场景(考虑使用服务网格或者 Ingress 控制器);
✔ 特定于节点的策略(你可以使用 CIDR 来表达这一需求不过你无法使用节点在 Kubernetes 中的其他标识信息来辩识目标节点);
✔ 基于名字来选择服务(不过,你可以使用 标签 来选择目标 Pod 或名字空间,这也通常是一种可靠的替代方案);
✔ 创建或管理由第三方来实际完成的“策略请求”;
✔ 实现适用于所有名字空间或 Pods 的默认策略(某些第三方 Kubernetes 发行版本 或项目可以做到这点);
✔ 高级的策略查询或者可达性相关工具;
✔ 生成网络安全事件日志的能力(例如,被阻塞或接收的连接请求);
✔ 显式地拒绝策略的能力(目前NetworkPolicy 的模型默认采用拒绝操作,其唯一的能力是添加允许策略);
✔ 禁止本地回路或指向宿主的网络流量(Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求);
Gradually away from not only the people and things around, and once their own!



