网络策略官方文档: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:
为了方便理解NetworkPolicy资源及其功能,常用的术语如下:
在Ingress规则中,由from字段指定的端点称为源端点;而在Egress规则中,网络端点也称为目标端点,用to字段标识。对于未启用Ingress或Egress规则的Pod组,出站和入站流量默认均为允许。一旦在networkpolicy.spec中定义了ingress或egress字段,则它们的from或to字段就代表白名单列表,空值意味着选定所有端点,即允许相应方向上所有流量通过,此时ingress和egress字段作用与未启用流量方向设置(即spec.policyTypes字段为空)时相同。
Ingress和Egress的生效逻辑略微复杂,Egress规则生效逻辑和Ingress类似,以Ingress规则为例:
在实际环境中,有些Pod提供的服务不需要或不能公开给所有人访问,这时候就需要对它们施加访问控制。在需要管控的Pod对象所在的名称空间创建一个NetworkPolicy资源,使用spec.podSelector筛选要管控的Pod,并使用spec.ingress定义入站规则就实现入站流量管控。
spec.ingress可嵌套的from和ports均为可选字段,空值表示允许所有入站流量通过。仅定义from字段时表示入站流量的目标端口不做限制,仅定义ports字段时表示入站流量的源端点不做限制。from和ports定义在一个列表项时,它们是逻辑与关系,表示匹配那些同时满足from和ports的入站流量
下面创建一些资源,用来后续测试网络策略的功能,包括两个名称空间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,无法访问
大多数情况下,一个名称空间下的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