kubernetes-NetworkPolicy介绍、Ingress及Egress的网络配置案例

219 阅读12分钟

NetworkPolicy 网络策略介绍

image.png

基于NetworkPolicy在三层(网络层) 或 四层(传输层)控制拒绝或允许请求流量。

  • 允许或拒绝特定的pod请求 目的namespace 中的 目的pod 的 所有或指定端口。
  • 允许或拒绝特定的 namespace 请求 目的namespace 中的 所有或特定的pod 所有或指定端口。
  • 允许或拒绝特定的 源IP范围 或 IP地址 请求 目的pod 的所有或特定端口。
  • 默认的入口和出口的流量都是运行的
  • pod不能拒绝自身对自身的访问(包没有经过内核 直接通过127.0.0.1通信了)

五元组

  • 源端口
  • 源IP
  • 目标端口
  • 目标IP
  • 协议

环境准备

1.kubernetes 二进制环境,calico网络组件, 2个node节点或以上、用于跨节点通信验证。

2.两个namespace,linux和python,分别代表不同项目的Pod。

3.每个namespace 运行多个pod,且pod运行在不同的node主机。

4.测试环境为每个namespace分别运行一个nginx和一个tomcat pod,用于测试不同主机的Pod运行在同一个ns的场景、以及跨ns的访问通信及访问限制。
kubectl create ns linux

kubectl create ns python

kubectl label ns linux nsname=linux

kubectl label ns python nsname=python

kubectl get ns --show-labels=true

step1 linux空间 部署nginx tomcat

linux-nginx.yaml

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: linux-nginx-deployment-label
  name: linux-nginx-deployment
  namespace: linux
spec:
  replicas: 1
  selector:
    matchLabels:
      app: linux-nginx-selector
  template:
    metadata:
      labels:
        app: linux-nginx-selector
    spec:
      containers:
      - name: linux-nginx-container
        image: nginx:1.20.2-alpine 
        #command: ["/apps/tomcat/bin/run_tomcat.sh"]
        #imagePullPolicy: IfNotPresent
        imagePullPolicy: Always
        ports:
        - containerPort: 80
          protocol: TCP
          name: http
        - containerPort: 443
          protocol: TCP
          name: https
        env:
        - name: "password"
          value: "123456"
        - name: "age"
          value: "18"
#        resources:
#          limits:
#            cpu: 2
#            memory: 2Gi
#          requests:
#            cpu: 500m
#            memory: 1Gi


---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: linux-nginx-service-label
  name: linux-nginx-service
  namespace: linux
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30008
  - name: https
    port: 443
    protocol: TCP
    targetPort: 443
    nodePort: 30443
  selector:
    app: linux-nginx-selector

linux-tomcat.yaml

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: linux-tomcat-app1-deployment-label
  name: linux-tomcat-app1-deployment
  namespace: linux
spec:
  replicas: 1
  selector:
    matchLabels:
      app: linux-tomcat-app1-selector
  template:
    metadata:
      labels:
        app: linux-tomcat-app1-selector
    spec:
      containers:
      - name: linux-tomcat-app1-container
        image: tomcat:7.0.109-jdk8-openjdk 
        #command: ["/apps/tomcat/bin/run_tomcat.sh"]
        #imagePullPolicy: IfNotPresent
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          protocol: TCP
          name: http
        env:
        - name: "password"
          value: "123456"
        - name: "age"
          value: "18"
#        resources:
#          limits:
#            cpu: 2
#            memory: 2Gi
#          requests:
#            cpu: 500m
#            memory: 1Gi


---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: linux-tomcat-app1-service-label
  name: linux-tomcat-app1-service
  namespace: linux
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 8080
    nodePort: 30006
  selector:
    app: linux-tomcat-app1-selector

image.png

step3 python空间部署 3个pod

python-nginx.yaml

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: python-nginx-deployment-label
  name: python-nginx-deployment
  namespace: python
spec:
  replicas: 1
  selector:
    matchLabels:
      app: python-nginx-selector
  template:
    metadata:
      labels:
        app: python-nginx-selector
        project: python
    spec:
      containers:
      - name: python-nginx-container
        image: nginx:1.20.2-alpine
        #command: ["/apps/tomcat/bin/run_tomcat.sh"]
        #imagePullPolicy: IfNotPresent
        imagePullPolicy: Always
        ports:
        - containerPort: 80
          protocol: TCP
          name: http
        - containerPort: 443
          protocol: TCP
          name: https
        env:
        - name: "password"
          value: "123456"
        - name: "age"
          value: "18"
