一文搞懂Kubernetes网络策略(上)
今天zouyee为大家带来《一文搞懂Kubernetes网络策略(上)》,其中《kuberneter调度由浅入深:框架》正在编写中,敬请期待,当前涉及版本均为1.20.+。
一、Network Policy简介
随着微服务架构的日渐盛行,Serverless框架的逐步落地,应用上云后带来了模块间网络调用需求的大规模增长,Kubernetes 自 1.3 引入了 Network Policy,其提供以应用为中心, 基于策略的网络控制,用于隔离应用以减少攻击面。Pod之间能否通信可通过如下三种组合进行确认:
1. 其他被允许的 Pods(例如:Pod 无法限制对自身的访问)
2. 被允许访问的namespace
3. IP CIDR(例如:与 Pod 运行所在节点的通信总是被允许的)
在定义基于 Pod 或namespace的 NetworkPolicy 时,可以使用`标签选择器`来设定哪些流量可以进入或离开 Pod。同时,当创建基于 IP 的 NetworkPolicy 时,可以基于 IP CIDR 来定义策略。
以下结构体示意图辅助理解,后面章节有具体说明:
版本变迁
Kubernetes版本 | Networking API版本 | Kubernetes版本 |
v1.5-v1.6 | extension/v1beta1 | 需要在kube-apiserver启动命令行开启extensions/v1beta1/networkpolicies |
v1.7 | networking.k8s.io/v1 | |
v1.8 | networking.k8s.io/v1 | 增加Egress及IPBlock特性 |
二、简要介绍
相关说明
默认情况下,Pod 是非隔离的,它们接受任何流量。
Pod 在被某 NetworkPolicy 选中时进入隔离状态。一旦名字空间中有 NetworkPolicy 选择了特定的 Pod,该 Pod 会拒绝该 NetworkPolicy 所不允许的连接。(名字空间下其他未被 NetworkPolicy 所选择的 Pod 会继续接受所有的流量)
网络策略不会冲突。如果任何一个或多个策略选择了一个 Pod, 则该 Pod 受限于这些策略的 入站(Ingress)/出站(Egress)规则的并集。
⚠️在使用 Network Policy 时,网络插件需要支持 Network Policy,如 Calico、Romana、Weave Net 和 Trireme 等,其中Engress为 出口流量,Ingress为 入口流量。
结构体说明
staging/src/k8s.io/api/networking/v1/types.go
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: network-policy-sample
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 对象一样,NetworkPolicy 需要 apiVersion、 kind 和 metadata 字段。
spec:NetworkPolicy 规约中包含了在名字空间中定义特定网络策略所需的所有信息。
podSelector:每个 NetworkPolicy 都包括一个 podSelector,它选择适用该该策略的 Pod。示例中的策略选择带有 “role=db” 标签的 Pod。若podSelector为空的,则选择名字空间下所有 Pod。
policyTypes: 每个 NetworkPolicy 都包含一个 policyTypes 列表,其中包含 Ingress 或 Egress 或(两者亦可)。policyTypes 字段表示给定的策略是应用于 所选 Pod 的入口流量还是来出口流量(两者亦可)。如果 NetworkPolicy 未指定 policyTypes 则默认情况下始终设置 Ingress;如果 NetworkPolicy 有任何出口规则的话则设置 Egress。
ingress: 每个 NetworkPolicy 可包含一个 ingress 规则的白名单列表。每个规则都允许同时匹配 from 和 ports 部分的流量。示例策略中包含一条 简单的规则:它匹配某个特定端口,第一个通过 ipBlock 指定,第二个通过 namespaceSelector 指定,第三个通过 podSelector 指定。
egress: 每个 NetworkPolicy 可包含一个 egress 规则的白名单列表。每个规则都允许匹配 to 和 port 部分的流量。该示例策略包含一条规则, 该规则指定端口上的流量匹配到 10.0.0.0/24 中的任何目的地。
该网络策略总结如下:
1. 隔离 default名字空间下 role=db 的 Pod 。
2. 出口限制:允许符合以下条件的 Pod 连接到 default名字空间下标签为 role=db的所有 Pod 的 6379 TCP 端口:
a) default名字空间下带有 role=frontend 标签的所有 Pod
b) 带有 project=myproject 标签的所有名字空间中的 Pod
c) IP 地址范围为172.17.0.0–172.17.0.255 和 172.17.2.0–172.17.255.255(即除了 172.17.1.0/24 之外的所有 172.17.0.0/16)
3. 入口限制:允许从带有 role=db标签的名字空间下的任何 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口。
简单示例
以 calico 为例看一下 Network Policy 的具体用法。
1) 配置 kubelet 使用 CNI 网络插件(默认已经配置,无需更改)
kubelet --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin ...
2) 安装 calio 网络插件
1 2 3 4 | # 注意修改 CIDR,需要跟 k8s pod-network-cidr 一致,默认为 192.168.0.0/16 # 当前选择的是小于50节点的安装方式,具体安装可查看 # https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml |
3) 部署应用
部署 nginx 服务
1 2 3 4 | $ kubectl create deployment nginx --image=nginx deployment "nginx" created $ kubectl expose deployment nginx --port=80 service "nginx" exposed |
测试网络
$ kubectl get svc,pod
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 186d
service/nginx ClusterIP 10.233.27.142 <none> 80/TCP 2s
NAME READY STATUS RESTARTS AGE
pod/nginx-f89759699-kfmbj 1/1 Running 0 62s
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.233.27.142:80)
remote file exists
/ #
测试网络策略
$ cat nginx-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: access-nginx
spec:
podSelector:
matchLabels:
app: nginx
ingress:
- from:
- podSelector:
matchLabels:
access: "true"
$ kubectl create -f nginx-policy.yaml
networkpolicy "access-nginx" created
# 不带 access=true 标签的 Pod 还是无法访问 nginx 服务
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.233.27.142:80)
wget: download timed out
/ #
# 而带有 access=true 标签的 Pod 可以访问 nginx 服务
$ kubectl run busybox --rm -ti --labels="access=true" --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.233.27.142:80)
/ #
三、应用场景
一般场景
a. 禁止访问指定服务
kubectl run web --image=nginx --labels app=web --expose --port 80
# 未有策略限制时,可以访问
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- http://web
<!DOCTYPE html>
<html>
<head>
创建网络策略
# cat web-deny-all.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-deny-all
spec:
podSelector:
matchLabels:
app: web
ingress: []
$ kubectl apply -f web-deny-all.yaml
networkpolicy "web-deny-all" created
访问测试
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web
wget: download timed out
b. 限制访问指定服务
kubectl run apiserver --image=nginx --labels app=bookstore,role=api --expose --port 80
创建网络
# cat api-allow.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: api-allow
spec:
podSelector:
matchLabels:
app: bookstore
role: api
ingress:
- from:
- podSelector:
matchLabels:
app: bookstore
# kubectl apply -f api-allow.yaml
networkpolicy "api-allow" created
访问测试
创建不加label的pod,预期结果,访问被限制
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://apiserver
wget: download timed out
/ # exit
创建带app=bookstore标签的pod,预期结果,访问被限制
$ kubectl run busybox --rm -ti --image=busybox --labels app=bookstore,role=frontend /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://apiserver
<!DOCTYPE html>
<html><head>
/ # exit
c. 放通访问限制
kubectl run apiserver --image=nginx --labels app=bookstore,role=api --expose --port 80
应用a中的网络策略,限制所有流量
# cat web-deny-all.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-deny-all
spec:
podSelector:
matchLabels:
app: web
ingress: []
$ kubectl apply -f web-deny-all.yaml
networkpolicy "web-deny-all" created
创建网络策略
# cat web-deny-all.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-all
namespace: default
spec:
podSelector:
matchLabels:
app: web
ingress:
- {}
$ kubectl apply -f web-allow-all.yaml
networkpolicy "web-allow-all" created
# 需要注意deny跟allow的细微差别就是[]与{},其中{}代表
- from:
podSelector: {}
namespaceSelector: {}
namespace限制
a. 禁止namespace中非白名单流量
创建网络策略
# cat default-deny-all.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: default-deny-all
namespace: default
spec:
podSelector: {}
ingress: []
# kubectl apply -f default-deny-all.yaml
说明:
namespace: default
该策略部署于defaultpodSelector
为{}
指匹配所有pod,因而该策略对default命名空间的所有pod都有效ingress
未指定,因而对于所有进入流量都禁止
b. 禁止其他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: {}
访问测试
# default命名空间访问
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.secondary
wget: download timed out
/ # exit
# secondary命名空间访问
$ kubectl run busybox --rm -ti --image=busybox --namespace=secondary /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.secondary
<!DOCTYPE html>
<html>
c. 放通所有namespace流量
kubectl run web --image=nginx --labels app=web --expose --port 80
创建网络策略
# cat web-allow-all-namespaces.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
namespace: default
name: web-allow-all-namespaces
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector: {}
# kubectl apply -f web-allow-all-namespaces.yaml
# kubectl create namespace secondary
说明:
app: web
网络策略应用到该标签podnamespaceSelector: {}
匹配所有命名空间
访问测试
# kubectl run busybox --rm -ti --image=busybox --namespace=secondary /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.secondary
<!DOCTYPE html>
<html>
d. 指定namespace访问服务
# kubectl run web --image=nginx \
--labels=app=web --expose --port 80
# kubectl create namespace dev
# kubectl label namespace/dev purpose=testing
# kubectl create namespace prod
# kubectl label namespace/prod purpose=production
创建网络策略
# cat web-allow-prod.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-prod
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector:
matchLabels:
purpose: production
# kubectl apply -f web-allow-prod.yaml
e. 允许其他namespace指定pod的流量
⚠️ Kubernetes 1.11后支持podSelector 与namespaceSelector的运算符操作,同时需要网络插件支持
# kubectl run web --image=nginx \
--labels=app=web --expose --port 80
# kubectl create namespace other
# kubectl create namespace other
创建网络策略
# cat web-allow-all-ns-monitoring.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-all-ns-monitoring
namespace: default
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector: # 选择namespaces中带有team=operations标签的pod
matchLabels:
team: operations
podSelector: # 选择带有type=monitoring标签的pod
matchLabels:
type: monitoring
# kubectl apply -f web-allow-all-ns-monitoring.yaml
访问测试
kubectl run busybox --rm -ti --image=busybox /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
(访问限制)
/ # exit
# kubectl run busybox --rm -ti --image=busybox --labels type=monitoring /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
(访问限制)
# kubectl run busybox --rm -ti --image=busybox --namespace=other /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
(访问限制)
# kubectl run busybox --rm -ti --image=busybox --namespace=other --labels type=monitoring /bin/sh
If you don't see a command prompt, try pressing enter.
/ # wget -qO- --timeout=2 http://web.default
<!DOCTYPE html>
<html>
<head>
...
(允许访问)
未完,待续
参考资料
1. Kubernetes 官网:https://kubernetes.io/docs/concepts/services-networking/network-policies/
2. Declare Network Policy: https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/
3. Securing Kubernetes Cluster Networking: https://ahmet.im/blog/kubernetes-network-policy/
4. fesikyer network policy: https://github.com/feiskyer/kubernetes-handbook/blob/master/concepts/network-policy.md
END
往期 · 精选