017-K8S-网络策略NetworkPolicy

一、网络策略(NetworkPolicy)简介

1. 概念解析

如果你希望在 IP 地址或端口层面(OSI 第 3 层或第 4 层)控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)

Kubernetes 网络策略(NetworkPolicy)是一个资源对象,主要用于定义Pod之间的流量控制,其实现了一个基于标签的选择器模型,允许管理员通过网络策略规则限制对Pod的流量访问。

网络策略(NetworkPolicy)是以Pod为单位进行授权的,因此,只有当所有的Pod都通过了网络策略时,才能够接收到其他Pod发送的流量。这种方式极大提高了网络的安全性。

Pod 是通过如下三个标识符的组合来辩识是否可以通讯:

  • 其他被允许的 Pods(例外:Pod 无法阻塞对自身的访问)
  • 被允许的名字空间
  • IP 组块 [ ipBlock ](例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)

2. 前置条件

网络策略通过 网络插件 来实现。 要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。 创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的

  • Calico (当前使用此网络插件)
  • Antrea
  • Cilium
  • Kube-router
  • Romana
  • Weave 网络

3. 隔离默认策略

  • 出口的隔离
    • 默认情况下,一个 Pod 的出口是非隔离的,即所有外向连接都是被允许的
  • 入口的隔离
    • 默认情况下,一个 Pod 对入口是非隔离的,即所有入站连接都是被允许的

4. 特别说明

网络策略是相加的,所以不会产生冲突。如果策略适用于 Pod 某一特定方向的流量, Pod 在对应方向所允许的连接是适用的网络策略所允许的集合。 因此,评估的顺序不影响策略的结果。

要允许从源 Pod 到目的 Pod 的连接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允许连接。 如果任何一方不允许连接,建立连接将会失败。

二、创建 pod 和 svc

基于 Nginx 镜像创建Pod,用于后续实验测试。

# 创建工作空间,演示的Pod创建在此工作空间内
$ kubectl create ns network

# 切换工作空间到 network, 切换后续所有操作默认再次工作空间内(此操作非必须)
$ kubectl config set-context --current --namespace=network

# 查看当前所在工作空间
$ kubectl config view --minify

# 创建两个nginx pod用于测试使用
kubectl run web1 --image=nginx:1.29.0 --labels=app=web,env=prod --image-pull-policy=IfNotPresent -n network --expose --port 80
kubectl run web2 --image=nginx:1.29.0 --labels=app=web,env=test --image-pull-policy=IfNotPresent -n network --expose --port 80

# 查看Pod
$ kubectl get pods -o wide -n network
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
web1   1/1     Running   0          65s   171.20.85.196   k8s-node01   <none>           <none>
web2   1/1     Running   0          30s   171.20.85.197   k8s-node01   <none>           <none>

# 查看 Service
$ kubectl get svc -o wide -n network
NAME   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
web1   ClusterIP   10.110.3.181    <none>        80/TCP    70s   app=web,env=prod
web2   ClusterIP   10.100.184.48   <none>        80/TCP    35s   app=web,env=test

# 修改nginx的首页文件index.html,用于区分两个pod
$ kubectl exec -it web1 -- sh -c "echo web1 > /usr/share/nginx/html/index.html"
$ kubectl exec -it web2 -- sh -c "echo web2 > /usr/share/nginx/html/index.html"

# 访问Pod,测试首页是否改变
$ curl 171.20.85.196
web1
$ curl 171.20.85.197
web2

三、未添加网络策略,访问Pod

查看当前命名空间下的 NetworkPolicy,当前没有网络策略。

$ kubectl get networkpolicy -n network
No resources found in network namespace.

新建一个临时 Pod, 用于访问 web1 和 web2。在没有网络策略的情况下,临时 Pod 可以访问 web1 和 web2

# 创建临时Pod,推出容器时自动删除该pod
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --labels=env=prod --image-pull-policy=IfNotPresent -n default -it --rm -- sh

# 由于临时 Pod 与 web1 不在同一个 名称空间,因此访问Service需要添加 名称空间
/home # curl web1.network
web1
/home # curl web1.network.svc.cluster.local
web1
/home # curl web2.network
web2

修改Nginx Pod 的 Service 类型为 LoadBalance ,让浏览器可以访问 Nginx。

