Kubernetes(k8s)網路策略NetworkPolicy

2023-06-14 12:11:57

一.系統環境

本文主要基於Kubernetes1.21.9和Linux作業系統CentOS7.4。

伺服器版本 docker軟體版本 Kubernetes(k8s)叢集版本 CPU架構
CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 v1.21.9 x86_64

Kubernetes叢集架構:k8scloude1作為master節點,k8scloude2,k8scloude3作為worker節點。

伺服器 作業系統版本 CPU架構 程序 功能描述
k8scloude1/192.168.110.130 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master節點
k8scloude2/192.168.110.129 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker節點
k8scloude3/192.168.110.128 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker節點

二.前言

Kubernetes是目前最流行的容器編排平臺之一,它可以自動化地部署、擴充套件和管理容器化應用程式。但是,在部署一個大規模的容器叢集時面臨一個問題就是如何保護網路安全。這時候,我們就需要使用網路策略(NetworkPolicy)。

本文將介紹Kubernetes中的網路策略(NetworkPolicy)。我們將深入瞭解什麼是網路策略,並如何在Kubernetes叢集中使用網路策略來保護網路安全。

使用網路策略NetworkPolicy的前提是已經有一套可以正常執行的Kubernetes叢集,關於Kubernetes(k8s)叢集的安裝部署,可以檢視部落格《Centos7 安裝部署Kubernetes(k8s)叢集》https://www.cnblogs.com/renshengdezheli/p/16686769.html。

三.網路策略(NetworkPolicy)簡介

Kubernetes網路策略(NetworkPolicy)是一個資源物件,主要用於定義Pod之間的流量控制,其實現了一個基於標籤的選擇器模型,允許管理員通過網路策略規則限制對Pod的流量存取。

網路策略(NetworkPolicy)是以Pod為單位進行授權的,因此,只有當所有的Pod都通過了網路策略時,才能夠接收到其他Pod傳送的流量。這種方式極大提高了網路的安全性。

四.建立pod和svc

建立存放網路策略的目錄

[root@k8scloude1 ~]# mkdir network

建立namespace

[root@k8scloude1 ~]# kubectl create ns network
namespace/network created

切換namespace

[root@k8scloude1 ~]# kubens network
Context "kubernetes-admin@kubernetes" modified.
Active namespace is "network".

為了方便進行curl測試網路,所有節點下載busybox-curl 映象(內建curl命令)

[root@k8scloude1 ~]# docker pull yauritux/busybox-curl 

[root@k8scloude2 ~]# docker pull yauritux/busybox-curl

[root@k8scloude3 ~]# docker pull yauritux/busybox-curl

pod的yaml檔案如下,功能是使用Nginx映象建立pod。

[root@k8scloude1 ~]# cd network/

[root@k8scloude1 network]# vim pod.yaml 

[root@k8scloude1 network]# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: podtest
  name: podtest
spec:
  #當需要關閉容器時,立即殺死容器而不等待預設的30秒優雅停機時長。
  terminationGracePeriodSeconds: 0
  containers:
  - name: nginx
    image: nginx
    #imagePullPolicy: IfNotPresent:表示如果本地已經存在該映象,則不重新下載;否則從遠端 Docker Hub 下載該映象
    imagePullPolicy: IfNotPresent

建立兩個nginx pod用於測試使用。

[root@k8scloude1 network]# sed 's/podtest/pod1/' pod.yaml | kubectl apply -f -
pod/pod1 created

[root@k8scloude1 network]# sed 's/podtest/pod2/' pod.yaml | kubectl apply -f -
pod/pod2 created

[root@k8scloude1 network]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          12s   10.244.112.129   k8scloude2   <none>           <none>
pod2   1/1     Running   0          8s    10.244.251.203   k8scloude3   <none>           <none>

修改nginx的首頁檔案index.html,用於區分兩個pod。

[root@k8scloude1 network]# kubectl exec -it pod1 -- sh -c "echo 111 >/usr/share/nginx/html/index.html"

[root@k8scloude1 network]# kubectl exec -it pod2 -- sh -c "echo 222 >/usr/share/nginx/html/index.html"

存取pod,首頁檔案已經改變。

[root@k8scloude1 network]# curl 10.244.112.129
111

