注意事项 佩雷夫 :本文的作者-Reuven Harrison-在软件开发方面拥有20多年的经验,如今是Tufin(一家创建安全策略管理解决方案的公司)的技术总监和共同创始人。 考虑到Kubernetes网络策略是在集群中进行网络分段的足够强大的工具,他认为在实践中应用它们并不是那么容易。 该材料(相当大量)旨在提高专家对此事的知识,并帮助他们创建必要的配置。 如今,许多公司越来越选择Kubernetes来运行其应用程序。 人们对该软件的兴趣如此之高,以至于有人称Kubernetes为“新数据中心操作系统”。 逐渐地,Kubernetes(或k8s)开始被视为业务的关键部分,这需要组织成熟的业务流程,包括网络安全性。
对于那些对使用Kubernetes感到困惑的安全专业人员来说,该平台的默认策略可能是一个真正的发现:允许所有这些。
本指南将帮助您了解网络策略的内部结构。 了解它们与普通防火墙的规则有何不同。 还将描述一些陷阱,并提出一些建议,以帮助保护Kubernetes中的应用程序。
Kubernetes网络策略
Kubernetes网络策略机制允许您在网络级别(OSI模型中的第三个)控制部署在平台上的应用程序的交互。 网络策略缺少现代防火墙的某些高级功能,例如OSI 7级监视和威胁检测,但是它们提供了基本的网络安全级别,这是一个很好的起点。
网络策略控制Pod之间的通信
Kubernetes工作负载分布在由一个或多个部署在一起的容器组成的Pod中。 Kubernetes为每个Pod分配一个可从其他Pod访问的IP地址。 Kubernetes网络策略为pod组设置权限的方式与使用云中的安全组控制对虚拟机实例的访问的方式相同。
定义网络策略
与其他Kubernetes资源一样,网络策略也是在YAML中设置的。 在下面的示例中,
balance
被授予访问
postgres
权限:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.postgres namespace: default spec: podSelector: matchLabels: app: postgres ingress: - from: - podSelector: matchLabels: app: balance policyTypes: - Ingress
( 注意 :此屏幕快照与所有后续类似屏幕快照一样,不是使用Kubernetes原生工具创建的,而是使用Tufin Orca工具创建的,该工具由原始文章的作者开发,并在本文的结尾提到。)定义自己的网络策略将需要基本的YAML知识。 该语言基于缩进(由空格而不是制表符指定)。 缩进元素属于其上方最近的缩进元素。 新列表项以连字符开头,所有其他项均为
键值 。
在YAML中描述了该策略之后,请使用
kubectl在集群中创建它:
kubectl create -f policy.yaml
网络策略规范
Kubernetes网络策略规范包括四个要素:
podSelector
:定义受此政策影响的Pod(目标)-强制;policyTypes
:指示其中包括哪些类型的策略:入口和/或出口-可选,但是,我建议您在所有情况下都明确注册它;ingress
:定义允许的到目标容器的传入流量-可选;egress
:定义允许从目标吊舱流出的流量-可选。
从Kubernetes网站借用的示例(我用
app
替换了
role
)显示了如何使用所有四个元素:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: # <<< matchLabels: app: 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