# 修改 Service 类型
$ kubectl patch svc web1 -n network -p '{"spec":{"type":"LoadBalancer"}}'
$ kubectl patch svc web2 -n network -p '{"spec":{"type":"LoadBalancer"}}'

# 查看 Service
$ kubectl get svc -o wide
NAME   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE   SELECTOR
web1   LoadBalancer   10.110.3.181    <pending>     80:31193/TCP   30m   app=web,env=prod
web2   LoadBalancer   10.100.184.48   <pending>     80:30826/TCP   29m   app=web,env=test

测试浏览器访问 Service

访问web1

访问web2

结论:未添加网络策略,外界可以随意访问 pod。这是不安全的。

四、Pod 添加网络策略

网络策略 NetworkPolicy 是一种以应用为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络“实体” 通信。 说白了,网络策略本质上就是建立一个防火墙,控制入站和出站流量。

网络策略通过 CNI(Containernetworking Interface) 网络插件来实现。 要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。 创建一个 NetworkPolicy 资源对象而没有控制器来使它生效的话,是没有任何作用的。此kubernetes集群使用的网络插件是Calico,Calico支持网络策略,大家熟知的Flannel是不支持网络策略的。

Pod 有两种隔离: 出口的隔离和入口的隔离,即出站(Egress)和入站(Ingress)。

为了允许两个 Pods 之间的网络数据流,源端 Pod 上的出站(Egress)规则和 目标端 Pod 上的入站(Ingress)规则都需要允许该流量。 如果源端的出站(Egress)规则或目标端的入站(Ingress)规则拒绝该流量, 则流量将被拒绝。

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

资源清单:networkpolicy-example.yaml

apiVersion: networking.k8s.io/v1    # networking.k8s.io/v1 是 NetworkPolicy 资源的标准 API
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network                # 资源所在名称空间,该 NetworkPolicy 只能作用于该名称空间下的Pod
spec:
  podSelector:
    matchLabels:
      role: db                      # 当前网络策略作用于 network 名称空间下,具有标签 role: db 的 Pod
  policyTypes:
  - Ingress                         # 控制入站流量
  - Egress                          # 控制出站流量
  ingress:
    - from:
        - ipBlock:
            #cidr: 0.0.0.0/0表示允许所有客户端可以访问
            cidr: 172.17.0.0/16     # 允许来自 IP 范围 172.17.0.0 到 172.17.255.255 的流量
            except:
              - 172.17.1.0/24       # 排除子范围 172.17.1.0 到 172.17.1.255,即这部分 IP 被禁止。
        - namespaceSelector:
            matchLabels:
              project: myproject    # 允许来自具有标签 project=myproject 的命名空间中的所有 Pod 发起的流量。
        - podSelector:
            matchLabels:
              role: frontend        # 允许 【同一命名】 空间内具有标签 role=frontend 的 Pod 发起的流量。
      ports:
        - protocol: TCP
          port: 6379                # 只允许上述来源访问 role=db Pod 的 TCP 6379 端口
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24       # 允许 role=db Pod 访问 IP 范围 10.0.0.0 到 10.0.0.255 的目标
      ports:
        - protocol: TCP
          port: 5978                # 只允许访问目标的 TCP 5978 端口

1.1 入站网络策略-pod标签选择器

查看 pod 标签

# 查看pod
$ kubectl get pod -o wide -n network --show-labels
NAME   READY   STATUS    RESTARTS   AGE    IP              NODE         NOMINATED NODE   READINESS GATES   LABELS
web1   1/1     Running   0          128m   171.20.85.196   k8s-node01   <none>           <none>            app=web,env=prod
web2   1/1     Running   0          128m   171.20.85.197   k8s-node01   <none>           <none>            app=web,env=test

# 查看Service
$ kubectl get svc -n network -o wide
NAME   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE    SELECTOR
web1   LoadBalancer   10.110.3.181    <pending>     80:31193/TCP   146m   app=web,env=prod
web2   LoadBalancer   10.100.184.48   <pending>     80:30826/TCP   146m   app=web,env=test

1.1.1 案例1

编写网络策略规则,如下网络策略的功能为:把名为 my-network-policy 的网络策略应用到 web1,仅仅允许当前命名空间(network)内标签为 role=podclient 的 pod可以访问 web1的80端口(TCP)。

