1、NetworkPolicy概述
官方說明:網絡策略(NetworkPolicy)是一種關於pod
間及pod
與其他網絡端點間所允許的通信規則的規范。
簡單來說,NetworkPolicy
就是對pod
進行網絡策略控制。用於為Kubernetes
實現更為精細的流量控制,實現租戶隔離機制。Kubernetes
使用標准的資源對象NetworkPolicy
供管理員按需定義網絡訪問控制策略。
2、NetworkPolicy策略模型
使用network policy
資源可以配置pod
的網絡,networkPolicy
是namespace scoped
的,他只能影響某個namespace
下的pod
的網絡出入站規則。
metadata
描述信息。podSelector
pod選擇器,選定的pod
所有的出入站流量要遵循本networkpolicy
的約束。policyTypes
策略類型。包括了Ingress
和Egress
,默認情況下一個policyTypes
的值一定會包含Ingress
,當有egress
規則時,policyTypes
的值中會包含Egress
。ingress
入站,即由其他網絡端點發往特定Pod
組的流量 ,通常由流量發出的源站點from
和流量的目標端口所定義 。egress
出站,即由特定的Pod
組發往其他網絡端點的流量 ,通常由流量的目標網絡端點to
和端口ports
來進行定義 。port
端口,TCP
或UDP
的端口號。to,from
端點,流量目標和流量源相關的組件, 它可以是CIDR
格式的IP
地址塊ipBlock
、網絡名稱空間選擇器namespaceSelector
匹配的名稱空間, 或Pod
選擇器podSelector
匹配的Pod
組。
對官方示例的說明:
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
該例子的效果如下:
1、default namespace
下label
包含role=db
的pod
,都會被隔絕,他們只能建立滿足networkPolicy
的ingress
和egress
描述的連接。即2-5點:
2、所有屬於172.17.0.0/16
網段的ip,除了172.17.1.0/24
中的ip,其他的都可以與上述pod
的6379
端口建立tcp
連接。
3、所有包含label:project=myproject
的namespace
中的pod
可以與上述pod
的6379
端口建立tcp
連接;
4、所有default namespace
下的label
包含role=frontend
的pod
可以與上述pod
的6379
端口建立tcp
連接;
5、允許上述pod
訪問網段為10.0.0.0/24
的目的ip
的5978
端口。
3、NetworkPolicy默認策略
默認情況下,如果名稱空間中不存在任何策略,則所有進出該名稱空間中的Pod的流量都被允許。以下示例用於更改該名稱空間中的默認行為。
- 默認拒絕所有入口流量
通過創建選擇所有容器但不允許任何進入這些容器的入口流量的NetworkPolicy
來為名稱空間創建default
隔離策略。這樣可以確保即使容器沒有選擇其他任何NetworkPolicy
,也仍然可以被隔離。此策略不會更改默認的出口隔離行為。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- 默認允許所有入口流量
如果要允許所有流量進入某個命名空間中的所有Pod
(即使添加了導致某些Pod
被視為“隔離”的策略),則可以創建一個策略來明確允許該命名空間中的所有流量。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
- 默認拒絕所有出口流量
通過創建選擇所有容器但不允許來自這些容器的任何出口流量的NetworkPolicy
來為名稱空間創建default egress
隔離策略。這樣可以確保即使沒有被其他任何NetworkPolicy
選擇的Pod
也不會被允許流出流量。此策略不會更改默認的ingress
隔離行為。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Egress
- 默認允許所有出口流量
如果要允許來自命名空間中所有Pod
的所有流量(即使添加了導致某些Pod
被視為“隔離”的策略),則可以創建一個策略,該策略明確允許該命名空間中的所有出口流量。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
- 默認拒絕所有入口和所有出口流量
為名稱空間創建default
策略,以通過在該名稱空間中創建以下NetworkPolicy
來阻止所有入站和出站流量。這樣可以確保即使沒有被其他任何NetworkPolicy
選擇的Pod
也不會被允許進入或流出流量。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
4、NetworkPolicy的實現
kubernetes
的網絡策略功能本身並不支持,依賴其所使用的網絡插件實現。因此,僅在使用那些支持網絡策略功能的網絡插件時才能夠配置網絡策略,企業內部可以使用簡單的flannel
、weave
、kube-router
等,適合公有雲的方案則有calico
等。不同的網絡實現原理vethpair
、bridge
、macvlan
等並不能統一地支持network policy
。每種解決方案各有其特定的網絡策略實現方式,它們的實現或依賴於節點自身的功能,或借助於Hypervisor
的特性,也可能是網絡自身的功能 。究其最終的底層實現原理,目前是基於linux iptables
實現,使用類似於nf_conntrack
檢查記錄網絡流量session
從而決定流量是否阻斷。
5、使用flannel+canal實現k8s的NetworkPolicy
Flannel
是解決容器網絡方案最為普遍和簡單的方案,Canal
代表了針對雲原生應用程序的最佳網絡策略解決方案,旨在讓用戶輕松的將Calico
和Flannel
網絡部署在一起作為統一的網絡解決方案,將Calico
的網絡策略執行和Flannel
的疊加及非疊加網絡連接選項的豐富功能相結合。
在Calico
的官方文檔的相應部分中描述到此種方案是:用於策略的Calico
和用於網絡的Flannel
相組合。官方文檔見:https://docs.projectcalico.org/v3.10/getting-started/kubernetes/installation/flannel
5.1、實驗環境說明
實驗k8s環境是kubeadm
搭建的1master+2node最新版本1.16.2版本k8s,cni
版本為0.3.1,flannel
版本為0.11.0,flannel
后端是vxlan
模式並且已經開啟了Directrouting
。
[root@k8s-master-01 ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:18:23Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:09:08Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
[root@k8s-master-01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master-01 Ready master 4d1h v1.16.2
k8s-node-01 Ready <none> 4d v1.16.2
k8s-node-02 Ready <none> 4d v1.16.2
[root@k8s-master-01 ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-58cc8c89f4-bd6s8 1/1 Running 2 4d1h
coredns-58cc8c89f4-bv5rl 1/1 Running 2 4d1h
etcd-k8s-master-01 1/1 Running 2 4d1h
kube-apiserver-k8s-master-01 1/1 Running 2 4d1h
kube-controller-manager-k8s-master-01 1/1 Running 2 4d1h
kube-flannel-ds-amd64-grzvh 1/1 Running 1 2d22h
kube-flannel-ds-amd64-hrd4h 1/1 Running 1 2d22h
kube-flannel-ds-amd64-l8rtk 1/1 Running 1 2d22h
kube-proxy-bp98z 1/1 Running 2 4d
kube-proxy-kvwgh 1/1 Running 2 4d
kube-proxy-n8lj6 1/1 Running 2 4d1h
kube-scheduler-k8s-master-01 1/1 Running 2 4d1h
5.2、安裝canal
按照官方文檔說明安裝:https://docs.projectcalico.org/v3.10/getting-started/kubernetes/installation/flannel
部署時canal支持將數據存儲於etcd中,支持選擇專用的etcd存儲,也能夠以kubernetes api server作為后端存儲,這里選擇以后種方式進行。
結合flannel
工作時,Calico
提供的默認配置清單中是以flannel
默認使用的10.244.0.0/16
為Pod
網絡,因此,請確保kube-controller-manager
程序在啟動時通過--clustr-cidr
選項設置使用了此網絡地址,並且--allocate-node-cidrs
的值應設置為true
。在使用kubeadm
安裝的此版本集群中,此選項均已配置。
如果集群cidr網絡地址不是默認值,需要修改配置再進行部署。
[root@k8s-master-01 canal]# curl https://docs.projectcalico.org/v3.10/manifests/canal.yaml -O
[root@k8s-master-01 canal]# kubectl apply -f canal.yaml
configmap/canal-config created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrole.rbac.authorization.k8s.io/flannel configured
clusterrolebinding.rbac.authorization.k8s.io/canal-flannel created
clusterrolebinding.rbac.authorization.k8s.io/canal-calico created
daemonset.apps/canal created
serviceaccount/canal created
[root@k8s-master-01 ~]# kubectl get pods -n kube-system -o wide|grep canal
canal-rxb97 2/2 Running 0 4h25m 192.168.2.12 k8s-node-02 <none> <none>
canal-tdcgf 2/2 Running 0 4h25m 192.168.2.10 k8s-master-01 <none> <none>
canal-vll8z 2/2 Running 0 4h25m 192.168.2.11 k8s-node-01 <none> <none>
5.3、應用示例
5.3.1、創建默認服務
運行一組nginx
pod並暴露80端口
[root@k8s-master-01 ~]# kubectl run nginx --image=nginx --replicas=3 --port=80 --expose
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
service/nginx created
deployment.apps/nginx created
[root@k8s-master-01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-5578584966-8spqk 1/1 Running 0 15s
nginx-5578584966-sjlvc 1/1 Running 0 15s
nginx-5578584966-xm4pb 1/1 Running 0 15s
[root@k8s-master-01 ~]# kubectl get svc nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.1.8.146 <none> 80/TCP 16s
5.3.2、測試訪問
[root@k8s-master-01 ~]# kubectl run busy1 --rm -it --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.1.8.146:80)
remote file exists ## 訪問成功
5.3.4、創建默認policy
創建默認拒絕所有入站流量的networkpolicy,測試訪問
[root@k8s-master-01 networkpolicy]# vim default-deny-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
[root@k8s-master-01 networkpolicy]# kubectl apply -f default-deny-ingress.yaml
networkpolicy.networking.k8s.io/default-deny created
[root@k8s-master-01 ~]# kubectl run busy1 --rm -it --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.1.8.146:80)
wget: download timed out ## 訪問失敗
5.3.5、創建一個允許帶有access=true的Pod訪問nginx的網絡策略
[root@k8s-master-01 networkpolicy]# vim nginx-policy.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: access-nginx
spec:
podSelector:
matchLabels:
run: nginx
ingress:
- from:
- podSelector:
matchLabels:
access: "true"
[root@k8s-master-01 networkpolicy]# kubectl apply -f nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created
使用不帶access=true標簽的pod訪問服務
[root@k8s-master-01 ~]# kubectl run busy1 --rm -it --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.1.8.146:80)
wget: download timed out ## 訪問失敗
使用帶access=true標簽的pod訪問服務
[root@k8s-master-01 ~]# kubectl run busy2 --rm -it --labels="access=true" --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.1.8.146:80)
remote file exists ## 訪問成功
5.4、生產使用場景
5.4.1、禁止訪問指定服務
kubectl run web --image=nginx --labels app=web,env=prod --expose --port 80
網絡策略
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-deny-all
spec:
podSelector:
matchLabels:
app: web
env: prod
5.4.2、只允許指定pod訪問服務
kubectl run apiserver --image=nginx --labels app=bookstore,role=api --expose --port 80
網絡策略
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: api-allow
spec:
podSelector:
matchLabels:
app: bookstore
role: api
ingress:
- from:
- podSelector:
matchLabels:
app: bookstore
5.4.3、禁止 namespace 中所有 Pod 之間的相互訪問
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: default
spec:
podSelector: {}
5.4.4、禁止其他 namespace 訪問服務
kubectl create namespace secondary
kubectl run web --namespace secondary --image=nginx \
--labels=app=web --expose --port 80
網絡策略
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
namespace: secondary
name: web-deny-other-namespaces
spec:
podSelector:
matchLabels:
ingress:
- from:
- podSelector: {}
5.4.5、只允許指定namespace訪問服務
kubectl run web --image=nginx \
--labels=app=web --expose --port 80
網絡策略
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-prod
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector:
matchLabels:
purpose: production
5.4.6、允許外網訪問服務
kubectl run web --image=nginx --labels=app=web --port 80
kubectl expose deployment/web --type=LoadBalancer
網絡策略
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-external
spec:
podSelector:
matchLabels:
app: web
ingress:
- ports:
- port: 80
from: []
參考來源:
https://kubernetes.io/zh/docs/concepts/services-networking/network-policies/
https://docs.projectcalico.org/v3.10/getting-started/kubernetes/installation/flannel
https://github.com/ahmetb/kubernetes-network-policy-recipes