请注意,所有四个元素都是可选的。 只
podSelector
,可以根据需要使用其他参数。
如果省略
policyTypes
,则该策略将解释如下:
- 默认情况下,假定它定义了入口侧。 如果该策略未明确指出,则系统将认为所有流量都被禁止。
- 出口侧的行为将取决于是否存在相应的出口参数。
为避免错误,建议
始终明确指定policyTypes
。
根据上述逻辑,如果省略了
ingress
和/或
egress
,则该策略将禁止所有流量(请参阅下面的“剥离规则”)。
默认策略是允许
如果未定义任何策略,Kubernetes默认会允许所有流量。 所有Pod均可自由交换信息。 从安全角度来看,这似乎违反直觉,但请记住,Kubernetes最初是由开发人员创建的,目的是确保应用程序交互。 网络策略是在以后添加的。
命名空间
命名空间是Kubernetes的一种协作机制。 它们旨在隔离逻辑环境,而默认情况下允许空间之间的数据交换。
与大多数Kubernetes组件一样,网络策略位于特定的名称空间中。 在
metadata
块中,您可以指定策略所属的空间:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: my-namespace # <<< spec: ...
如果未在元数据中显式编写名称空间,则系统将使用kubectl中指定的名称空间(默认情况下,
namespace=default
):
kubectl apply -n my-namespace -f namespace.yaml
我建议
显式指定一个名称空间,除非您要一次编写用于多个名称空间的策略。
策略中的主要 podSelector
元素将从策略所属的名称空间中选择Pod(拒绝从另一个名称空间访问Pod)。
同样,
入口和出口块中的 podSelector只能从其名称空间中选择Pod,除非您将它们与
namespaceSelector
结合使用(这将在“按名称空间和Pod过滤”一节中进行讨论) 。
策略命名规则
策略名称在单个名称空间中是唯一的。 在一个空间中不能有两个具有相同名称的策略,但是在不同的空间中可能会具有相同名称的策略。 当您要在多个空间中重新应用同一策略时,此功能很有用。
我特别喜欢一种命名方式。 它包括将名称空间名称与目标Pod组合在一起。 例如:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.postgres # <<< namespace: default spec: podSelector: matchLabels: app: postgres ingress: - from: - podSelector: matchLabels: app: admin policyTypes: - Ingress