资源清单:network-policy-pod-selector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Ingress
  ingress:
    - from:
        - podSelector:        # 仅当前命名空间内,具有标签 role: podclient 的 Pod 可以访问 web1
            matchLabels:
              role: podclient
      ports:
        - protocol: TCP
          port: 80            # web1 只开放80端口

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-pod-selector.yaml

# 查看 NetworkPolicy
$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       28s

**测试1:使用浏览器访问 web1 **

浏览器没有Pod标签,因此访问 web1 失败

浏览器访问 web1

测试2:没有 role=podclient 标签的临时pod,访问web1

# 在 network 名称空间创建临时Pod,不添加标签
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --image-pull-policy=IfNotPresent -n network -it --rm -- sh

# 访问 web1 Service 失败,因为没有 role=podclient 标签,无法通过网络策略
/home # curl web1
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

# 访问 web2 Service 成功,因为 web2 无网络策略限制
/home # curl web2
web2

测试3:具有 role=podclient 标签,但Pod不在 network 名称空间,访问web1

# 在 default 名称空间创建临时Pod,添加标签 role=podclient
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --labels=role=podclient --image-pull-policy=IfNotPresent -n default -it --rm -- sh

# 访问 web1 Service 失败,虽然pod1具有标签 role=podclient,但由于网络策略限制了与web1必须在同一名称空间,因此访问失败
/home # curl web1.network
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

# 访问 web2 Service 成功,因为 web2 无网络策略限制
/home # curl web2.network
web2

测试4:具有 role=podclient 标签,且Pod在 network 名称空间

# 在 network 名称空间创建临时Pod,添加标签 role=podclient
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --labels=role=podclient --image-pull-policy=IfNotPresent -n network -it --rm -- sh

# 访问 web1 成功
/home # curl web1
web1

1.2 入站网络策略-namespaceSelector命名空间选择器

查看命名空间的标签

$ kubectl get ns --show-labels
NAME              STATUS   AGE     LABELS
default           Active   4d      kubernetes.io/metadata.name=default
kube-node-lease   Active   4d      kubernetes.io/metadata.name=kube-node-lease
kube-public       Active   4d      kubernetes.io/metadata.name=kube-public
kube-system       Active   4d      kubernetes.io/metadata.name=kube-system
network           Active   177m    kubernetes.io/metadata.name=network

给命名空间打标签

给 default 命名空间打 name=default 标签

# 给 default 命名空间打标签
$ kubectl label ns default name=default
namespace/default labeled

# 查看命名空间标签
$ kubectl get ns --show-labels | grep default
default           Active   4d      kubernetes.io/metadata.name=default,name=default

1.2.1 案例1

定义通过 namespaceSelector 命名空间来控制的入站网络策略,如下网络策略的功能为:把名为 my-network-policy 的网络策略应用到 web1,允许标签为 name: default 的 namespace下的所有 pod 可以访问 web1 的 80 端口(TCP)。

资源清单: network-policy-namespace-selector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:      # 具有 name=default 标签的命名空间下所有的pod,可以访问web1的80端口
              name: default
      ports:
        - protocol: TCP
          port: 80            # web1 只开放80端口

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-namespace-selector.yaml

# 查看 network 命名空间下的 NetworkPolicy
$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       113s

测试1:在没有 name=default 标签的名称空间下创建Pod,访问 web1

# 在 network 名称空间创建临时Pod
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --image-pull-policy=IfNotPresent -n network -it --rm -- sh

# 访问 web1 失败,network 命名空间没有 name=default 标签,无法通过网络策略
/home # curl web1
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

# 访问 web2 成功,web2 无网络策略限制
/home # curl web2
web2

测试2: 在具有 name=default 标签的名称空间下创建Pod,访问 web1

# 在 network 名称空间创建临时Pod
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --image-pull-policy=IfNotPresent -n default -it --rm -- sh

# 访问 web1 成功,default 命名空间具有 name=default 标签,通过了网络策略
/home # curl web1.network
web1

1.2.2 案例2

修改网络策略,设置只允许 default 命名空间里的特定 pod 能访问,如下网络策略的功能为:把名为 my-network-policy 的网络策略应用到 web1,允许标签为name: default 的 namespace下的标签为 role: podclient 的 pod 可以访问 web1 的80端口(TCP)。

