基于canel的网络策略

背景描述:
我们的k8s集群使用flannel cni插件,由于flannel 不支持 networkpolicy, 所以我们研究了其他的支持网络策略的cni,calico 和canal

为什么是canal:
刚开始去研究了 calico ,发现calico支持 ipip 和bgp两种策略, 由于我们IT当前不支持BGP网络, 而IPIP模式对于calico而言,意义不是很大,所以决定部署更为简单的canal

什么是canal:
Canal是Flannel和Calico联合发布的一个统一网络插件,提供CNI网络插件,并支持network policy

Colico网络插件工作于192.168.0.0/16网络并且每一个节点分配一个节点网络,例如node1是192.168.0.0/16网络,node2是192.168.1.0/16网络
flanel无法提供网络策略,所以在flanel之上安装colico。在flanel提供的网络基础之上在额外提供Colico,此处的Colico只服务与网络策略,
官网提供的安装方式:https://projectcalico.docs.tigera.io/getting-started/kubernetes/flannel/flannel

Colico部署有两种方式:

使用一套独立的etcd
使用kubernetes的etcd,不能直接操作etcd而是需要通过kubernetes API Server调用etcd

使用 Kubernetes API 数据存储进行安装(推荐)
一、确保 Kubernetes 控制器管理器设置了以下标志:

--cluster-cidr=<your-pod-cidr>和--allocate-node-cidrs=true.

提示:在 kubeadm 上,您可以传递--pod-network-cidr=<your-pod-cidr> 给 kubeadm 来设置两个 Kubernetes 控制器标志。
二、下载 Kubernetes API 数据存储的 flannel 网络清单。

curl https://projectcalico.docs.tigera.io/manifests/canal.yaml -O

三、如果您使用的是 pod CIDR 10.244.0.0/16,请跳到下一步。如果您在 kubeadm 中使用不同的 pod CIDR,则无需更改 - Calico 将根据运行配置自动检测 CIDR。对于其他平台,请确保取消注释清单中的 CALICO_IPV4POOL_CIDR 变量并将其设置为与您选择的 pod CIDR 相同的值。

四、发出以下命令来安装 Calico。

kubectl apply -f canal.yaml

五、验证是否成功安装canel

kubectl get pods -n kube-system
NAME                             READY     STATUS    RESTARTS   AGE
canal-426md                      3/3       Running   0          13s
canal-srhh7                      3/3       Running   0          13s
canal-2gzvq                      3/3       Running   0          13s

与flanel一样是一个DaemonSet控制器,每个节点都需要部署一个canal Pod,所以当前有多少个节点就会存在多少个canal Pod
canal原理图

kubernetes中的网络控制策略:

NetworkPolicy是kubernetes对pod的隔离手段,可以看到,NetworkPolicy实际上只是宿主机上的一系列iptables规则。

Egress:表示出站流量,就是pod作为客户端访问外部服务,pod地址作为源地址(客户端),服务端作为目标地址进行通信。通常来说客户端端口是随机的,而服务端地址是固定的。策略可以定义目标地址或者目标端口

Ingress:表示入站流量,pod地址和服务作为服务端(目标地址)提供外部访问。pod地址作为目标地址,客户端是源地址。策略可以定义源地址目标端口
podSelector:规则生效在那个pod上,可以配置单个pod或者一组pod。可以定义单方向。空 podSelector选择命名空间中的所有Pod。

kubectl explain networkpolicy.spec讲解:

egress: 出站流量规则 可以根据ports和to去定义规则。ports下可以指定目标端口和协议。to(目标地址):目标地址分为ip地址段、pod、namespace,
ingress: 入站流量规则 可以根据ports和from。ports下可以指定目标端口和协议。from(来自那个地址可以进来):地址分为ip地址段、pod、namespace
policyTypes:指定那个规则 那个规则生效,同时指定Egress和Ingress,意味着同时生效,不指定就是全部生效。
podSelector:定义NetworkPolicy的限制范围。直白的说就是规则应用到哪个pod上,哪个Pod就生效。podSelector: {},留空就是定义对当前namespace下的所有pod生效。没有定义白名单的话 默认就是Deny ALL (拒绝所有)

kubectl explain networkpolicy.spec.egress.to: #to表示目标地址

