TOC

Network Policy

    对于Kubernetes系统来讲,虽然内部有所谓的Namespace资源,而且Pod也属于名称空间级别的资源,也就意味着Pod是部署在名称空间级别下的,而且指定名称空间列出的Pod也只能是指定名称空间下的Pod,也就意味着它给我们提供的Pod能够让两个同名的Pod可以存在,只是他们要在不同的名称空间下而已,那么夸名称空间的两个Pod,那么我们在名称空间dev下创建了一个Pod,另外一个prod名称空间下也创建了一个Pod,它们只要同一个Kubernetes集群上受同一个Pod plugin来管理,他们所分配的地址彼此之间是可以直接通信的,即便是他们夸网络名称空间这个通信一样不会被隔离,因为名称空间仅隔离名称而已,不隔离通信,这样就导致了如果我们期望把Kubernetes这个集群所提供的环境能共享给多个项目,多个团队,甚至是多个组织使用时,那么夸组织这种通信安全或者通信隔离将无从得到保障,所以为了实现这样的功能,Kubernetes也引入了一个标准资源来允许用户获得一个名称空间之后,在自己的名称空间来定义这个跨名称空间的Pod和Pod怎么通信,包括名称空间内的Pod之间怎么通信,都能进行定义;

    官方文档地址:https://kubernetes.io/docs/concepts/services-networking/network-policies/

    当然这种定义方式不是我们手写iptables等访问策略来实现,而是使用标准的Kubernetes资源,NetworkPolicy,这种资源在定义时,与我们以往的资源类型没什么区别,可以类比Ingress来理解NetworkPolicy的工作逻辑,在Ingress Controller是确保能够引入并调度流量的关键位置,但是这个Controller中的程序可以运行为Nginx,也可以是haproxy,但是不同的程序它的配置文件是不一样的,因此为了避免用户在学习每一种Ingress Controller相关应用程序的配置文件的格式,Kubernetes引入了一种标准的资源就叫Ingress,你可以使用Ingress格式定义出Ingress规则以后,它能够将其对应应用程序的配置文件,NetworkPolicy也是这样的,它允许我们就按照NetworkPolicy内部的逻辑来定义如何管控Pod间通信而后由Kubernetes负责,将其转为对应节点的iptables或者其他规则, 我们可以定义指定名称空间的Pod决不允许任何其他名称空间的Pod访问即可,只要把这个策略一应用,而后Kubernetes就会负责将其转换为对这一组Pod如何进行分组以及如何给它施加访问控制机制,这是真正在网络层控制了访问控制机制,因此网络策略能够将我们所期望实现的网络访问控制功能转换为对应的访问控制机制所能实现的规则;
    Kubernetes大多数还是部署在Linux主机上的,如果我们的Kubernetes能够运行在Unix、FreeBSD或者OpenBSD之上,OpenBSD是内核级的访问控制不是iptables,它有自己的访问控制逻辑,所以不同的操作系统,在内核级完成流量访问控制控制逻辑不尽相同,那么NetworkPolicy就能够根据你的系统的机制和支持的网络访问控制逻辑,转换成对应操作系统级的规则,所以它也是一种一对多的实现逻辑,当然这种机制无非是由Kubernetes将底层操作系统的不同给隐藏起来,从而提供一个统一的中间层,仅此而已;
Network Policy基础工作逻辑
    网络策略的生效,一般而言是应用在某一个名称空间当中对一组一个或者所有Pod施加访问控制的,究竟是多少个取决于用户的定义,比如我们指明说要应用在这么名称空间之后没指明哪个Pod或者哪些Pod,那默认就是这个名称空间的所有Pod,当然我们也可以使用一个Pod Seleteor来定义一组匹配某个标签的Pod,其他Pod不受控制也行;
    在Kubernetes之上它的NetworkPolicy对于名称空间当中的Pod流量的访问控制大体上分为几种,一种是自己访问别人,一种是别人访问自己,在Kubernetes的NetworkPolicy当中这种自己访问别人的,我们还可以控制自己能够访问哪些别人以及如何进行访问,在Kubernetes使用一组规则来定义叫做egress(出站),同样的别人访问自己也使用一类规则来定义,叫做ingress(入站),这个ingress和此前的web服务ingress是不一样的,这个ingress是代表流量方向的,因此在NetworkPolicy当中我们可以通过egress来控制出站流量,来控制着把谁当作通信目标来通信,通过ingress来控制入站流量,能够控制能够接收哪些后端的访问,或者来自于哪些对端的访问,那么这些对端指定是另外一组Pod,或者另外一些主机,那么另外一组Pod我们还可以标识为另外一个名称空间的所有Pod,或者当前名称空间的另外一部分Pod,或者另外一组集群之外的主机等都行;
    所以标识对端有三种方式,比如某个名称空间的所有Pod,或者某些名称空间的所有Pod,或者某个名称空间能够被某个Pod Selector选择器所匹配到的一部分Pod,或者直接使用ip来标识,或者使用来自于哪个网段的;
    egress使用的to来标识,to表示出站报文,通常我们要主动访问别人,所以此时我们还可以使用ports用来标识我们要访问对端服务器的哪个端口的服务;
    ingress使用的是from来标识,from表示入站报文,在from这里也可以使用ports标识允许自己哪个端口被访问;
[root@node1 ~]# kubectl explain networkpolicy

配置清单参数
    如果to或者from下级子配置项,都配置了,那么满足其一就可以了;
egress:出站规则;
    ports:Pod要访问的端口;
    to.ipBlock:使用ip或者或者网络地址;
    to.namespaceSelector:指定名称空间的所有Pod;
    to.podSelector:使用标签来选择Pod;