资源清单:network-policy-namespace-pod-selector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:      # 具有 name=default 标签的命名空间下的pod
              name: default
          podSelector:
            matchLabels:      # 在具有 name=default 标签的命名空间基础上,还需要具有 role=podclient 标签的pod
              role: podclient
      ports:
        - protocol: TCP
          port: 80            # web1 只开放80端口

注意: podSelector 前面没有 - ,因此 namespaceSelector 和 podSelector 这两个条件是 并集关系,需要同时满足。

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-namespace-pod-selector.yaml 
networkpolicy.networking.k8s.io/my-network-policy created

# 查看 network 名称空间下的 NetworkPolicy
$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       11s

测试1:在没有 name=default 标签的名称空间下,创建具有标签 role=podclient 的 Pod,访问 web1

# 在 network 名称空间创建临时Pod
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --labels=role=podclient --image-pull-policy=IfNotPresent -n network -it --rm -- sh

# 访问 web1 失败,network 命名空间没有 name=default 标签,无法通过网络策略
/home # curl web1
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

# 访问 web2 成功,web2 无网络策略限制
/home # curl web2
web2

测试2: 在具有 name=default 标签的名称空间下,创建没有标签 role=podclient 的 Pod,访问 web1

# 在 network 名称空间创建临时Pod
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --image-pull-policy=IfNotPresent -n default -it --rm -- sh

# 访问 web1 失败,虽然 default 命名空间具有 name=default 标签,但 pod1 没有标签 role=podclient,无法通过了网络策略
/home # curl web1.network
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

测试3: 在具有 name=default 标签的名称空间下,创建具有标签 role=podclient 的 Pod,访问 web1

# 在 network 名称空间创建临时Pod
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --image-pull-policy=IfNotPresent --labels=role=podclient -n default -it --rm -- sh

# 访问 web1 成功,pod1 同时满足了在具有 name=default 标签的 default 命名空间下,且具有标签 role=podclient,通过了网络策略
/home # curl web1.network
web1

1.3 入站网络策略-IP地址控制

1.3.1 案例1

定义通过IP地址来控制的入站网络策略,如下网络策略的功能为:把名为my-network-policy的网络策略应用到 web1,只允许 171.20.10.0/24 这个网段的pod可以访问 web1 的80端口(TCP)。

资源清单:network-policy-ipBlock.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 171.20.10.0/24    # 只允许 171.20.10.0/24 这个网段的pod可以访问 web1 的80端口(TCP)
      ports:
        - protocol: TCP
          port: 80            # web1 只开放80端口

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-ipBlock.yaml

# 查看 network 名称空间的 NetworkPolicy
$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       35s

测试1:创建Pod,固定IP地址,访问web1

# 查看 Pod Ip地址范围
$ calicoctl get ippool
NAME                  CIDR            SELECTOR   
default-ipv4-ippool   171.20.0.0/16   all()


# 创建 171.20.10.0/24 这个网段的pod
$ kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: pod
  annotations:
    "cni.projectcalico.org/ipAddrs": "[\"171.20.10.27\"]"
  name: pod1
  namespace: default
spec:
  containers:
    - image: nginx:1.29.0
      imagePullPolicy: IfNotPresent
      name: pod1
EOF


# 创建非 171.20.10.0/24 这个网段的pod
$ kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: pod
  annotations:
    "cni.projectcalico.org/ipAddrs": "[\"171.20.20.57\"]"
  name: pod2
  namespace: default
spec:
  containers:
    - image: nginx:1.29.0
      imagePullPolicy: IfNotPresent
      name: pod2
EOF


# 查看创建 Pod 的IP地址
$ kubectl get pods -o wide -n default
NAME   READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          24s   171.20.10.27   k8s-node02   <none>           <none>
pod2   1/1     Running   0          16s   171.20.20.57   k8s-node01   <none>           <none>


# pod1 访问 web1 成功,pod1 ip 为 171.20.10.27,在 171.20.10.0/24 网段内,可通过网络策略
$ kubectl exec -it pod1 -n default -- sh -c "curl web1.network"
web1

# pod2 访问 web1 失败,pod2 ip 为 171.20.20.57,不在 171.20.10.0/24 网段内,无法通过网络策略
$ kubectl exec -it pod2 -n default -- sh -c "curl web1.network"
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

