前言:
networkPolicy是kubernetes集群的一个重要安全特性。顾名思义,网络策略,控制网络流量的一个资源。
那么,kubernetes集群的网络是由docker虚拟网卡,cni网络插件,flannel网络插件(也可能会使用calico,weaver等等其它网络插件)这些模块组成的。
主要还是基于Linux内核层面的iptables或者ipvs通过上述的网络插件使得整个集群的网络成为网络层次有若干个子网的,内部是可以跨节点,跨子网段的一个整体网络。
例如,我有一个kubeadm部署的集群,集群内部网络如下:
两个子网段,10.244.36 和10.244.169
root@k8s-master:~# kubectl get po -A -owide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES a a 1/1 Running 1 (5m27s ago) 13h 10.244.36.76 k8s-node1 <none> <none> b b 1/1 Running 1 (5m27s ago) 13h 10.244.36.69 k8s-node1 <none> <none> default busybox 1/1 Running 0 49s 10.244.36.79 k8s-node1 <none> <none> default front-end-5f64577768-tsq76 1/1 Running 14 (5m27s ago) 9d 10.244.36.75 k8s-node1 <none> <none> default guestbook-86bb8f5bc9-2nk6c 1/1 Running 13 (5m27s ago) 8d 10.244.36.82 k8s-node1 <none> <none> default guestbook-86bb8f5bc9-2xrh6 1/1 Running 13 (5m27s ago) 8d 10.244.36.78 k8s-node1 <none> <none> default guestbook-86bb8f5bc9-78pq5 1/1 Running 13 (5m27s ago) 8d 10.244.36.73 k8s-node1 <none> <none> default guestbook-86bb8f5bc9-m4wr4 1/1 Running 14 (5m21s ago) 9d 10.244.169.153 k8s-node2 <none> <none> default guestbook-86bb8f5bc9-pkzpl 1/1 Running 14 (5m21s ago) 9d 10.244.169.152 k8s-node2 <none> <none> default guestbook-86bb8f5bc9-sq4xf 1/1 Running 13 (5m21s ago) 8d 10.244.169.155 k8s-node2 <none> <none> 剩下的略略略
OK,此时在集群内部启动一个临时的pod,以上的集群内部IP是都可以ping通的:
kubectl run busybox -it --rm --image=busybox -- /bin/sh
/ # ping 10.244.36.80 -c 2 PING 10.244.36.80 (10.244.36.80): 56 data bytes 64 bytes from 10.244.36.80: seq=0 ttl=63 time=0.119 ms 64 bytes from 10.244.36.80: seq=1 ttl=63 time=0.129 ms --- 10.244.36.80 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.119/0.124/0.129 ms ping 10.244.169.147 -c 2 PING 10.244.169.147 (10.244.169.147): 56 data bytes 64 bytes from 10.244.169.147: seq=0 ttl=62 time=0.737 ms 64 bytes from 10.244.169.147: seq=1 ttl=62 time=0.802 ms
那就说明了一个问题,kubernetes集群的网络是一个自由的,无任何限制的网络,很显然,这样的网络是没有安全性可言的,因为,任意一个pod都可以连接到其它的pod,那么,如果有某一个不受控制的黑客部署的pod在集群内,不是非常的不安全吗?
其实说了这么多,基于网络插件比如calico的networkPolicy会对集群内的网络做一个细粒度的控制,例如,控制某类带有特定标签的pod能够访问其它的指定的pod,简单的说人话就是能够做一定的网络隔离。
Kubernetes提供了NetworkPolicy,支持按Namespace和按Pod级别的网络访问控制。它利用label指定namespaces或pod,底层用iptables实现。不是所有的 Kubernetes 网络方案都支持 Network Policy。比如 Flannel 就不支持,Calico 是支持的。
例如,calico网络方案的networkPolicy工作流程是这样的:
a.通过kubectl client创建network policy资源;
b.calico的policy-controller监听network policy资源,获取到后写入calico的etcd数据库;
c.node上calico-felix从etcd数据库中获取policy资源,调用iptables做相应配置。
使用network policy
资源可以配置pod
的网络,networkPolicy
是namespace scoped
的,他只能影响某个namespace
下的pod
的网络出入站规则。
你首先需要有一个支持网络策略的 Kubernetes 集群。已经有许多支持 NetworkPolicy 的网络提供商,包括:
一,
ingress和egress
ingress 表示进口流量,egress表示出口流量,入口流量和出口流量都是相对networkPolcy所指定的namespace来说的,例如下面这个policy:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access-nginx spec: podSelector: matchLabels: app: nginx ingress: - from: - podSelector: matchLabels: access: "true"
以上这个policy意思是所有具有标签 access=true的pod才可以访问namespace为default的所有具有pod 标签 app=nginx的pod
OK,此时查看default这个命名空间下的所有pod的具有app=nginx的pod:
root@k8s-master:~# kubectl get po -owide --show-labels |grep nginx nginx-6799fc88d8-ktjxh 1/1 Running 5 (80m ago) 8d 10.244.169.140 k8s-node2 <none> <none> access=true,app=nginx,pod-template-hash=6799fc88d8 nginx-kusc0041 1/1 Running 11 (80m ago) 12d 10.244.169.141 k8s-node2 <none> <none> run=nginx-kusc0041 task-2-ds-fcm5l 1/1 Running 13 (80m ago) 14d 10.244.235.193 k8s-master <none> <none> controller-revision-hash=688c88fb84,nginx=task-2-ds,pod-template-generation=1 task-2-ds-nwdlv 1/1 Running 19 (80m ago) 18d 10.244.169.135 k8s-node2 <none> <none> controller-revision-hash=688c88fb84,nginx=task-2-ds,pod-template-generation=1 task-2-ds-pmlqw 1/1 Running 19 (80m ago) 18d 10.244.36.81 k8s-node1 <none> <none> controller-revision-hash=688c88fb84,nginx=task-2-ds,pod-template-generation=1
此时,带标签access=true的pod可以访问140,如果不带access=true,将不可以访问140
不带标签的时候:
kubectl run test -it --rm --image=busybox -- /bin/sh If you don't see a command prompt, try pressing enter. / # wget 10.244.169.141 Connecting to 10.244.169.141 (10.244.169.141:80) saving to 'index.html' index.html 100% |***************************************************************************************************************************************| 615 0:00:00 ETA 'index.html' saved / # wget 10.244.169.140 Connecting to 10.244.169.140 (10.244.169.140:80) ^C
带符合的标签的时候:
root@k8s-master:~# kubectl run test -it --rm --image=busybox --labels="access=true" -- /bin/sh If you don't see a command prompt, try pressing enter. / # wget 10.244.169.140 Connecting to 10.244.169.140 (10.244.169.140:80) saving to 'index.html' index.html 100% |***************************************************************************************************************************************| 615 0:00:00 ETA 'index.html' saved
那么,有一点需要注意了,podSelector 前面加 -号和 不加-号的,如有-号表示范围扩大,这个时候是两个条件任意一个符合即可(逻辑关系是或)。如无-号表示精确匹配pod的label标签(逻辑关系是与),还是以上面的例子为例:
无减号,表示精确的匹配
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access-nginx spec: podSelector: matchLabels: app: nginx ingress: - from: - namespaceSelector: {} podSelector: matchLabels: access: "true"
有减号,表示或者,范围匹配
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access-nginx spec: podSelector: matchLabels: app: nginx ingress: - from: - namespaceSelector: {} - podSelector: matchLabels: access: "true"
二,
实验1:
创建一个networkPolicy,使得名为a的namespace内的pod全部隔离,只有具有标签 access=true的pod才可以访问a namespace内的其它pod
网络策略未创建前:
OK,先创建名为a的namespace,并且在该namespace内创建两个都使用nginx镜像的名称分别为nginx1和nginx2
kubectl create ns a kubectl run nginx1 --image=nginx -n a kubectl run nginx2 --image=nginx -n a
查看pod的状态,可以看到有绑定两个IP,分别是10.244.36.86,10.244.36.96
root@k8s-master:~# kubectl get po -n a -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx1 1/1 Running 1 (123m ago) 4h39m 10.244.36.86 k8s-node1 <none> <none> nginx2 1/1 Running 0 97m 10.244.36.96 k8s-node1 <none> <none>
OK,这个时候我们进入nginx1,看看a 这个namespace内的pod是否互相隔离:
进入pod的命令:
kubectl exec -it po -n a nginx1 -- /bin/sh
可以发现,两个pod之间是可以直接互相访问的,没有任何的阻碍,当然了,其它的namespace内的pod也是没有任何阻碍的可以访问到
# curl 10.244.36.96 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } # curl 10.244.36.86 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style>
网络策略创建后:
root@k8s-master:~# cat test.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access-nginx namespace: a spec: podSelector: {} ingress: - from: - podSelector: matchLabels: access: "true"
OK,这个网络策略表示名为a的namespace内的所有pod互相直接隔离,只有具有access=true的pod才可以访问a 这个namespace内的其它的pod。
上述文件执行后,此时,在进入nginx1,可以发现无法访问nginx2了:
# curl 10.244.36.96 ^C
但可以自由的访问其它的namespace内的pod,证明网络策略的作用范围只在a 这个namespace内:
root@k8s-master:~# kubectl get po -A -owide |grep 10.244.169.151 default nginx-6799fc88d8-ktjxh 1/1 Running 2 (141m ago) 22h 10.244.169.151 k8s-node2 <none> <none>
curl 10.244.169.151 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; }
OK,现在给nginx1增加符合网络策略进入策略的标签acces=true,然后登陆nginx1,再次访问nginx2,可以发现可以正常访问了:
给nginx1添加标签:
kubectl label po -n a nginx1 access=true root@k8s-master:~# kubectl get po -n a nginx1 --show-labels NAME READY STATUS RESTARTS AGE LABELS nginx1 1/1 Running 1 (152m ago) 5h8m access=true,run=nginx1
再次访问,可以发现恢复正常了,证明网络策略是生效了。
# curl 10.244.36.96 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style>
由以上实验我们可以得出一个结论:
1,namespace是networkPolicy的作用域
2,from表示方向,因此,上面的例子,标签是打在了nginx1,然后登陆的nginx1,那么,如果标签打到了nginx2上,就需要使用nginx2访问nginx1了。
三,
实验2
某个namespace(这里还是使用a这个namespace),拒绝所有的入站流量和出站流量
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: a spec: podSelector: {} policyTypes: - Ingress - Egress
此时,在default这个namespace里有一个nginx,登陆这个pod,可以发现a这个namespace里的所有pod都无法访问了:
root@k8s-master:~# kubectl get po -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES busybox 1/1 Running 1 (3h12m ago) 7h25m 10.244.36.105 k8s-node1 <none> <none> front-end-5f64577768-tsq76 1/1 Running 15 (3h12m ago) 10d 10.244.36.99 k8s-node1 <none> <none> nfs-client-provisioner-56dd5765dc-9z772 1/1 Running 30 (3h12m ago) 10d 10.244.169.162 k8s-node2 <none> <none> nginx-6799fc88d8-ktjxh 1/1 Running 2 (3h12m ago) 23h 10.244.169.151 k8s-node2 <none> <none>
root@k8s-master:~# kubectl exec -it -n default nginx-6799fc88d8-ktjxh -- /bin/sh # curl 10.244.36.96 curl: (28) Failed to connect to 10.244.36.96 port 80: Connection timed out