[root@k8scloude1 network]# curl 10.244.251.203
222

給pod建立svc服務。

[root@k8scloude1 network]# kubectl expose --name=pod1svc pod pod1 --port=80
service/pod1svc exposed

[root@k8scloude1 network]# kubectl expose --name=pod2svc pod pod2 --port=80
service/pod2svc exposed

[root@k8scloude1 network]# kubectl get svc -o wide
NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE   SELECTOR
pod1svc   ClusterIP   10.104.66.174    <none>        80/TCP    36s   test=pod1
pod2svc   ClusterIP   10.108.254.110   <none>        80/TCP    17s   test=pod2

五.沒有網路策略的條件下存取pod

檢視網路策略,現在沒有任何的網路策略。

[root@k8scloude1 network]# kubectl get networkpolicy
No resources found in network namespace.

建立一個臨時pod作為使用者端,用於存取pod1和pod2,在沒有任何網路策略的條件下,可以存取pod1svc和pod2svc。

[root@k8scloude1 network]# kubectl run podclient --image=yauritux/busybox-curl:latest -it --rm --image-pull-policy=IfNotPresent -- sh 
If you don't see a command prompt, try pressing enter.

/home # curl pod1svc
111
/home # curl pod2svc
222
/home # exit
Session ended, resume using 'kubectl attach podclient -c podclient -i -t' command when the pod is running
pod "podclient" deleted

建立一個臨時pod作為使用者端,-n default:進入容器並切換到default名稱空間。

[root@k8scloude1 network]# kubectl run podclient --image=yauritux/busybox-curl:latest -n default -it --rm --image-pull-policy=IfNotPresent -- sh 
If you don't see a command prompt, try pressing enter.

/home # curl pod1svc
curl: (6) Couldn't resolve host 'pod1svc'

#跨名稱空間存取需要指定namespace
/home # curl pod1svc.network
111
/home # curl pod2svc.network
222

/home # exit
Session ended, resume using 'kubectl attach podclient -c podclient -i -t' command when the pod is running
pod "podclient" deleted

刪除svc。

[root@k8scloude1 network]# kubectl delete svc pod1svc
service "pod1svc" deleted

[root@k8scloude1 network]# kubectl delete svc pod2svc
service "pod2svc" deleted

給pod建立svc,為了讓叢集外的機器能存取svc服務,我們指定svc的釋出方式為Loadbalancer,注意使用LoadBalancer的svc釋出方式需要用到METALLB建立地址池,詳情請檢視部落格《Kubernetes(k8s)服務service:service的發現和service的釋出》。

[root@k8scloude1 network]# kubectl expose --name=pod1svc pod pod1 --port=80 --type=LoadBalancer
service/pod1svc exposed

[root@k8scloude1 network]# kubectl expose --name=pod2svc pod pod2 --port=80 --type=LoadBalancer
service/pod2svc exposed

[root@k8scloude1 network]# kubectl get svc -o wide
NAME      TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE   SELECTOR
pod1svc   LoadBalancer   10.103.187.15   192.168.110.188   80:31116/TCP   49s   test=pod1
pod2svc   LoadBalancer   10.110.132.42   192.168.110.189   80:32118/TCP   9s    test=pod2

現在在Windows裡存取svc:192.168.110.188 和192.168.110.189。

也可以通過物理機IP+PORT埠存取svc。

可以看到,沒有網路策略的條件下,外界可以隨意存取pod,這是不安全的,下面給pod新增網路策略。

六.給pod新增網路策略

網路策略NetworkPolicy 是一種以應用為中心的結構,允許你設定如何允許 Pod 與網路上的各類網路「實體」 通訊。 說白了,網路策略本質上就是建立一個防火牆,控制入站和出站流量。

網路策略通過CNI(Containernetworking Interface)網路外掛來實現。 要使用網路策略,你必須使用支援 NetworkPolicy 的網路解決方案。 建立一個 NetworkPolicy 資源物件而沒有控制器來使它生效的話,是沒有任何作用的。此kubernetes叢集使用的網路外掛是Calico,Calico支援網路策略,大家熟知的Flannel是不支援網路策略的,關於網路外掛的詳情,請檢視部落格《使用CNI網路外掛(calico)實現docker容器跨主機互聯》。