1.3.2 案例2

修改网络策略,设置只允许 171.20.10.0/24 这个网段的进行访问,但是 171.20.10.10 不可以访问,如下网络策略的功能为:把名为 my-network-policy 的网络策略应用到 web1,只允许 171.20.10.0/24 这个网段的 pod 可以访问 web1 的80端口(TCP),但是 171.20.10.10 这个IP的 pod 不可以访问 web1 的80端口(TCP)

资源清单: network-policy-ipBlock-except.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 171.20.10.0/24    # 只允许 171.20.10.0/24 这个网段的pod可以访问 web1 的80端口(TCP)、
            except:
              - 171.20.10.10/32        # 排除单个ip
      ports:
        - protocol: TCP
          port: 80            # web1 只开放80端口

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-ipBlock-except.yaml

# 查询 NetworkPolicy
$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       42s

测试1:创建Pod,固定IP地址,访问web1

# 查看 Pod Ip地址范围
$ calicoctl get ippool
NAME                  CIDR            SELECTOR   
default-ipv4-ippool   171.20.0.0/16   all()


# 创建 171.20.10.0/24 这个网段的pod
$ kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: pod
  annotations:
    "cni.projectcalico.org/ipAddrs": "[\"171.20.10.27\"]"
  name: pod1
  namespace: default
spec:
  containers:
    - image: nginx:1.29.0
      imagePullPolicy: IfNotPresent
      name: pod1
EOF


# 创建非 171.20.10.0/24 这个网段的pod
$ kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: pod
  annotations:
    "cni.projectcalico.org/ipAddrs": "[\"171.20.10.10\"]"
  name: pod2
  namespace: default
spec:
  containers:
    - image: nginx:1.29.0
      imagePullPolicy: IfNotPresent
      name: pod2
EOF


# 查看创建 Pod 的IP地址
$ kubectl get pods -o wide -n default
NAME   READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          24s   171.20.10.27   k8s-node02   <none>           <none>
pod2   1/1     Running   0          16s   171.20.20.10   k8s-node01   <none>           <none>


# pod1 访问 web1 成功,pod1 ip 为 171.20.10.27,在 171.20.10.0/24 网段内,可通过网络策略
$ kubectl exec -it pod1 -n default -- sh -c "curl web1.network"
web1

# pod2 访问 web1 失败,pod2 ip 为 171.20.10.10,被 NetworkPolicy except 排除在外,无法通过网络策略
$ kubectl exec -it pod2 -n default -- sh -c "curl web1.network"
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

2. 出站网络策略

创建 web3

# 创建web3用于测试使用
$ kubectl run web3 --image=nginx:1.29.0 --labels=app=web,env=dev --image-pull-policy=IfNotPresent -n network --expose --port 80

# 查看Pod
$ $ kubectl get pods -o wide -n network
NAME   READY   STATUS    RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
web1   1/1     Running   0          6h26m   171.20.85.196   k8s-node01   <none>           <none>
web2   1/1     Running   0          6h26m   171.20.85.197   k8s-node01   <none>           <none>
web3   1/1     Running   0          5s      171.20.10.0     k8s-node02   <none>           <none>

# 查看 Service
$ $ kubectl get pods -o wide -n network --show-labels
NAME   READY   STATUS    RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES   LABELS
web1   1/1     Running   0          6h27m   171.20.85.196   k8s-node01   <none>           <none>            app=web,env=prod
web2   1/1     Running   0          6h26m   171.20.85.197   k8s-node01   <none>           <none>            app=web,env=test
web3   1/1     Running   0          35s     171.20.10.0     k8s-node02   <none>           <none>            app=web,env=dev

# 修改nginx的首页文件index.html,用于区分pod
$ kubectl exec -it web3 -- sh -c "echo web3 > /usr/share/nginx/html/index.html"

# 访问Pod,测试首页是否改变
$ curl 171.20.10.0
web3

2.1 出站网络策略-pod标签选择器

2.1.1 案例1

定义通过 pod 标签选择器来控制的出站网络策略,如下网络策略的功能为:把名为 my-network-policy 的网络策略应用到 web1,web1只能访问当前命名空间标签为 test: pod3的pod的80端口(TCP)