ingress:入站规则;
    ports:Pod内部向外提供服务的端口t;
    from.ipBlock:使用ip或者或者网络地址;
    from.namespaceSelector:指定名称空间的所有Pod;
    from.podSelector:使用标签来选择Pod;
podSelector:Pod挑选器;
policyTypes:界定哪个规则生效,取值有三种,Egress、Igress、Egress  and Igress;
  • 因为Flannel是不具备网络策略功能的,如果要使用Flannel做网络策略那么还需要在Kubernetes集群之中加入一个网络策略插件calico;
NetwokPolicy配置示例
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  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
默认策略
    在Kubernetes上有个基本法则,只要实现了控制,就必须显示定义,否则就直接拒绝,以下面的例子为例如果policyTypes定义了哪种类型的策略,比如Egress,但是配置清单里面的Egress配置项没有给定其相关匹配规则那么就表示拒绝,如果给定了表示允许;
    其中大括号{}表示所有;
# 拒绝所有入口流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress
# 默认允许所有入口流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress
# 默认拒绝所有出口流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Egress
# 默认允许所有出口流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress
# 默认拒绝所有出口入口流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

实战示例

加入Calico网络插件
    文档地址:https://docs.projectcalico.org/v3.10/getting-started/kubernetes/installation/flannel
[root@node1 ~]# kubectl apply -f https://docs.projectcalico.org/v3.10/manifests/canal.yaml
[root@node1 ~]# kubectl get pods -n kube-system  -l k8s-app=canal -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP           NODE            NOMINATED NODE   READINESS GATES
canal-6t8g4   2/2     Running   0          76s   172.16.1.3   node2.cce.com   <none>           <none>
canal-7zcff   2/2     Running   0          76s   172.16.1.2   node1.cce.com   <none>           <none>
canal-s9hxl   2/2     Running   0          76s   172.16.1.4   node3.cce.com   <none>           <none>
构建测试环境
    创建两个namespace分别创建几个Pod,并给namespace和Pod加上响应的label,用于测试网络策略;
[root@node1 ~]# cat test.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: dev
  labels:
    ns-type: dev
---
apiVersion: v1
kind: Pod
metadata:
  name: dev-pod
  namespace: dev
  labels:
    pot-type: custom
spec:
  containers:
  - name: dev-container
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Namespace
metadata:
  name: pord
  labels:
    ns-type: pord
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pord-deployment
  namespace: pord
spec:
  replicas: 2
  selector:
    matchLabels:
      pot-type: deployment
  template:
    metadata:
      name: pord-pod
      namespace: pord
      labels:
        pot-type: deployment
    spec:
      containers:
      - name: prod-container
        image: ikubernetes/myapp:v2
# 查看创建的pod
[root@node1 ~]# kubectl get pods -n dev -o wide
NAME      READY   STATUS    RESTARTS   AGE     IP           NODE            NOMINATED NODE   READINESS GATES
dev-pod   1/1     Running   0          2m21s   10.244.2.3   node3.cce.com   <none>           <none>
[root@node1 ~]# kubectl get pods -n pord -o wide 
NAME                               READY   STATUS    RESTARTS   AGE     IP           NODE            NOMINATED NODE   READINESS GATES
pord-deployment-557876b6bc-2fvrw   1/1     Running   0          2m22s   10.244.1.3   node2.cce.com   <none>           <none>
pord-deployment-557876b6bc-s9vq2   1/1     Running   0          2m22s   10.244.3.4   node4.cce.com   <none>           <none>
# 测试进入dev下面的pod然后去请求pord下面的pod
[root@node1 ~]# kubectl exec -it -n dev dev-pod -- sh
/ # wget -O - -q 10.244.1.3/hostname.html
pord-deployment-557876b6bc-2fvrw  # 测试访问没问题
/ # wget -O - -q 10.244.3.4/hostname.html
pord-deployment-557876b6bc-s9vq2  # 测试访问没问题

实战示例

    下面的示例都基于上面的配置清单实现;
拒绝外部namespace访问
    在pord名称空间创建一个NetworkPolicy规则,拒绝外部namespace对内部web服务器的访问,只允许prod名称空间内部的Pod访问;
# 创建策略
[root@node4 ~]# cat deny-other-namespace.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-pord
  namespace: pord
spec:
  ingress:
  - from:
    - namespaceSelector:
        matchExpressions:  # 匹配表达式
        - key: ns-type # 指定key
          operator: In # 判断指定的key是否在下面的values列表里面
          values:
          - pord
  podSelector: {}
  policyTypes:
  - Ingress  # 只控制入站流量
[root@node4 ~]# kubectl apply -f deny-other-namespace.yaml
# 在dev名称空间测试是否能够访问
[root@node1 ~]# kubectl exec -it -n dev dev-pod -- sh
/ # wget -T 3 -O - -q 10.244.1.3/hostname.html
wget: download timed out  # 第一个Pod无法访问
/ # wget -T 3 -O - -q 10.244.3.4/hostname.html
wget: download timed out  # 第二个Pod无法访问
# 在prod名称空间新建一个自主式Pod测试是否能访问该名称空间内部的由deployment控制的几个Pod
[root@node2 ~]# kubectl exec -it -n pord pord-deployment-557876b6bc-2fvrw -- sh
/ # wget -T 3-O - -q 10.244.1.3/hostname.html
pord-deployment-557876b6bc-2fvrw
/ # wget -T 3 -O - -q 10.244.3.4/hostname.html
pord-deployment-557876b6bc-s9vq2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注