#        resources:
#          limits:
#            cpu: 2
#            memory: 2Gi
#          requests:
#            cpu: 500m
#            memory: 1Gi


---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: python-nginx-service-label
  name: python-nginx-service
  namespace: python
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30014
  - name: https
    port: 443
    protocol: TCP
    targetPort: 443
    nodePort: 30453
  selector:
    app: python-nginx-selector
    project: python #一个或多个selector,至少能匹配目标pod的一个标签 

python-tomcat.yml

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: python-tomcat-app1-deployment-label
  name: python-tomcat-app1-deployment
  namespace: python
spec:
  replicas: 1
  selector:
    matchLabels:
      app: python-tomcat-app1-selector
  template:
    metadata:
      labels:
        app: python-tomcat-app1-selector
    spec:
      nodeName: 172.31.7.113
      containers:
      - name: python-tomcat-app1-container
        image: tomcat:7.0.109-jdk8-openjdk 
        #command: ["/apps/tomcat/bin/run_tomcat.sh"]
        #imagePullPolicy: IfNotPresent
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          protocol: TCP
          name: http
        env:
        - name: "password"
          value: "123456"
        - name: "age"
          value: "18"
#        resources:
#          limits:
#            cpu: 2
#            memory: 2Gi
#          requests:
#            cpu: 500m
#            memory: 1Gi


---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: python-tomcat-app1-service-label
  name: python-tomcat-app1-service
  namespace: python
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 8080
    nodePort: 30015
  selector:
    app: python-tomcat-app1-selector

testpod-busybox.yaml

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: testpod-busybox-deployment-label
  name: testpod-busybox-deployment
  namespace: python
spec:
  replicas: 1
  selector:
    matchLabels:
      app: python-nginx-selector
      project: python
  template:
    metadata:
      labels:
        app: python-nginx-selector
        project: python
    spec:
      containers:
      - name: testpod-busybox-container
        image: centos:7.9.2009
        command:
          - sleep
          - "50000000"
        imagePullPolicy: IfNotPresent
        #imagePullPolicy: Always

image.png

step3 linux空间 python空间中的 tomcat 各自创建静态文件 用于访问区分

tomcat空间

kubectl exec -it $python-tomcat bash -n python

cd /usr/local/tomcat/webapps;mkdir app

echo "python web app web page" > app/index.jsp

注意替换变量 $python-tomcat 为实际的pod name

linux空间

kubectl exec -it $linux-tomcat bash -n linux

cd /usr/local/tomcat/webapps;mkdir app

echo "linux web app web page" > app/index.jsp

注意替换变量 $linux-tomcat 为实际的pod name

step4 登陆python空间的busybox 测试访问tomcat

kubectl exec -ti testpod-busybox-deployment-85f4ccf67b-m5f7n bash -n python


[root@testpod-busybox-deployment-85f4ccf67b-m5f7n /]# curl 10.200.196.139:8080/app/index.jsp 
linux web app web page

[root@testpod-busybox-deployment-85f4ccf67b-m5f7n /]# curl 10.200.196.142:8080/app/index.jsp
python web app web page

busybox访问 linux空间和 python 空间的tomcat都是畅通的 image.png

同样在master02节点测试 也是畅通的 image.png

证明 各个空间中的pod可以相互访问 畅通无阻, 默认 网络策略不限制

case1 ingress 只允许同namespace访问

策略: 以pod为限制单位、只允同namespace 含有特定标签的 源pod 访问 目标pod 的所有端口:

case1-ingress-podSelector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tomcat-access--networkpolicy
  namespace: python
spec:
  policyTypes:
  - Ingress
  podSelector:
    matchLabels:
      app: python-tomcat-app1-selector #对匹配到的目的Pod应用以下规则
  ingress: #入栈规则,如果指定目标端口就是匹配全部端口和协议,协议TCP, UDP, or SCTP
  - from:
    - podSelector:
        matchLabels:
          #app: python-nginx-selector #如果存在多个matchLabel条件,如果存在多个matchLabel条件,是and的关系,即要同时满足条件A、条件B、条件X
          project: "python"

从其他的位置 访问失败了 image.png

从nginx的容器中 请求成功了 image.png

case2-ingress

ingress-以pod加单个端口为限制单位、只允同namespace含有特定标签的源pod访问目标pod的指定端口

case2-ingress-podSelector-ns-SinglePort.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tomcat-access--networkpolicy
  namespace: python