Pod 有兩種隔離: 出口的隔離和入口的隔離,即出站(Egress)和入站(Ingress)。

為了允許兩個 Pods 之間的網路資料流,源端 Pod 上的出站(Egress)規則和 目標端 Pod 上的入站(Ingress)規則都需要允許該流量。 如果源端的出站(Egress)規則或目標端的入站(Ingress)規則拒絕該流量, 則流量將被拒絕。

下面先介紹入站(Ingress)網路策略,即誰能存取我和誰不能存取我。

6.1 入站網路策略

下面的yaml檔案是一個標準的網路策略:

  • PodSelector: 指定被此 NetworkPolicy 影響的 Pod。此處匹配 Label 為 "role=db" 的 Pod。
  • PolicyTypes: 規定所需的網路策略型別。此處包括 Ingress 和 Egress。
  • Ingress: 定義允許從指定來源(IP 地址範圍、名稱空間或 Pod)和埠接收流量的規則。此處僅允許 TCP 流量存取埠 6379。
  • Egress: 定義允許傳送到指定目標(IP 地址範圍)和埠的流量的規則。此處僅允許 TCP 流量傳送到埠 5978 的目標 IP 地址範圍為 10.0.0.0/24。
[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    matchLabels:
      role: db
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Ingress
  - Egress
  #ingress:入站規則
  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

我們先講解使用pod標籤選擇器定義的入站網路策略。

6.1.1 入站網路策略-pod標籤選擇器

檢視pod的標籤。

[root@k8scloude1 ~]# kubectl get pod -o wide --show-labels
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES   LABELS
pod1   1/1     Running   1          16h   10.244.112.136   k8scloude2   <none>           <none>            test=pod1
pod2   1/1     Running   1          16h   10.244.251.227   k8scloude3   <none>           <none>            test=pod2

編寫網路策略規則,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,僅僅允許當前名稱空間內標籤為role=podclient的pod可以存取pod1的80埠(TCP)。

[root@k8scloude1 network]# vim networkpolicy.yaml 

#先通過pod標籤選擇器控制
[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則,只有Ingress表示定義的是入站規則
  policyTypes:
  - Ingress
  #ingress:入站規則
  ingress:
  #允許哪些使用者端可以存取
  - from:
    #標籤為role: podclient的pod可以存取
    - podSelector:
        matchLabels:
          role: podclient
    ports:
    - protocol: TCP
      port: 80

應用網路策略。

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy configured

[root@k8scloude1 network]# kubectl get networkpolicy -o wide
NAME                POD-SELECTOR   AGE
my-network-policy   test=pod1      85s

檢視svc。

[root@k8scloude1 network]# kubectl get svc -o wide
NAME      TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE   SELECTOR
pod1svc   LoadBalancer   10.103.187.15   192.168.110.188   80:31116/TCP   19h   test=pod1
pod2svc   LoadBalancer   10.110.132.42   192.168.110.189   80:32118/TCP   19h   test=pod2

使用Windows使用者端存取svc,因為Windows使用者端沒有pod標籤,所以Windows使用者端存取不了pod1svc服務了。

image-20230612161057871

使用臨時pod podclient使用者端存取svc,臨時pod podclient沒有標籤role: podclient,所以存取不了pod1svc服務,pod2沒有網路策略保護,可以隨意被存取。

[root@k8scloude1 network]# kubectl run podclient --image=yauritux/busybox-curl:latest -it --rm --image-pull-policy=IfNotPresent -- sh
If you don't see a command prompt, try pressing enter.

/home # curl pod1svc
^C

/home # curl pod2svc
222

/home # exit
Session ended, resume using 'kubectl attach podclient -c podclient -i -t' command when the pod is running
pod "podclient" deleted

建立podclient。

[root@k8scloude1 network]# kubectl run podclient --image=yauritux/busybox-curl:latest  --image-pull-policy=IfNotPresent  -it
If you don't see a command prompt, try pressing enter.

/home # exit

Session ended, resume using 'kubectl attach podclient -c podclient -i -t' command when the pod is running

給pod podclient打標籤。

[root@k8scloude1 network]# kubectl label pod podclient role=podclient
pod/podclient labeled

檢視pod標籤,此時podclient具有了role=podclient的標籤。

[root@k8scloude1 network]# kubectl get pod -o wide --show-labels
NAME        READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES   LABELS
pod1        1/1     Running   1          20h   10.244.112.136   k8scloude2   <none>           <none>            test=pod1
pod2        1/1     Running   1          20h   10.244.251.227   k8scloude3   <none>           <none>            test=pod2
podclient   1/1     Running   1          39s   10.244.112.140   k8scloude2   <none>           <none>            role=podclient,run=podclient

在podclient裡存取svc,因為podclient具有標籤 role=podclient,所以可以存取pod1svc,pod2沒有網路策略保護,可以隨意被存取。

[root@k8scloude1 network]# kubectl exec -it podclient -- sh
/home # curl pod1svc
111
/home # curl pod2svc
222
/home # exit

建立podnsclient,-n default表示在default名稱空間建立podnsclient。

[root@k8scloude1 network]# kubectl run podnsclient --image=yauritux/busybox-curl:latest  --image-pull-policy=IfNotPresent -n default -it
If you don't see a command prompt, try pressing enter.
/home # exit
Session ended, resume using 'kubectl attach podnsclient -c podnsclient -i -t' command when the pod is running

在podnsclient裡存取svc,podnsclient不具備role=podclient標籤,所以存取不了pod1svc

[root@k8scloude1 network]# kubectl exec -it podnsclient -n default -- sh
/home # curl pod1svc.network
^C

/home # exit

command terminated with exit code 130

給podnsclient新增role=podclient標籤,--overwrite強制覆蓋。

[root@k8scloude1 network]# kubectl label pod podnsclient role=podclient -n default --overwrite
pod/podnsclient labeled

[root@k8scloude1 network]# kubectl get pod -o wide --show-labels -n default
NAME          READY   STATUS    RESTARTS   AGE     IP               NODE         NOMINATED NODE   READINESS GATES   LABELS
podnsclient   1/1     Running   1          7m22s   10.244.251.230   k8scloude3   <none>           <none>            role=podclient

在podnsclient裡存取svc,存取pod1svc失敗,這表示網路策略podSelector:matchLabels:role: podclient,只允許在當前namespace裡標籤為role: podclient的pod可以存取,其他名稱空間的pod就算有這個標籤也不行

[root@k8scloude1 network]# kubectl exec -it podnsclient -n default -- sh

#存取不了
/home # curl pod1svc.network
^C

/home # exit
command terminated with exit code 130

6.1.2 入站網路策略-namespaceSelector名稱空間選擇器

檢視名稱空間的標籤。

[root@k8scloude1 network]# kubectl get ns --show-labels
NAME              STATUS   AGE     LABELS
daemonset         Active   22d     kubernetes.io/metadata.name=daemonset
default           Active   38d     kubernetes.io/metadata.name=default
deployment        Active   22d     kubernetes.io/metadata.name=deployment
ingress-nginx     Active   2d13h   app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx,kubernetes.io/metadata.name=ingress-nginx
......
volume            Active   30d     kubernetes.io/metadata.name=volume

給default名稱空間打name=default標籤。

[root@k8scloude1 network]# kubectl label ns default name=default
namespace/default labeled

[root@k8scloude1 network]# kubectl get ns --show-labels
NAME              STATUS   AGE     LABELS
daemonset         Active   22d     kubernetes.io/metadata.name=daemonset
default           Active   38d     kubernetes.io/metadata.name=default,name=default
deployment        Active   22d     kubernetes.io/metadata.name=deployment
......
volume            Active   30d     kubernetes.io/metadata.name=volume

下面定義通過namespaceSelector名稱空間來控制的入站網路策略,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,允許標籤為name: default的namespace下的所有pod可以存取pod1的80埠(TCP)。

[root@k8scloude1 network]# vim networkpolicy.yaml 


[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Ingress
  #ingress:入站規則
  ingress:
  #允許哪些使用者端可以存取
  - from:
    - namespaceSelector:
        matchLabels:
          #允許標籤為name: default的namespace下的pod存取
          name: default
    ports:
    - protocol: TCP
      port: 80

應用網路策略。

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy configured

[root@k8scloude1 network]# kubectl get networkpolicy -o wide
NAME                POD-SELECTOR   AGE
my-network-policy   test=pod1      3h1m

在podnsclient裡存取svc, 存取成功,default名稱空間下的任何pod都能存取pod1svc。

[root@k8scloude1 network]# kubectl get pod -o wide -n default
NAME          READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
podnsclient   1/1     Running   1          21m   10.244.251.230   k8scloude3   <none>           <none>

[root@k8scloude1 network]# kubectl exec -it podnsclient -n default -- sh

/home # curl pod1svc.network
111

/home # exit

在podclient裡存取svc,存取失敗,可以看到當前namespace下的pod都不能存取pod1svc,default名稱空間下的pod能存取pod1svc。

[root@k8scloude1 network]# kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
pod1        1/1     Running   1          20h   10.244.112.136   k8scloude2   <none>           <none>
pod2        1/1     Running   1          20h   10.244.251.227   k8scloude3   <none>           <none>
podclient   1/1     Running   1          25m   10.244.112.140   k8scloude2   <none>           <none>

[root@k8scloude1 network]# kubectl exec -it podclient -- sh

/home # curl pod1svc
^C

/home # exit
command terminated with exit code 130

修改網路策略,設定只允許default名稱空間裡的特定pod能存取,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,允許標籤為name: default的namespace下的標籤為role: podclient的pod可以存取pod1的80埠(TCP)。

[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Ingress
  #ingress:入站規則
  ingress:
  #允許哪些使用者端可以存取
  - from:
    - namespaceSelector:
        matchLabels:
          name: default
      podSelector:
        matchLabels:
          role: podclient
    ports:
    - protocol: TCP
      port: 80

應用網路策略。

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy configured

在podnsclient裡存取svc,存取失敗,原因為:podnsclient雖然在default名稱空間,但是pod的標籤標籤不是role: podclient。

[root@k8scloude1 network]# kubectl exec -it podnsclient -n default -- sh
#存取不了
/home # curl pod1svc.network
^C

/home # exit
command terminated with exit code 130

[root@k8scloude1 network]# kubectl get pod -o wide -n default --show-labels
NAME          READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES   LABELS
podnsclient   1/1     Running   1          31m   10.244.251.230   k8scloude3   <none>           <none>            run=podclient

給podnsclient打role=podclient標籤。

[root@k8scloude1 network]# kubectl label pod podnsclient role=podclient -n default
pod/podnsclient labeled

[root@k8scloude1 network]# kubectl get pod -o wide -n default --show-labels
NAME          READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES   LABELS
podnsclient   1/1     Running   1          33m   10.244.251.230   k8scloude3   <none>           <none>            role=podclient,run=podclient

設定標籤之後,podnsclient就可以存取pod1svc了。

[root@k8scloude1 network]# kubectl exec -it podnsclient -n default -- sh

/home # curl pod1svc.network
111

/home # exit

修改網路策略,設定允許所有名稱空間裡標籤為run=podclient的pod可以存取,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,允許所有名稱空間下的標籤為role: podclient的pod可以存取pod1的80埠(TCP)。

[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Ingress
  #ingress:入站規則
  ingress:
  #允許哪些使用者端可以存取
  - from:
   #允許所有名稱空間裡標籤為role=podclient的pod可以存取
    - namespaceSelector:
        matchLabels:
      podSelector:
        matchLabels:
          role: podclient
    ports:
    - protocol: TCP
      port: 80

檢視標籤為role=podclient的所有pod。

[root@k8scloude1 network]# kubectl get pod -A -l role=podclient
NAMESPACE   NAME          READY   STATUS    RESTARTS   AGE
default     podnsclient   1/1     Running   1          39m
network     podclient     1/1     Running   1          42m

[root@k8scloude1 network]# kubectl get pod -A -l role=podclient --show-labels
NAMESPACE   NAME          READY   STATUS    RESTARTS   AGE   LABELS
default     podnsclient   1/1     Running   1          40m   role=podclient,run=podclient
network     podclient     1/1     Running   1          43m   role=podclient,run=podclient

應用網路策略。

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy configured

在podclient和podnsclient裡存取svc,全都存取成功。

[root@k8scloude1 network]# kubectl exec -it podclient -- sh
/home # curl pod1svc
111

/home # exit

[root@k8scloude1 network]# kubectl exec -it -n default podnsclient -- sh
/home # curl pod1svc.network
111

/home # exit

6.1.3 入站網路策略-IP地址控制

下面定義通過IP地址來控制的入站網路策略,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,只允許192.168.110.0/24這個網段的pod可以存取pod1的80埠(TCP)。

[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Ingress
  #ingress:入站規則
  ingress:
  #允許哪些使用者端可以存取
  - from:
    - ipBlock:
        #只允許192.168.110.0/24這個網段的進行存取
        cidr: 192.168.110.0/24
    ports:
    - protocol: TCP
      port: 80

應用網路策略。

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy configured

修改網路策略,設定只允許192.168.110.0/24這個網段的進行存取,但是192.168.110.128不可以存取,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,只允許192.168.110.0/24這個網段的pod可以存取pod1的80埠(TCP),但是192.168.110.128這個IP的pod不可以存取pod1的80埠(TCP)。

[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Ingress
  #ingress:入站規則
  ingress:
  #允許哪些使用者端可以存取
  - from:
    - ipBlock:
        cidr: 192.168.110.0/24
        #192.168.110.128不可以存取
        except:
        - 192.168.110.128/24
    ports:
    - protocol: TCP
      port: 80

6.2 出站網路策略

6.2.1 出站網路策略-pod標籤選擇器

下面設定出站網路策略,就是pod1能存取誰?

現在有pod1,pod2。

[root@k8scloude1 network]# kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE    IP               NODE         NOMINATED NODE   READINESS GATES
pod1        1/1     Running   1          22h    10.244.112.136   k8scloude2   <none>           <none>
pod2        1/1     Running   1          22h    10.244.251.227   k8scloude3   <none>           <none>
podclient   1/1     Running   1          153m   10.244.112.140   k8scloude2   <none>           <none>

建立pod3。

[root@k8scloude1 network]# sed 's/podtest/pod3/' pod.yaml | kubectl apply -f -
pod/pod3 created

[root@k8scloude1 network]# kubectl get pod -o wide
NAME        READY   STATUS    RESTARTS   AGE    IP               NODE         NOMINATED NODE   READINESS GATES
pod1        1/1     Running   1          22h    10.244.112.136   k8scloude2   <none>           <none>
pod2        1/1     Running   1          22h    10.244.251.227   k8scloude3   <none>           <none>
pod3        1/1     Running   0          5s     10.244.112.141   k8scloude2   <none>           <none>
podclient   1/1     Running   1          155m   10.244.112.140   k8scloude2   <none>           <none>

修改pod3的首頁檔案index.html。

[root@k8scloude1 network]# kubectl exec -it pod3 -- sh -c "echo 333 > /usr/share/nginx/html/index.html"

存取pod3,首頁檔案已經改變。

[root@k8scloude1 network]# curl 10.244.112.141
333

給pod3建立svc,型別預設(ClusterIP)就行。

[root@k8scloude1 network]# kubectl expose --name=pod3svc pod pod3 --port=80
service/pod3svc exposed

[root@k8scloude1 network]# kubectl get svc -o wide
NAME      TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE   SELECTOR
pod1svc   LoadBalancer   10.103.187.15   192.168.110.188   80:31116/TCP   22h   test=pod1
pod2svc   LoadBalancer   10.110.132.42   192.168.110.189   80:32118/TCP   22h   test=pod2
pod3svc   ClusterIP      10.103.213.71   <none>            80/TCP         11s   test=pod3

下面定義通過pod標籤選擇器來控制的出站網路策略,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,pod1只能存取當前名稱空間標籤為 test: pod3的pod的80埠(TCP)。

[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Egress
  egress:
  - to:
    #只能存取標籤為 test: pod3的pod
    - podSelector:
        matchLabels:
          test: pod3
    ports:
    - protocol: TCP
      port: 80

應用網路策略。

[root@k8scloude1 network]# kubectl get networkpolicy
No resources found in network namespace.

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy created

[root@k8scloude1 network]# kubectl get networkpolicy
NAME                POD-SELECTOR   AGE
my-network-policy   test=pod1      2s

進入pod1存取其他pod,可以看到在pod1中可以通過IP地址存取pod3,但是不能通過pod3svc存取pod3,原因為:如果想通過svc存取pod,pod需要去kube-dns那裡查詢svc的IP地址,但是現在pod1沒有kube-dns的存取許可權

[root@k8scloude1 network]# kubectl exec -it pod1 -- sh
# curl 10.244.251.227
^C

# curl 10.244.112.141
333

# exit

[root@k8scloude1 network]# kubectl exec -it pod1 -- sh
# curl 10.244.112.141
333

# curl pod3svc
^C

# exit        
command terminated with exit code 130

6.2.2 出站網路策略-pod標籤選擇器和namespaceSelector名稱空間選擇器

檢視名稱空間kube-system的標籤。

[root@k8scloude1 network]# kubectl get ns --show-labels
NAME              STATUS   AGE     LABELS
daemonset         Active   22d     kubernetes.io/metadata.name=daemonset
default           Active   39d     kubernetes.io/metadata.name=default,name=default
deployment        Active   23d     kubernetes.io/metadata.name=deployment
ingress-nginx     Active   2d16h   app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx,kubernetes.io/metadata.name=ingress-nginx
job               Active   7d23h   kubernetes.io/metadata.name=job
kube-node-lease   Active   39d     kubernetes.io/metadata.name=kube-node-lease
kube-public       Active   39d     kubernetes.io/metadata.name=kube-public
kube-system       Active   39d     kubernetes.io/metadata.name=kube-system
......
volume            Active   30d     kubernetes.io/metadata.name=volume

檢視kube-dns服務的埠。

[root@k8scloude1 network]# kubectl get svc -o wide -n kube-system | grep dns
kube-dns         ClusterIP   10.96.0.10    <none>        53/UDP,53/TCP,9153/TCP   39d   k8s-app=kube-dns

修改網路策略,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,pod1能存取kube-system名稱空間下的所有pod和當前名稱空間下標籤為test: pod3的pod,存取埠為80和53。

[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    - podSelector:
        matchLabels:
          test: pod3
    ports:
    - protocol: TCP
      port: 80
    - protocol: UDP
      port: 53

應用網路策略。

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy configured

進入pod1存取其他pod,可以看到在pod1中可以通過IP地址存取pod3,也可以通過pod3svc存取pod3了。

#這樣就可以通過svc存取了
[root@k8scloude1 network]# kubectl exec -it pod1 -- sh

# curl 10.244.112.141
333

# curl pod3svc
333

# exit

6.2.3 優化出站網路策略

但是這樣寫有問題:pod1可以存取標籤為test: pod3的pod的80埠和53埠。

修改網路策略,如下網路策略的功能為:把名為my-network-policy的網路策略應用到pod1,pod1能存取kube-system名稱空間下的所有pod的53埠,pod1能存取當前名稱空間下標籤為test: pod3的pod的80埠。

[root@k8scloude1 network]# vim networkpolicy.yaml 

[root@k8scloude1 network]# cat networkpolicy.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
spec:
  #podSelector網路策略要應用到哪個pod上,通過標籤的方式來控制pod,只能應用到和網路策略相同namespace的pod
  podSelector:
    #如果matchLabels下面沒寫標籤,則應用網路策略到當前namespace下的所有pod
    matchLabels:
      #test: pod1:把網路策略應用到標籤為test: pod1的pod上
      test: pod1
  #policyTypes:啟用的是入站規則還是出站規則
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53
  - to:
    - podSelector:
        matchLabels:
          test: pod3
    ports:
    - protocol: TCP
      port: 80

應用網路策略。

[root@k8scloude1 network]# kubectl apply -f networkpolicy.yaml 
networkpolicy.networking.k8s.io/my-network-policy configured

進入pod1存取其他pod,可以看到在pod1中可以通過IP地址存取pod3,也可以通過pod3svc存取pod3了。

[root@k8scloude1 network]# kubectl exec -it pod1 -- sh
# curl 10.244.112.141
333

# curl pod3svc
333

# exit

刪除網路策略。

[root@k8scloude1 network]# kubectl delete -f networkpolicy.yaml 
networkpolicy.networking.k8s.io "my-network-policy" deleted

[root@k8scloude1 network]# kubectl get networkpolicy
No resources found in network namespace.

6.2.4 出站網路策略-指定埠範圍

在Kubernetes v1.22版本,出了個新特性,在編寫 NetworkPolicy 時,你可以針對一個埠範圍而不是某個固定埠。這一目的可以通過使用 endPort 欄位來實現,如下例所示:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 32000
      endPort: 32768

上面的規則允許名稱空間 default 中所有帶有標籤 role: db 的 Pod 使用 TCP 協定 與 10.0.0.0/24 範圍內的 IP 通訊,只要目標埠介於 32000 和 32768 之間就可以。

使用此欄位時存在以下限制

  • 作為一種 Beta 階段的特性,埠範圍設定預設是被啟用的。要在整個叢集 範圍內禁止使用 endPort 欄位,你需要為 API 伺服器設定 -feature-gates=NetworkPolicyEndPort=false,... 以禁用 NetworkPolicyEndPort 特性。
  • endPort 欄位必須等於或者大於 port 欄位的值。
  • port,endPort 兩個欄位的設定值都只能是數位。

注意:你的叢集所使用的 CNI 網路外掛 必須支援在 NetworkPolicy 規約中使用 endPort 欄位。 如果你的網路外掛 不支援 endPort 欄位,而你指定了一個包含 endPort 欄位的 NetworkPolicy, 策略只對單個 port 欄位生效。

6.3 預設網路策略

6.3.1 預設拒絕所有入站流量

可以通過建立選擇所有容器但不允許任何進入這些容器的入站流量的 NetworkPolicy 來為名稱空間建立 "default" 隔離策略。這樣可以確保即使容器沒有選擇其他任何 NetworkPolicy,也仍然可以被隔離。 此策略不會更改預設的出口隔離行為。

預設拒絕所有入站流量,網路策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

6.3.2 預設允許所有入站流量

如果要允許所有流量進入某個名稱空間中的所有 Pod(即使新增了導致某些 Pod 被視為 「隔離」的策略),則可以建立一個策略來明確允許該名稱空間中的所有流量。

預設允許所有入站流量,網路策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

6.3.3 預設拒絕所有出站流量

可以通過建立選擇所有容器但不允許來自這些容器的任何出站流量的 NetworkPolicy 來為名稱空間建立 "default" egress 隔離策略。
此策略可以確保即使沒有被其他任何 NetworkPolicy 選擇的 Pod 也不會被允許流出流量。 此策略不會更改預設的入站流量隔離行為。

預設拒絕所有出站流量,網路策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

6.3.4 預設允許所有出站流量

如果要允許來自名稱空間中所有 Pod 的所有流量(即使新增了導致某些 Pod 被視為「隔離」的策略), 則可以建立一個策略,該策略明確允許該名稱空間中的所有出站流量。

預設允許所有出站流量,網路策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

6.3.5 預設拒絕所有入口和所有出站流量

你可以為名稱空間建立「預設」策略,以通過在該名稱空間中建立以下 NetworkPolicy 來阻止所有入站和出站流量。
此策略可以確保即使沒有被其他任何 NetworkPolicy 選擇的 Pod 也不會被 允許入站或出站流量。

預設拒絕所有入口和所有出站流量,網路策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

七.總結

網路策略(NetworkPolicy)是Kubernetes叢集中一個非常重要的安全控制措施,可以幫助我們保護Kubernetes叢集的網路安全。在本文中,我們已經學習了什麼是網路策略(NetworkPolicy),以及如何在Kubernetes叢集中使用網路策略(NetworkPolicy)來保護網路安全。

通過網路策略(NetworkPolicy)範例,我們展示瞭如何使用網路策略(NetworkPolicy)來限制Pod之間的流量存取。

在Kubernetes叢集中使用網路策略(NetworkPolicy)可以提高網路的安全性,但也需要注意以下幾點:

  • 應該避免建立過於複雜的網路策略(NetworkPolicy),因為這可能會導致網路通訊中斷或延遲。
  • 在建立網路策略(NetworkPolicy)前,需要確保已仔細檢查其規則,並確認這些規則符合預期。
  • 當修改或刪除一個網路策略(NetworkPolicy)時,需要確保所有Pod都能夠正常通訊。

最後,希望本文可以幫助讀者瞭解Kubernetes的網路策略(NetworkPolicy)並在實踐中得到應用。