网络策略介绍
网络策略官方文档:https://kubernetes.io/zh-cn/docs/concepts/services-networking/network-policies/
网络策略是控制Pod之间如何进行通信的规则,它使用标签来筛选Pod,并在该组Pod之上定义规则来定义管控其流量,从而为k8s提供更精细的流量控制和租户隔离机制。管理员和用户可以通过NetworkPolicy资源按需定义网络访问控制策略
网络策略的具体实现要依靠CNI网络插件完成,例如Calico、Weave和Antrea等。因此,仅在使用支持网络策略的网络插件时才能让自定义的网络策略生效。不同的网络插件实现网络策略的方式也是不一样的。
本文以Calico网络插件为例,它的calico-kube-contollers是用于将用户自定义的网络策略进行实现的组件,它主要依赖于在节点上构建iptales规则实现访问控制功能。
默认情况下,k8s并未对Pod的流量做任何限制。Pod对象能够与集群上其他任何Pod通信,也能够与集群外部的网络端点通信。NetworkPolicy是名称空间级别资源,允许用户在通过标签选择器筛选的一组Pod上分别管理入站(Ingress)和出站(Egress)流量。将NetworkPolicy应用到名称空间中后,被标签选择器选中的Pod将默认拒绝所有流量,仅放行由NetworkPolicy资源明确允许的流量。未被NetworkPolicy资源的标签选择器选中的Pod对象的流量则不受影响。
NetworkPolicy是Kubernetes API中标准的资源类型,它的部署文件常用字段如下:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ...
namespace: ...
spec:
podSelector: <Object> #Pod标签选择器,用来在当前名称空间下筛选出一组Pod,之后定义的出入站规则对此组Pod生效
policyTypes: <[]string> #网络策略类型,Ingress表示对入站流量限制;Egress表示对出站流量限制,同时提供表示都限制
ingress: #允许通过的入站流量的规则定义,空值表示允许所有的入站流量通过
- from: #允许通过的入站流量的源端点列表,空值表示所有端点
- ipBlock: #用IP地址范围匹配源端点,不能与下面的namespacesSelector和podSelector同时使用
cidr: <string> #源端点的IP网段
except: <[]string> #从cidr中排除的网段范围或地址,这些源端点不能访问指定的Pod
namespacesSelector: <Object> #由标签选择器筛选的名称空间中的端点,空值表示所有名称空间
podSelector: <Object> #由标签选择器筛选的Pod端点,空值表示none
ports: <[]Object> #入站流量的目标端口列表,空值表示所有端口
egress: #允许通过的出站流量的规则定义,空值表示允许所有出站流量通过
- to: <[]Object> #允许出站流量访问的目标端点对象列表,空值表示所有端点,可嵌套字段通ingress.from
ports: <[]Object> #允许出站流量访问的目标端口对象列表,空值表示所有端口
为了方便理解NetworkPolicy资源及其功能,常用的术语如下:
- Pod组:由NetworkPolicy通过标签选择器筛选出的一组Pod,它们是网络策略规则管控的目标
- Egress规则:出站流量相关的规则,负责管控选定的Pod组发往其它端点的流量,可由流量的目标端点(spec.egress.to)和目标端口(spec.egress.ports)来定义
- Ingress规则:入站流量的相关规则,负责管控选定的Pod组所能接收的流量,可由流量的源端点(spec.ingress.from)和流量的目标端口(spec.ingress.ports)来定义
- 对端端点:与选定的Pod组交互的对端主机,可以由CIDR格式的地址段(ipBlock)来匹配指定地址段内的Pod对象、名称空间选择器(namespacesSelector)来匹配名称空间内的所有Pod对象,也可以结合Pod选择器(podSelector)在指定名称空间选出一组Pod对象
在Ingress规则中,由from字段指定的端点称为源端点;而在Egress规则中,网络端点也称为目标端点,用to字段标识。对于未启用Ingress或Egress规则的Pod组,出站和入站流量默认均为允许。一旦在networkpolicy.spec中定义了ingress或egress字段,则它们的from或to字段就代表白名单列表,空值意味着选定所有端点,即允许相应方向上所有流量通过,此时ingress和egress字段作用与未启用流量方向设置(即spec.policyTypes字段为空)时相同。
Ingress和Egress的生效逻辑略微复杂,Egress规则生效逻辑和Ingress类似,以Ingress规则为例:
- 定义了spec.policyTypes值为Ingress,但未定义spec.ingress字段时,它无法匹配任何流量,因此选定的Pod组不接受任何端点访问
- 定义了spec.policyTypes值为Ingress,但使用空值的spec.ingress或spec.ingress.from字段,表示匹配所有的端点,因而选出的Pod组可以被任意端点访问
- 最后,即使Egress规则拒绝所有流量,但由Ingress规则放行的请求流量的响应报文依然能够正常出站,它并不受限于Egress规则的限制,反之亦然
Ingress规则
在实际环境中,有些Pod提供的服务不需要或不能公开给所有人访问,这时候就需要对它们施加访问控制。在需要管控的Pod对象所在的名称空间创建一个NetworkPolicy资源,使用spec.podSelector筛选要管控的Pod,并使用spec.ingress定义入站规则就实现入站流量管控。
spec.ingress可嵌套的from和ports均为可选字段,空值表示允许所有入站流量通过。仅定义from字段时表示入站流量的目标端口不做限制,仅定义ports字段时表示入站流量的源端点不做限制。from和ports定义在一个列表项时,它们是逻辑与关系,表示匹配那些同时满足from和ports的入站流量
- ingress.from字段
from字段的值是一个列表,用于指定访问目标Pod组入站流量的来源,可嵌套使用ipBlock、namespaceSelector和podSelector 3个字段。这3个字段匹配Pod的方式各有不同,且ipBlock不能和另外两个字段同时使用,同时使用namespaceSelector和podSelector这两个字段时它们是逻辑与关系。from下的多个列表项是逻辑或关系,入站流量只要能匹配其中一个就可以。- ipBlock:根据IP地址或网络地址来匹配源端点
- namespaceSelector:使用标签选择器筛选名称空间,它将匹配筛选出的名称空间内的所有Pod对象;空值表示匹配所有名称空间,即源端点可以是集群上的任意Pod对象
- podSelector:在NetworkPolicy资源所在的名称空间中基于标签选择器筛选Pod对象,空值表示选中当前名称空间所有Pod对象。与namespaceSelector同时使用时,作用域为namespaceSelector筛选出的名称空间
- ingress.ports 字段
ports字段的值也是一个对象列表,用于定义入站流量可以访问的目标端口,可以嵌套使用port和protocol字段- port:端口号或在container上定义的端口名称,未定义时匹配所有端口
- protocol: 协议,TCP或UDP,默认TCP
下面创建一些资源,用来后续测试网络策略的功能,包括两个名称空间project1和project2,在project1名称空间下部署一个nginx和tomcat,在project名称空间下部署一个nginx,部署文件如下:
apiVersion: v1
kind: Namespace
metadata:
name: project1
labels:
name: project1
---
apiVersion: v1
kind: Namespace
metadata:
name: project2
labels:
name: project2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-project1
namespace: project1
spec:
replicas: 1
selector:
matchLabels:
app: nginx
project: project1
template:
metadata:
labels:
app: nginx
project: project1
tier: fronted
spec:
containers:
- name: nginx
image: nginx
ports:
- name: http
containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-project1
namespace: project1
spec:
replicas: 1
selector:
matchLabels:
app: tomcat
project: project1
tier: backend
template:
metadata:
labels:
app: tomcat
project: project1
tier: backend
spec:
containers:
- name: tomcat
image: tomcat:10.1.2
ports:
- name: http
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-project1
namespace: project1
spec:
type: NodePort
selector:
app: nginx
project: project1
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-project2
namespace: project2
spec:
replicas: 1
selector:
matchLabels:
app: nginx
project: project2
template:
metadata:
labels:
app: nginx
project: project2
spec:
containers:
- name: nginx
image: nginx
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-project2
namespace: project2
spec:
type: NodePort
selector:
app: nginx
project: project2
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
示例1
限定指定的标签的Pod才能访问project1名称空间下的tomcat pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy-demo1
namespace: project1
spec:
policyTypes: ["Ingress"]
podSelector: #规则针对tomcat Pod生效
matchLabels:
app: tomcat
project: project1
tier: backend
ingress:
- from:
- podSelector: #拥有下面指定标签的Pod才能访问tomcat Pod,默认是在当前名称空间下筛选
matchLabels:
app: nginx
project: project1
tier: fronted
ports: #只能访问tomcat Pod的8080端口
- port: 8080
protocol: TCP
查看networkpPolicy资源
用同名称空间下nginx pod访问tomcat Pod,可以访问,因为nginx pod的标签符合NetworkPolicy的设定
在default名称空间中使用clinet-pod访问project1名称空间下的tomcat,无法访问,因为client-pod的标签不符合NetworkPolicy的设定
示例2
通过ip地址限定可以访问tomcat的Pod
kind: NetworkPolicy
metadata:
name: network-policy-demo2
namespace: project1
spec:
policyTypes: ["Ingress"]
podSelector:
matchLabels:
app: tomcat
project: project1
tier: backend
ingress:
- from:
- ipBlock:
cidr: 10.244.0.0/16
except:
- 10.244.169.0/24
ports:
- port: 8080
protocol: TCP
在node-01和node-02上分别运行一个client-pod,然后去访问tomcat,node-01上的client-pod可以访问,node-02上的client-pod不能访问,因为它属于10.244.169.0/24网段
示例3
通过名称空间限定可以访问tomcat Pod
kind: NetworkPolicy
metadata:
name: network-policy-demo3
namespace: project1
spec:
policyTypes: ["Ingress"]
podSelector:
matchLabels:
app: tomcat
project: project1
tier: backend
ingress:
- from: #限定只有project2名称空间下具有app=nginx的Pod才能访问tomcat
- podSelector: #podSlector筛选app=nginx的Pod
matchLabels:
app: nginx
namespaceSelector: #namespaceSelector和podSlector是逻辑与关系,表示在namespaceSelector筛选出的名称空间下用podSlector筛选Pod
matchLabels:
name: project2
ports:
- port: 8080
protocol: TCP
在project1名称空间下用nginx-pod访问tomcat-pod,不能访问
在project2名称空间下用nginx-pod访问tomcat-pod,可以访问
在project2名称空间下使用其它pod访问tomcat,无法访问
Egress规则
大多数情况下,一个名称空间下的Pod资源总有对外请求的需求,例如向CoreDns请求名称解析等。因此,通常应该将出站流量的默认策略设置为允许通过。但如果要实现更精细的控制,仅放行有对外请求必要的Pod对象的出站流量,可以通过Egress实现。
spec.egress字段用于定义出站流量规则,它可以嵌套使用to和ports字段,to用于定义选定的Pod组的出站流量可访问的目标端点,其格式和逻辑与ingress.from一致;ports用于定义选定的Pod组的出站流量可访问的目标端口。
下面是一个示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy-egress-demo1
namespace: project1
spec:
policyTypes: ["Egress"] #网络策略类型为Egress
podSelector: #对project1名称空间下的nginx-pod进行限制
matchLabels:
app: nginx
project: project1
tier: fronted
ingress:
- to: #Egress条件1,限制nginx只能访问同名称空间下的tomcat-pod的8080端口
- podSelector:
matchLabels:
app: tomcat
project: project1
tier: backend
ports:
- port: 8080
protocol: TCP
- to: #Egress条件2,限制nginx只能访问192.168.211.0/24网段的主机的任何端口(不定义ports字段表示不限制)
- ipBlock:
cidr: 192.168.211.0/24
测试在project1名称空间下nginx-pod访问同名称空间下的tomcat-pod,可以正常访问
测试在project1名称空间下nginx-pod访问192.168.211.12:80,可以正常访问
测试在project1名称空间下nginx-pod访问coredns服务,解析域名失败,无法访问
综合示例
下面是一个综合示例,用于实现名称空间隔离。它大致实现了以下几点需求:
1.本 名称空间内的Pod可以互相访问
2. 集群上管理类应用所在名称空间中的Pod(例如日志收集、监控、dashboard等)可以访问本名称空间中的Pod
3. 本名称空间中的Pod可以访问CoreDns、Kubernetes API等必要的服务
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-networkpolicy
namespace: project1
spec:
policyTypes: ["Ingress", "Egress"]
podSelector: {} #project1名称空间下所有Pod对象
ingress:
- from: #入站规则1,允许指定名称空间中的Pod访问任意端口
- namespaceSelector:
matchExpressions:
- {key: "name", operator: In, values: ["kube-system", "kube-dashboard", "log", "monitor", "project1"]}
egress:
- to: #出战规则1,允许访问同名称空间下Pod的任意端口
- namespaceSelector:
matchLabels:
name: project1
- to: [] #出站规则2,允许访问任何端点的UDP 53端口,DNS服务
ports:
- port: 53
protocol: UDP
- to: #出站规则3,允许访问kube-apierver
- podSelector:
matchLabels:
component: kube-apiserver
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- port: 443
protocol: TCP