spec:
  policyTypes:
  - Ingress
  podSelector:
    matchLabels:
      app: python-tomcat-app1-selector

  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: python-nginx-selector #指定访问源的匹配条件,如果存在多个matchLabel条件,是and的关系,即要同时满足条件A、条件B、条件X
          #project: "python"
    ports: #入栈规则,如果指定目标端口就是匹配全部端口和协议,协议TCP, UDP, or SCTP
    - protocol: TCP
      port: 8888 #允许通过TCP协议访问目标pod的8888端口,但是其它没有允许的端口将全部禁止访问
      #port: 80

在python-nginx的容器中 无法访问8080端口了 image.png

底层的原理是什么?

在 python-tomcat-app1-selector pod所在的宿主机上 可以看到iptables规则 image.png

case3-ingress

ingress-允许同namespace的所有pod访问当前namespace的目标pod多个指定端口

case3-ingress-podSelector-ns-MultiPort.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tomcat-access-networkpolicy
  namespace: python
spec:
  policyTypes:
  - Ingress
  podSelector: #目标pod
    matchLabels:
      app: python-tomcat-app1-selector
  ingress:
  - from:
    - podSelector: #匹配源pod,matchLabels: {}为不限制源pod即允许所有pod,写法等同于resources(不加就是不限制)
        matchLabels: {}
    ports: #入栈规则,如果指定目标端口就是匹配全部端口和协议,协议TCP, UDP, or SCTP
    - protocol: TCP
      port: 8080 #允许通过TCP协议访问目标pod的8080端口,但是其它没有允许的端口将全部禁止访问
      #port: 80
    - protocol: TCP
      port: 3306
    - protocol: TCP
      port: 6379

验证

跨namespace禁止访问

case4-ingress

ingress-允许同namespace的所有pod访问当前namespace的目标pod所有端口

case4-ingress-podSelector-ns.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tomcat-access--networkpolicy
  namespace: python
spec:
  policyTypes:
  - Ingress
  podSelector: #目标pod
    matchLabels: {} # 不填东西 就是匹配所有目标pod
  ingress:
  - from:
    - podSelector: #匹配源pod,matchLabels: {}为不限制源pod即允许所有pod,写法等同于resources(不加就是不限制)
        matchLabels: {}

验证

1.其它namespace无法访问目标ns的pod。

2.同namespace的pod可以访问当前namespace中的所有pod的任意端口

case5-ingress

ingress-ipBlock白名单(基于pod的ip地址段进行限制、不受NS限制)

case5-ingress-ipBlock.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tomcat-access--networkpolicy
  namespace: python
spec:
  policyTypes:
  - Ingress
  podSelector: #目标pod
    matchLabels:
      app: python-tomcat-app1-selector
  ingress:
  - from:
#    - podSelector: #匹配源pod,matchLabels: {}为不限制源pod即允许所有pod,写法等同于resources(不加就是不限制)
#        matchLabels: {}
    - ipBlock:
        cidr: 10.200.0.0/16 #cidr定义的是白名单,代表允许访问的地址范围10.200.0.0/16,没有允许的将禁止访问目标pod
        except: # 定义禁止的地址
        - 10.200.196.133/32 #在以上范围内禁止访问的源IP地址 10.200.0.0/16 中的10.200.196.133/32 不能进行访问
    ports: #入栈规则,如果指定目标端口就是匹配全部端口和协议,协议TCP, UDP, or SCTP
    - protocol: TCP
      port: 8080 #允许通过TCP协议访问目标pod的8080端口,但是其它没有允许的端口将全部禁止访问
      #port: 80
    - protocol: TCP
      port: 3306
    - protocol: TCP
      port: 6379

image.png

不建议在k8s中进行限制 建议放到前面 CDN WAF进行限制

验证:

1.只要在白名单范围内没有被except指定禁止的源pod IP,都允许访问。

2.在只设置了ipBlock匹配的前提下,其它namespace 中没有在except范围的pod 也可以访问目标pod及linux ns中的pod

只要不在except地址范围内,也可以访问python ns中的pod了

case6-ingress

ingress-namespace Selector-ns选择器:

case6-ingress-namespaceSelector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tomcat-access--networkpolicy
  namespace: python
spec:
  policyTypes:
  - Ingress
  podSelector: #目标pod
    matchLabels: {} #允许访问python namespace 中的所有pod