资源清单:network-policy-pod-selector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Egress
  egress:                   # 出站网络策略
    - to:
        - podSelector:
            matchLabels:    # 只能访问标签为 env: dev 的Pod
              env: dev
      ports:
        - protocol: TCP     # 只能访问端口 80,且协议为TCP
          port: 80

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-pod-selector.yaml

# 查询 NetworkPolicy
$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       85s

测试出站网络策略

进入 web1 , 访问 web2 和 web3

# web1 直接访问 web3 的ip是成功的,因为满足的出站策略
$ kubectl exec -it web1 -n network -- bash -c "curl 171.20.10.0"
web3

# web1 直接访问 web2 的ip失败了,因为 出站策略里,限制了访问的pod,不包含 pod2
$ kubectl exec -it web1 -n network -- bash -c "curl 171.20.85.197"
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

# web1 访问 web3 svc 失败,原因为:如果想通过svc访问 pod,pod 需要去kube-dns那里查询 svc 的IP地址,但是现在 web1 没有 kube-dns 的访问权限
$ kubectl exec -it web1 -n network -- bash -c "curl web3.network"
curl: (6) Could not resolve host: web3.network

web1 访问 web3 svc 失败,原因为:如果想通过svc访问 pod,pod 需要去kube-dns那里查询 svc 的IP地址,但是现在 web1 没有 kube-dns 的访问权限

2.2 出站网络策略-pod标签选择器和 namespaceSelector 命名空间选择器

2.2.1 案例1

修改网络策略,如下网络策略的功能为:把名为 my-network-policy 的网络策略应用到 web1,web1 能访问 kube-system 命名空间下的所有 pod 和当前命名空间下标签为 env: dev 的 pod,访问端口为 80 和 53。

查看命名空间 kube-system 的标签

$ kubectl get ns --show-labels | grep kube-system
kube-system       Active   4d4h    kubernetes.io/metadata.name=kube-system

资源清单: network-policy-pod-namespace.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Egress
  egress:                   # 出站网络策略
    - to:
        - namespaceSelector:      # 标签为 kubernetes.io/metadata.name: kube-system 下的所有pod,web1 都能访问
            matchLabels:
              kubernetes.io/metadata.name: kube-system
        - podSelector:
            matchLabels:          # 并且能访问标签为 env: dev 的Pod(web3)
              env: dev
      ports:
        - protocol: TCP           # 能访问端口 80,且协议为TCP
          port: 80
        - protocol: UDP           # 能访问端口 53,且协议为UDP
          port: 53

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-pod-namespace.yaml

# 查询 NetworkPolicy
$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       85s

测试出站网络策略

进入 web1 , 访问 web2 和 web3

# web1 直接访问 web3 svc成功,因为满足的出站策略,且 web1 具有 kube-dns 访问权限,因此可以解析域名
$ kubectl exec -it web1 -n network -- bash -c "curl web3.network"
web3

# web1 直接访问 web2 的ip失败了,因为 出站策略里,限制了访问的pod,不包含 pod2
$ kubectl exec -it web1 -n network -- bash -c "curl web2.network"
curl: (28) Failed to connect to web1 port 80 after 133895 ms: Operation timed out

2.3 优化出站网络策略

上面的案例存在问题:web1可以访问标签为 env: dev 的 pod 的 80端口 和 53端口, 而 web3 只有 80端口,53 端口属于 kube-system 名称空间下的 coredns pod.

2.3.1 案例1

修改网络策略,如下网络策略的功能为:把名为 my-network-policy 的网络策略应用到 web1,web1能访问 kube-system 命名空间下的所有 pod 的53端口,web1能访问当前命名空间下标签为 env: dev 的pod的80端口。

资源清单:network-policy-pod-namespace2.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Egress
  egress:                   # 出站网络策略
    - to:
        - namespaceSelector:      # 标签为 kubernetes.io/metadata.name: kube-system 下的所有 pod,web1 都能访问
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP           # 能访问端口 53,且协议为UDP
          port: 53
    - to:
        - podSelector:
            matchLabels: # 并且能访问标签为 env: dev 的Pod(web3)
              env: dev
      ports:
        - protocol: TCP  # 能访问端口 80,且协议为TCP
          port: 80

执行资源清单