podSelector:       指定一组Pod,控制两组Pod之间的通信,源可以是一组Pod,目标也可以是一组Pod
ipBlock:          IP地址串,可以是一个IP地址范围,也可以是一个准确的IP
namespaceSelector:名称空间选择器,指定一个名称空间且这个名称空间范围内的所有Pod都可以访问
设置to可以是三种情况中的一个,也可以三种交叉使用

kubectl explain networkpolicy.spec.ingress:

from:指定那些地址可以访问,可以是一个地址范围也可以是一个具体地址
ports:目标端口,也就是自己开放的端口

举例:
事先创建两个namespace (dev,prod)

在dev的namespace下定义一个入站流量拒绝的规则:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-policy
spec:
  podSelector: {}        #留空意味着对整个namespace生效
  policyTypes:
  - Ingress

policyTypes指定了Ingress而Ingress没有定义任何规则,也就是说拒绝所有入站流量,没有指定Egress也就是说不控制Egress,而不控制Egress默认都是允许出站的。
以上没有指定某个名称空间,执行时在后面执行文件后面指定namespace,然后这个yaml就在指定的namespace下面创建对象或者服务,这样可以更灵活的使用yaml。

kubectl apply -f network-policy.yaml  -n dev

在dev和prod的namespace下个各自创建一个pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  labels:
    name: myapp
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1

kubectl apply -f policy-pod.yaml  -n dev

kubectl apply -f policy-pod.yaml  -n prod

查看是否创建成功

kubectl get pod -o wide   -n prod 
NAME      READY     STATUS    RESTARTS   AGE       IP           NODE
pod-1     1/1       Running   0          3h        10.244.2.3   k8s-node02
kubectl get pod -owide   -n dev 
NAME      READY     STATUS    RESTARTS   AGE       IP           NODE
pod-1     1/1       Running   0          3h        10.244.2.2   k8s-node02
curl 10.244.2.3
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
curl 10.244.2.2 不通

允许所有Ingress入站请求:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-policy
spec:
  podSelector: {} #留空意味着对整个namespace生效
  ingress:
  - {}        
  policyTypes:  
  - Ingress

验证是否成功:

curl 10.244.2.2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

放行特定的入站访问流量:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-myapp-policy
spec:
  podSelector: 
    matchLabels:
      app: myapp   #选择app=myapp 的标签放行
    ingress:
    - from:
      - ipBlock:   #地址段
          cidr: 10.244.0.0/16  #允许这个地址段访问
      except:    #排除一下地址不可以访问
      - 10.244.1.2/32
      ports:
      - port: 80   只运行访问80端口
        protocol: TCP
kubectl apply -f alloy-pod.yaml -n dev 

任何在dev这个名称空间中拥有标签app且标签值为myapp的都允许对其80端口进行访问。
在prod的namespace下定义一个出站流量拒绝的规则:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-policy
spec:
  podSelector: {}         #留空意味着对整个namespace生效
  policyTypes:  
  - Egress
kubectl apply -f policy-pod.yaml  -n prod

上方在prod里面创建的有Pod所以直接使用已创建的prod就好

kubectl exec -it pod-1 -n prod -- /bin/sh 
ping 10.244.2.3   不通

允许所有Egress出站请求:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-policy
spec:
  podSelector: {} #留空意味着对整个namespace生效
  Egress:
  - {}        
  policyTypes:  
  - Egress
kubectl apply -f policy-pod.yaml  -n prod

验证是否成功:

ping 10.244.2.3
Pinging 10.244.2.3 [10.244.2.3]with 32 bytes of data:
Reply from 10.244.2.3: bytes=32 time=8ms TTL=45

完整的NetworkPolicy对象:

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

该规则只在default 的namespace下,携带role:db标签的pod生效。限制请求的类型包括Ingress和Egress。
Kubernetes会拒绝任何访问被隔离pod的请求,除非这个请求来自以下“白名单”里的对象。并且访问的是被隔离pod的6379端口。
default Namespace里的,携带了role=fronted标签的pod可以访问
任何Namespace里的,携带了project=myproject标签的pod可以访问
任何源地址数据172.17.0.0/16网段,且不属于172.17.1.0/24网段的请求
Kubernetes会拒绝被隔离pod对外发起任何请求,除非请求的目的地址属于10.0.0.0/24网段,并且访问的是该网段地址的5978端口。

最后修改:2023 年 06 月 09 日
如果觉得我的文章对你有用,请随意赞赏