网络策略(Network Policy),用于限制 Pod 出入流量,提供 Pod 级别和 Namespace 级别网络访问控制。
已知 flannel 目前并不支持网络策略,即使创建了也并不会应用,其它组件在使用网络策略时还请自行了解以下是否支持。
应用场景
- 应用程序(Pod)间的访问控制。例如微服务 A ,允许访问微服务 B ,微服务 C 不能访问微服务 A
- 开发环境命名空间不能访问测试环境命名空间 Pod
- 当 Pod 暴露到外部时,需要做 Pod 白名单
- 多租户网络环境隔离(使用项目较多)
Pod 网络隔离
其实是和物理机的网络隔离概念是同理的,分为 入口隔离 和 出口隔离
Pod 网络入口方向隔离
- 基于 Pod 级网络隔离:只允许特定对象访问 Pod(使用标签定义),允许白名单上的 IP 地址或者 IP 段访问 Pod
- 基于 Namespace 级别网络隔离:多个命名空间,A 和 B 命名空间 Pod 完全隔离
Pod 网络出口方向隔离
- 拒绝某个 Namespace 上所有的 Pod 访问外部
- 基于目的 IP 的网络隔离:只允许 Pod 访问白名单上的 IP 地址或者 IP 段
- 基于目标端口的网络隔离:只允许 Pod 访问白名单上的端口
应用示例1
需求:将 default 命名空间携带 app=web 标签的 pod 隔离,只允许 default 命名空间携带 run=client1 标签的 pod 访问 80 端口。
首先分析需求,将 default 命名空间携带 app=web 标签的 pod 隔离:意思是要对 app=web 标签的 pod 使用网络策略。只允许 default 命名空间携带 run=client1 标签的 pod 访问 80 端口:意思是携带 run=client1 标签的 pod 可以访问携带 run=web 标签的 pod 的80端口
准备测试环境
# 创建一个nginx pod
kubectl create deployment web --image=nginx
# 创建两个访问基础pod
kubectl run client1 --image=busybox:1.28 --command -- sleep 36000
kubectl run client2 --image=busybox:1.28 --command -- sleep 36000
查看测试环境:label这里使用的就是默认的 label 来做策略,所以不需要单独写 yaml 指定 label
# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
client1 1/1 Running 0 1m15s run=client1
client2 1/1 Running 0 1m10s run=client2
web-96d5df5c8-sd9th 1/1 Running 0 1m20s app=web,pod-template-hash=96d5df5c8
以上需求中的标签是默认生成的,改成自己需要的 label 即可。
在不使用网络策略的情况下,查看 web pod 的 ip ,进入 client1 中尝试访问,可以 ping 通,也可以 wget 到页面。client2 也是一样的。
$ kubectl exec -it client1 -- sh
/ # ping 192.168.146.100
PING 192.168.146.100 (192.168.146.100): 56 data bytes
64 bytes from 192.168.146.100: seq=0 ttl=63 time=0.239 ms
64 bytes from 192.168.146.100: seq=1 ttl=63 time=0.133 ms
/ # wget 192.168.146.100
Connecting to 192.168.146.100 (192.168.146.100:80)
index.html 100% |********************************************| 612 0:00:00 ETA
/ # cat index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
验证网络策略
清楚了需求,再来编写 yaml 文件
$ vim test-np.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-np
namespace: default
spec:
podSelector: # 要针对哪些pod实施这个网络策略,根据pod的label选择
matchLabels:
app: web
policyTypes: # 策略类型,指定策略用于入站、出站流量
- Ingress # 入站策略,与前面的ingress区分,不一样
ingress: # 入站策略,以下两个策略合起来就是,default命名空间中的
- from:
- namespaceSelector: # namespace白名单,以下namespace中的pod可以访问
matchLabels:
project: default
- podSelector: # pod白名单,具有以下label的pod可以访问
matchLabels:
run: client1
ports: # 允许访问使用策略的pod的端口号
- protocol: TCP
port: 80 # 端口号
使用网络策略
kubectl apply -f test-np.yaml
查看创建好的策略
$ kubectl get networkpolicies
NAME POD-SELECTOR AGE
test-np app=web 4m4s
限制网络策略已经被应用,按照需求,现在 client1 应该可以访问到 web 的 80 端口的,client2 并访问不到
# client1
$ kubectl exec -it client1 -- sh
/ # ping 192.168.146.100
PING 192.168.146.100 (192.168.146.100): 56 data bytes
^C
--- 192.168.146.100 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
/ # wget 192.168.146.100
Connecting to 192.168.146.100 (192.168.146.100:80)
index.html 100% |********************************************| 612 0:00:00 ETA
# client2
$ kubectl exec -it client2 -- sh
/ # ping 192.168.146.100
PING 192.168.146.100 (192.168.146.100): 56 data bytes
^C
--- 192.168.146.100 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
/ # wget 192.168.146.100 # 很长时间才会报出错误
Connecting to 192.168.146.100 (192.168.146.100:80)
wget: can't connect to remote host (192.168.146.100): Connection timed out
通过以上访问验证,发现 client1 不可以 ping,但可以访问到 web 的 80 端口,而 client2 即 ping 不通,也访问不到 80 端口。已经达到了提出的需求。
应用示例2
为避免影响示例,将示例1的策略删除
kubectl delete -f test-np.yaml
需求:default 命名空间下所有 pod 可以互相访问,也可以访问其它命名空间 pod,但其它命名空间不能访问 default 命名空间 pod
分析需求,正常没有任何策略的情况下,pod 间都可以互相访问,包括不同命名空间也是一样可以访问,也就是只需要做到其它命名空间不能访问 default 命名空间的 pod 即可。入站策略就可以做到了
准备测试环境
依然使用示例1中的 web pod,因为涉及到多个命名空间,将 client1 在 kube-system 命名空间进行创建
kubectl run client1 --image=busybox:1.28 -n kube-system --command -- sleep 36000
在不使用网络策略的情况下,查看 web pod 的 ip ,进入 client1 中尝试访问,可以 ping 通,也可以 wget 到页面
$ kubectl exec -it client1 -n kube-system -- sh
/ # ping 192.168.146.100
PING 192.168.146.100 (192.168.146.100): 56 data bytes
64 bytes from 192.168.146.100: seq=0 ttl=63 time=0.280 ms
^C
--- 192.168.146.100 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.280/0.280/0.280 ms
/ # wget 192.168.146.100
Connecting to 192.168.146.100 (192.168.146.100:80)
index.html 100% |********************************************| 612 0:00:00 ETA
验证网络策略
清楚了需求,再来编写 yaml 文件
spec.podSelector: {} //如果为空,默认为对应命名空间下的所有pod应用该策略
spec.ingress.from.podSelector: {} //如果为空,默认只允许应用策略所在命名空间的pod之间访问,不允许非本命名空间的所有pod访问本命名空间的 pod。也就是需求中的 其它命名空间不能访问 default 命名空间 pod
$ vim test-np2.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-np2
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
使用网络策略
kubectl apply -f test-np2.yaml
查看创建好的策略
$ kubectl get networkpolicies
NAME POD-SELECTOR AGE
test-np2 <none> 37s
按照需求,现在 kube-system 命名空间的 client1 是访问不到 web 的。
$ kubectl exec -it client1 -n kube-system -- sh
/ # ping 192.168.146.100
PING 192.168.146.100 (192.168.146.100): 56 data bytes
策略实现原理
当用户创建好 networkpolicy 后,提交到 api,并写入到 etcd 中存储。有了网络策略后,最终的实现是由部署在集群中 CNI 插件来完成的。每个节点的 CNI pod 通过 API 获取到网络策略,并应用在自己节点,使用 iptables 或者 ipvs 来做网络策略。