# 执行资源清单,创建 NetworkPolicy
$ kubectl apply -f network-policy-pod-namespace2.yaml

$ kubectl get networkpolicy -n network
NAME                POD-SELECTOR   AGE
my-network-policy   env=prod       26m

测试出站网络策略

# 查看 coredns Pod
$ kubectl get pods -n kube-system -o wide | grep coredns
coredns-5f98f8d567-72vc7                   1/1     Running   0               4d16h   171.20.58.192   k8s-node02     <none>           <none>
coredns-5f98f8d567-cv75d                   1/1     Running   0               4d16h   171.20.85.192   k8s-node01     <none>           <none>

# 查看 coredns Service
$ kubectl get service -n kube-system -o wide | grep kube-dns
kube-dns       ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP   4d16h   k8s-app=kube-dns

# 测试 pod1 访问 coredns Service 53 端口,成功
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --labels=env=prod --image-pull-policy=IfNotPresent -n default -it --rm -- sh
/home # telnet kube-dns.kube-system 53
Connected to kube-dns.kube-system

# 测试 web1 访问 web3 80 端口,成功
$ kubectl exec -it web1 -n network -- bash -c "curl web3.network"
web3

2.4 出站网络策略-指定端口范围

在Kubernetes v1.22 版本,出了个新特性,在编写 NetworkPolicy 时,你可以针对一个端口范围而不是某个固定端口。这一目的可以通过使用 endPort 字段来实现,如下例所示:

资源清单: network-policy-ipBlock.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: network
spec:
  podSelector:
    matchLabels: # 匹配作用的 Pod 标签
      env: prod
  policyTypes:
  - Egress
  egress:                   # 出站网络策略
    - to:
        - ipBlock:
            cidr: 171.20.0.0/16    # 只允许 171.20.0.0/16 这个网段的pod可以访问
      ports:
        - protocol: TCP
          port: 53                  # 可以访问 53-80 之间的端口
          endPort: 80

上面的规则允许名字空间 network 中所有带有标签 env: prod 的 Pod 使用 TCP 协议 与 171.20.0.0/16 范围内的 IP 通信,只要目标端口介于 53 和 80 之间就可以。

执行资源清单

$ kubectl apply -f network-policy-ipBlock.yaml

测试出站网络策略

# 测试 pod1 访问 coredns Service 53 端口,成功
$ kubectl run pod1 --image=yauritux/busybox-curl:latest --labels=env=prod --image-pull-policy=IfNotPresent -n default -it --rm -- sh
/home # telnet kube-dns.kube-system 53
Connected to kube-dns.kube-system

# 测试 web1 访问 web3 80 端口,成功
$ kubectl exec -it web1 -n network -- bash -c "curl web3.network"
web3

使用endPort字段时存在以下限制

  • 作为一种 Beta 阶段的特性,端口范围设定默认是被启用的。要在整个集群 范围内禁止使用 endPort 字段,你需要为 API 服务器设置 -feature-gates=NetworkPolicyEndPort=false,… 以禁用 NetworkPolicyEndPort 特性。
  • endPort 字段必须等于或者大于 port 字段的值。
  • port,endPort 两个字段的设置值都只能是数字。

注意:你的集群所使用的 CNI 网络插件 必须支持在 NetworkPolicy 规约中使用 endPort 字段。 如果你的网络插件 不支持 endPort 字段,而你指定了一个包含 endPort 字段的 NetworkPolicy, 策略只对单个 port 字段生效。

五、默认网络策略

1. 默认拒绝所有入站流量

可以通过创建选择所有容器但不允许任何进入这些容器的入站流量的 NetworkPolicy 来为命名空间创建 “default” 隔离策略。这样可以确保即使容器没有选择其他任何 NetworkPolicy,也仍然可以被隔离。 此策略不会更改默认的出口隔离行为。

默认拒绝所有入站流量,网络策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: default
spec:
  podSelector: {}	# 空选择器({})表示匹配default命名空间下所有Pod
  policyTypes:		# 定义入站流量
  - Ingress
  					# 没有 ingress 规则,意味着拒绝所有入站流量

2. 默认允许所有入站流量

如果要允许所有流量进入某个命名空间中的所有 Pod(即使添加了导致某些 Pod 被视为 “隔离”的策略),则可以创建一个策略来明确允许该名字空间中的所有流量。