标签
可以将自定义标签附加到Kubernetes对象上,例如吊舱和名称空间。 标签相当于云中的标签。 Kubernetes网络策略使用标签来选择要应用的
Pod :
podSelector: matchLabels: role: db
...或它们适用
的名称空间 。 在此示例中,将选择名称空间中带有相应标签的所有窗格:
namespaceSelector: matchLabels: project: myproject
一个警告:使用
namespaceSelector
确保所选的 namespaceSelector
包含所需的标签 。 请记住,
default
,内置名称空间(例如
default
和
kube-system
不包含标签。
您可以如下所示向该空间添加标签:
kubectl label namespace default namespace=default
在这种情况下,
metadata
部分中的名称空间应引用空间的实际名称,而不是标签:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default # <<< spec: ...
来源和目的地
防火墙策略由带有源和目标的规则组成。 为此定义了Kubernetes网络策略-对其应用了一组Pod,然后为传入(入口)和/或传出(出口)流量建立规则。 在我们的示例中,策略目标将是
default
名称空间中的所有Pod,并带有带有
app
密钥和
db
值的标签:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: app: 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


此策略中的“
ingress
小节将进入的流量打开到目标容器。 换句话说,入口是源,目标是适当的目的地。 同样,出口是目标,目标是其来源。
这等效于防火墙的两个规则:入口→目标; 目标→出口。出口和DNS(重要!)
在限制出站流量时,请
特别注意DNS -Kubernetes使用此服务将服务映射到IP地址。 例如,以下策略将不起作用,因为您不允许
balance
应用程序访问DNS:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.balance namespace: default spec: podSelector: matchLabels: app: balance egress: - to: - podSelector: matchLabels: app: postgres policyTypes: - Egress

您可以通过打开对DNS服务的访问来修复它:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.balance namespace: default spec: podSelector: matchLabels: app: balance egress: - to: - podSelector: matchLabels: app: postgres - to: # <<< ports: # <<< - protocol: UDP # <<< port: 53 # <<< policyTypes: - Egress

last to元素为空,因此它间接选择
所有名称空间中的所有Pod ,从而使
balance
将DNS查询发送到相应的Kubernetes服务(它通常在
kube-system
空间中工作)。
这种方法有效,但是
过于宽松和不安全 ,因为它允许您将DNS查询定向到群集之外。
您可以通过三个连续的步骤对其进行改进。
1.通过添加
namespaceSelector
仅允许集群中的DNS查询:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.balance namespace: default spec: podSelector: matchLabels: app: balance egress: - to: - podSelector: matchLabels: app: postgres - to: - namespaceSelector: {} # <<< ports: - protocol: UDP port: 53 policyTypes: - Egress

2.仅在
kube-system
名称空间中允许DNS查询。
为此,将标签添加到
kube-system
kubectl label namespace kube-system namespace=kube-system
:
kubectl label namespace kube-system namespace=kube-system
并使用
namespaceSelector
在策略中注册它:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.balance namespace: default spec: podSelector: matchLabels: app: balance egress: - to: - podSelector: matchLabels: app: postgres - to: - namespaceSelector: # <<< matchLabels: # <<< namespace: kube-system # <<< ports: - protocol: UDP port: 53 policyTypes: - Egress

3.偏执狂甚至可以走得更远,并将DNS查询限制为
kube-system
的特定DNS服务。 在“按名称空间和容器过滤”部分中,我们将说明如何实现此目的。
另一个选择是在名称空间级别解析DNS。 在这种情况下,无需为每个服务打开它:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.dns namespace: default spec: podSelector: {} # <<< egress: - to: - namespaceSelector: {} ports: - protocol: UDP port: 53 policyTypes: - Egress
空的
podSelector
选择名称空间中的所有pod。

首次比赛和规则顺序
在普通防火墙中,数据包的操作(“允许”或“拒绝”)由它满足的第一条规则确定。
在Kubernetes中,策略的顺序无关紧要。默认情况下,未设置策略时,将允许Pod之间的通信,并且它们可以自由交换信息。 一旦您开始制定策略,受其中至少一个影响的Pod就会根据已选择策略的所有策略的分离(逻辑或)而被隔离。 不受任何政策影响的豆荚保持开放状态。
您可以使用剥离规则更改此行为。
剥离规则(拒绝)
防火墙策略通常禁止任何明确未经授权的流量。
Kubernetes没有拒绝动作 ,但是,通过选择空的一组源Pod(入口),使用常规(允许)策略可以实现类似的效果:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: default spec: podSelector: {} policyTypes: - Ingress

此策略选择名称空间中的所有Pod,并使入口保持未定义状态,从而阻止所有传入流量。
同样,您可以限制名称空间中的所有传出流量:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-egress namespace: default spec: podSelector: {} policyTypes: - Egress

请注意,
任何允许流向名称空间中的Pod的其他策略都将优先于此规则 (类似于在防火墙配置中添加允许规则而不是拒绝规则)。
允许所有(任何允许任何允许)
要创建“允许所有”策略,您必须添加上述禁止策略并添加一个空的
ingress
元素:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all namespace: default spec: podSelector: {} ingress: # <<< - {} # <<< policyTypes: - Ingress

它允许从
所有名称空间(和所有IP)中的所有容器访问default
名称空间中的任何容器 。 默认情况下,此行为是启用的,因此通常不需要进一步定义。 但是,有时可能需要暂时禁用某些特定权限才能诊断问题。
可以缩小规则,并仅允许访问
default
名称空间中的一组
特定Pod (
app:balance
):
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all-to-balance namespace: default spec: podSelector: matchLabels: app: balance ingress: - {} policyTypes: - Ingress

以下策略允许所有入口和出口流量,包括访问群集外部的任何IP:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all spec: podSelector: {} ingress: - {} egress: - {} policyTypes: - Ingress - Egress


结合多种政策
使用逻辑或在三个级别上组合策略; 每个Pod的权限是根据影响它的所有策略的相异性设置的:
1.在
from
和
to
字段中,可以定义三种类型的元素(所有元素都使用OR组合):
namespaceSelector
选择整个命名空间;podSelector
选择吊舱;ipBlock
选择一个子网。
此外,
from
/
to
小节中的元素数量(即使相同)也不受限制。 所有这些将通过逻辑或组合。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.postgres namespace: default spec: ingress: - from: - podSelector: matchLabels: app: indexer - podSelector: matchLabels: app: admin podSelector: matchLabels: app: postgres policyTypes: - Ingress

2.在策略内部,
ingress
部分可以具有许多
from
元素(通过逻辑OR组合)。 同样,
egress
部分可以包含许多to元素(也可以通过子句连接):
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.postgres namespace: default spec: ingress: - from: - podSelector: matchLabels: app: indexer - from: - podSelector: matchLabels: app: admin podSelector: matchLabels: app: postgres policyTypes: - Ingress

3.各种策略也通过逻辑或组合
但是,在组合它们时,
克里斯·库尼 指出了一个限制:Kubernetes只能组合具有不同
policyTypes
(
Ingress
或
Egress
)的策略。 定义入口(或出口)的策略将相互覆盖。
命名空间之间的关系
默认情况下,名称空间之间的信息交换是允许的。 可以使用限制到命名空间的传出和/或传入流量的禁止策略来更改此设置(请参见上面的“剥离规则”)。
通过阻止对名称空间的访问(请参见上面的“剥离规则”),可以通过使用
namespaceSelector
允许来自特定名称
namespaceSelector
连接来限制策略的例外:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: database.postgres namespace: database spec: podSelector: matchLabels: app: postgres ingress: - from: - namespaceSelector: # <<< matchLabels: namespace: default policyTypes: - Ingress

结果,
default
名称空间中的所有容器都将有权访问
database
名称空间中的
postgres
容器。 但是,如果您想仅对
default
名称空间中的特定容器开放对
postgres
访问权限,该怎么办?
按名称空间和Pod过滤
Kubernetes 1.11和更高版本允许您使用逻辑I组合
namespaceSelector
和
podSelector
运算符。它看起来像这样:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: database.postgres namespace: database spec: podSelector: matchLabels: app: postgres ingress: - from: - namespaceSelector: matchLabels: namespace: default podSelector: # <<< matchLabels: app: admin policyTypes: - Ingress

为什么将其解释为AND而不是通常的OR?
请注意,
podSelector
不能以连字符开头。 在YAML中,这意味着
podSelector
和位于其前面的
namespaceSelector
引用相同的列表项。 因此,它们由逻辑I组合。
在
podSelector
前面添加连字符将产生一个新列表项,该列表项将使用逻辑OR与先前的
namespaceSelector
组合。
要
在所有名称空间中选择带有特定标签的Pod,请输入一个空的
namespaceSelector
:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: database.postgres namespace: database spec: podSelector: matchLabels: app: postgres ingress: - from: - namespaceSelector: {} podSelector: matchLabels: app: admin policyTypes: - Ingress

多个标签与AND结合
使用逻辑或将具有许多对象(主机,网络,组)的防火墙的规则组合在一起。 如果数据包的来源与
Host_1
或
Host_2
匹配,则以下规则将起作用:
| Source | Destination | Service | Action | | ----------------------------------------| | Host_1 | Subnet_A | HTTPS | Allow | | Host_2 | | | | | ----------------------------------------|
相反,在Kubernetes中,
podSelector
或
namespaceSelector
中的各种标签由逻辑I组合。例如,以下规则将选择同时具有两个标签的Pod,
role=db
AND
version=v2
:
podSelector: matchLabels: role: db version: v2
相同的逻辑适用于所有类型的运算符:策略目标选择器,窗格选择器和名称空间选择器。
子网和IP地址(IP块)
防火墙使用VLAN,IP地址和子网来划分网络。
在Kubernetes中,IP地址是自动分配给Pod的,并且可以经常更改,因此标签用于在网络策略中选择Pod和名称空间。
ipBlocks
(
ipBlocks
)用于控制入站(入站)或出站(出站)外部(南北)连接。 例如,此策略使所有从
default
名称空间访问的Pod都可以访问Google DNS服务:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: egress-dns namespace: default spec: podSelector: {} policyTypes: - Egress egress: - to: - ipBlock: cidr: 8.8.8.8/32 ports: - protocol: UDP port: 53

此示例中的空容器选择器表示“选择名称空间中的所有容器”。
该策略仅提供对8.8.8.8的访问; 拒绝访问任何其他IP。 因此,从本质上讲,您阻止了对Kubernetes内部DNS服务的访问。 如果仍然要打开它,请明确指定它。
通常,由于
ipBlocks
中未使用
ipBlocks
的内部IP地址,因此
ipBlocks
和
podSelectors
是互斥的。
通过指定
内部IP容器 ,您实际上将允许使用这些地址与容器进行连接。 实际上,您将不知道要使用哪个IP地址,这就是为什么不应将其用于选择Pod的原因。
作为反例,以下策略包括所有IP,因此允许访问所有其他Pod:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: egress-any namespace: default spec: podSelector: {} policyTypes: - Egress egress: - to: - ipBlock: cidr: 0.0.0.0/0

您可以通过排除容器的内部IP地址来仅打开对外部IP的访问。 例如,如果您的Pod的子网是10.16.0.0/14:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: egress-any namespace: default spec: podSelector: {} policyTypes: - Egress egress: - to: - ipBlock: cidr: 0.0.0.0/0 except: - 10.16.0.0/14

端口和协议
通常,pod在一个端口上侦听。 这意味着您可以简单地省略策略中的端口号,并将所有内容保留为默认值。 但是,建议对策略进行严格限制,因此在某些情况下,您仍然可以指定端口:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.postgres namespace: default spec: ingress: - from: - podSelector: matchLabels: app: indexer - podSelector: matchLabels: app: admin ports: # <<< - port: 443 # <<< protocol: TCP # <<< - port: 80 # <<< protocol: TCP # <<< podSelector: matchLabels: app: postgres policyTypes: - Ingress

注意,
ports
选择器应用于它包含的
to
或
from
块中的所有元素。 要为不同的元素集指定不同的端口,请使用
to
或
from
输入或
egress
分成几个小节,并在每个小节中列出您的端口:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.postgres namespace: default spec: ingress: - from: - podSelector: matchLabels: app: indexer ports: # <<< - port: 443 # <<< protocol: TCP # <<< - from: - podSelector: matchLabels: app: admin ports: # <<< - port: 80 # <<< protocol: TCP # <<< podSelector: matchLabels: app: postgres policyTypes: - Ingress

默认端口起作用:
- 如果您完全省略端口的定义,则表示所有协议和所有端口。
- 如果省略协议定义,则表示TCP。
- 如果省略端口的定义,则表示所有端口。
最佳实践:不要依赖默认值,明确指定所需的内容。
请注意,有必要使用Pod端口,而不是服务(下一段中将对此进行更多说明)。
是否为Pod或服务定义了策略?
通常,Kubernetes中的Pod通过服务相互联系-虚拟负载平衡器将流量重定向到实现该服务的Pod。 您可能会认为网络策略控制对服务的访问,但事实并非如此。
Kubernetes网络策略适用于Pod端口,而不适用于服务。, 80- , 8080 pod', 8080.
: ( pod') .
Service Mesh
(, . Istio — . .) .
Ingress, Egress?
— , pod pod' , ( egress-), pod ( , , ingress-).
, .
pod-
egress -, . pod'-
. pod - , (egress) .
pod'-
,
ingress -, . pod'-. pod - , (ingress) .
. «Stateful Stateless» .
Kubernetes . , , .
Kubernetes (DNS) egress. , IP- ( aws.com).
. Kubernetes . kubectl Kubernetes , , . Kubernetes . :
kubernetes get networkpolicy <policy-name> -o yaml
, Kubernetes .
Kubernetes , API-, , Container Networking Interface (CNI). Kubernetes CNI . CNI , Kubernetes,
( — . .) , , CNI .
, Kubernetes , CNI.
Stateful Stateless?
CNI Kubernetes, , (, Calico Linux conntrack). pod' TCP- . Kubernetes, (statefulness).
Kubernetes:
- Service Mesh sidecar- . Istio .
- CNI , Kubernetes.
- Tufin Orca Kubernetes.
Tufin Orca Kubernetes ( , ).
结论
Kubernetes , . , - . .
, , .
译者的PS
另请参阅我们的博客: