Kubernetes(k8s)网络策略NetworkPolicy

一.系统环境

本文主要基于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。

image-20230612160448377

image-20230612160459978

也可以通过物理机IP+PORT端口访问svc。

image-20230612160549143

image-20230612160601398

可以看到,没有网络策略的条件下,外界可以随意访问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:
    #cidr: 0.0.0.0/0表示允许所有客户端可以访问
    - 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)并在实践中得到应用。

posted @ 2023-06-14 09:41  人生的哲理  阅读(3685)  评论(2编辑  收藏  举报