默认允许所有入站流量,网络策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
  namespace: default
spec:
  podSelector: {}			# 空选择器({})表示匹配default命名空间下所有Pod
  ingress:					
  - {}						# 空规则({})表示允许所有入站流量。
  policyTypes:
  - Ingress					# 控制入站流量(从外部或集群内其他 Pod 访问目标 Pod)

3. 默认拒绝所有出站流量

可以通过创建选择所有容器但不允许来自这些容器的任何出站流量的 NetworkPolicy 来为名字空间创建 “default” egress 隔离策略。
此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被允许流出流量。 此策略不会更改默认的入站流量隔离行为。

默认拒绝所有出站流量,网络策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: default
spec:
  podSelector: {}		# 空选择器({})表示匹配default命名空间下所有Pod
  policyTypes:
  - Egress				# 控制出站流量(Pod 访问其他 Pod、Service 或外部网络)
  						# 没有 egress 规则,意味着拒绝所有出站流量

4. 默认允许所有出站流量

如果要允许来自名字空间中所有 Pod 的所有流量(即使添加了导致某些 Pod 被视为“隔离”的策略), 则可以创建一个策略,该策略明确允许该名字空间中的所有出站流量。

默认允许所有出站流量,网络策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}		# 空选择器({})表示匹配default命名空间下所有Pod
  egress:
  - {}					# 空规则({})表示允许所有出站流量。
  policyTypes:
  - Egress				# 控制出站流量(Pod 访问其他 Pod、Service 或外部网络)

5. 默认拒绝所有入口和所有出站流量

你可以为名字空间创建“默认”策略,以通过在该名字空间中创建以下 NetworkPolicy 来阻止所有入站和出站流量。
此策略可以确保即使没有被其他任何 NetworkPolicy 选择的 Pod 也不会被 允许入站或出站流量。

默认拒绝所有入口和所有出站流量,网络策略如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}		# 空选择器({})表示匹配default命名空间下所有Pod
  policyTypes:
  - Ingress				# 控制入站流量(从外部或集群内其他 Pod 访问目标 Pod)
  - Egress				# 控制出站流量(Pod 访问其他 Pod、Service 或外部网络)
  						# 没有 egress 规则,意味着拒绝所有出站流量
  						# 没有 ingress 规则,意味着拒绝所有入站流量

六、总结

网络策略(NetworkPolicy)是 Kubernetes 集群中一个非常重要的安全控制措施,可以帮助我们保护 Kubernetes集群的网络安全。

通过网络策略(NetworkPolicy)示例,展示了如何使用网络策略( NetworkPolicy )来限制Pod之间的流量访问。

在 Kubernetes 集群中使用网络策略( NetworkPolicy )可以提高网络的安全性,但也需要注意以下几点:

  • 应该避免创建过于复杂的网络策略(NetworkPolicy),因为这可能会导致网络通信中断或延迟。
  • 在创建网络策略(NetworkPolicy)前,需要确保已仔细检查其规则,并确认这些规则符合预期。
  • 当修改或删除一个网络策略(NetworkPolicy)时,需要确保所有Pod都能够正常通信。

通过网络策略(至少目前还)无法完成的工作

  • 强制集群内部流量经过某公用网关(这种场景最好通过服务网格或其他代理来实现)
  • 与 TLS 相关的场景(考虑使用服务网格或者 Ingress 控制器)
  • 特定于节点的策略(你可以使用 CIDR 来表达这一需求不过你无法使用节点在 Kubernetes 中的其他标识信息来辩识目标节点);
  • 基于名字来选择服务来选择目标 Pod 或名字空间
  • 基于名字来选择服务来选择目标 Pod 或名字空间
  • 实现适用于所有名字空间或 Pods 的默认策略(某些第三方 Kubernetes 发行版本或项目可以做到这点);
  • 高级的策略查询或者可达性相关工具;
  • 生成网络安全事件日志的能力(例如,被阻塞或接收的连接请求);
  • 显式地拒绝策略的能力(目前,NetworkPolicy 的模型默认采用拒绝操作, 其唯一的能力是添加允许策略);
  • 禁止本地回路或指向宿主的网络流量(Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求);

参考链接

https://www.cnblogs.com/renshengdezheli/p/17479289.html


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 george_95@126.com