#      app: python-tomcat-app1-selector #可以只允许访问python namespace中指定的pod
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          nsname: linux #只允许指定的namespace访问
    - namespaceSelector:
        matchLabels:
          nsname: python #只允许指定的namespace访问
    ports: #入栈规则,如果指定目标端口就是匹配全部端口和协议,协议TCP, UDP, or SCTP
    - protocol: TCP
      port: 8080 #允许通过TCP协议访问目标pod的8080端口,但是其它没有允许的端口将全部禁止访问
    - protocol: TCP
      port: 3306
    - protocol: TCP
      port: 6379

验证:

1.被明确允许的namespace中的pod可以访问目标pod

2.没有明确声明允许的namespace将禁止访问

3.没有明确声明允许的话,即使同一个namespace也禁止访问

4.比如只允许了linux和python两个ns,那么default中的pod将无法访问

case7-Egress

限制出口流量

Egress-podSelector-Pod出口方向目的IP 及 目的端口限制

只允许访问指定的目的地址范围及端口:限制指定的pod可以访指定的ip

限制python-tomcat-app1-selector的pod 只能访问 10.200.0.0/16 和 172.31.7.106/32 的主机

这个规则不能访问外网了

case7-Egress-ipBlock.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-access-networkpolicy
  namespace: python
spec:
  policyTypes:
  - Egress
  podSelector: #目标pod选择器
    matchLabels:  #基于label匹配目标pod
      app: python-tomcat-app1-selector #匹配python namespace中app的值为python-tomcat-app1-selector的pod,然后基于egress中的指定网络策略进行出口方向的网络限制
  egress:
  - to:
    - ipBlock:
        cidr: 10.200.0.0/16 #允许匹配到的pod出口访问的目的CIDR地址范围
    - ipBlock:
        cidr: 172.31.7.106/32 #允许匹配到的pod出口访问的目的主机
    ports:
    - protocol: TCP
      port: 80 #允许匹配到的pod访问目的端口为80的访问
    - protocol: TCP
      port: 53 #允许匹配到的pod访问目的端口为53 即DNS的解析
    - protocol: UDP
      port: 53 #允许匹配到的pod访问目的端口为53 即DNS的解析

image.png

验证:

1.基于Egress白名单,定义ns中匹配成功的pod可以访问ipBlock指定的地址和ports指定的端口。

2.匹配成功的pod访问未明确定义在Egress的白名单的其它IP的请求,将拒绝。

3.没有匹配成功的源pod,主动发起的出口访问请求不受影响

case8-Egress

Egress-podSelector-Pod出口方向目的Pod限制-只允许源pod访问指定的目的pod及端口:

基于podSelector选择器,限制源pod能够访问的目的pod

case8-Egress-PodSelector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-access-networkpolicy
  namespace: python
spec:
  policyTypes:
  - Egress
  podSelector: #目标pod选择器
    matchLabels:  #基于label匹配目标pod
      app: python-nginx-selector #匹配python namespace中app的值为python-tomcat-app1-selector的pod,然后基于egress中的指定网络策略进行出口方向的网络限制
  egress:
  - to:
    - podSelector: #匹配pod,matchLabels: {}为不限制源pod即允许所有pod,写法等同于resources(不加就是不限制)
        matchLabels:
          app: python-tomcat-app1-selector
    ports:
    - protocol: TCP
      port: 8080 #允许80端口的访问
    - protocol: TCP
      port: 53 #允许DNS的解析
    - protocol: UDP
      port: 53

验证:

1.匹配成功的源pod只能访问指定的目的pod的指定端口

2.其它没有允许的出口请求将禁止访问

case9-Egress

限制匹配成功的pod访问指定的namespace

case9-Egress-namespaceSelector.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-access-networkpolicy
  namespace: python
spec:
  policyTypes:
  - Egress
  podSelector: #目标pod选择器
    matchLabels:  #基于label匹配目标pod
      app: python-nginx-selector #匹配python namespace中app的值为python-tomcat-app1-selector的pod,然后基于egress中的指定网络策略进行出口方向的网络限制
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          nsname: python #指定允许访问的目的namespace
    - namespaceSelector:
        matchLabels:
          nsname: linux #指定允许访问的目的namespace
    - namespaceSelector:
        matchLabels:
          nsname: kube-system #指定允许访问的目的namespace
    ports:
    - protocol: TCP
      port: 80 #允许80端口的访问
    - protocol: TCP
      port: 8080 #允许8080端口的访问
    - protocol: TCP
      port: 53 #允许DNS的解析
    - protocol: UDP
      port: 53

验证

1.匹配成功的源pod可以访问指定的目标namespace。

2.不能访问除指定的namespace以外的其它namespace及外网。

3.比如允许指定的源pod访问linux和python ns中的8080、3306、6379等端口,但是其它ns无法访问