查看原文
其他

从青铜到王者,一篇文章讲清楚 Kubernetes 网络策略!

zouyee 高效运维 2022-07-13

一、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 来定义策略。
以下结构体示意图辅助理解,后面章节有具体说明:

版本变迁:

二、简要介绍

相关说明

默认情况下,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
下面是 NetworkPolicy 的一个示例,如需完整说明,可参看结构定义文档:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: network-policy-sample namespace: defaultspec: 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 网络插件

# 注意修改 CIDR,需要跟 k8s pod-network-cidr 一致,默认为 192.168.0.0/16# 当前选择的是小于50节点的安装方式,具体安装可查看# https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremiseskubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

3) 部署应用

部署 nginx 服务

$ kubectl create deployment nginx --image=nginxdeployment "nginx" created$ kubectl expose deployment nginx --port=80service "nginx" exposed

测试网络

$ kubectl get svc,podNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 186dservice/nginx ClusterIP 10.233.27.142 <none> 80/TCP 2s
NAME READY STATUS RESTARTS AGEpod/nginx-f89759699-kfmbj 1/1 Running 0 62s
$ kubectl run busybox --rm -ti --image=busybox /bin/shIf you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginxConnecting to nginx (10.233.27.142:80)remote file exists/ #

测试网络策略

如果只让那些拥有标签 access: true 的 Pod 访问 nginx 服务, 那么可以创建一个如下所示的 NetworkPolicy 对象:

$ cat nginx-policy.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: access-nginxspec: podSelector:    matchLabels:     app: nginx    ingress: - from: - podSelector: matchLabels: access: "true"
$ kubectl create -f nginx-policy.yamlnetworkpolicy "access-nginx" created
# 不带 access=true 标签的 Pod 还是无法访问 nginx 服务$ kubectl run busybox --rm -ti --image=busybox /bin/shIf you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginxConnecting 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/shIf you don't see a command prompt, try pressing enter.
/ # wget --spider --timeout=1 nginxConnecting 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/shIf you don't see a command prompt, try pressing enter./ # wget -qO- http://web<!DOCTYPE html><html><head>

创建网络策略

# cat web-deny-all.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: name: web-deny-allspec: podSelector: matchLabels: app: web ingress: []$ kubectl apply -f web-deny-all.yamlnetworkpolicy "web-deny-all" created

访问测试

$ kubectl run busybox --rm -ti --image=busybox /bin/shIf you don't see a command prompt, try pressing enter./ # wget -qO- --timeout=2 http://webwget: download timed out

b. 限制访问指定服务

kubectl run apiserver --image=nginx --labels app=bookstore,role=api --expose --port 80

创建网络

# cat api-allow.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: name: api-allowspec: podSelector: matchLabels: app: bookstore role: api ingress: - from: - podSelector: matchLabels: app: bookstore# kubectl apply -f api-allow.yamlnetworkpolicy "api-allow" created

访问测试

创建不加label的pod,预期结果,访问被限制$ kubectl run busybox --rm -ti --image=busybox /bin/shIf you don't see a command prompt, try pressing enter./ # wget -qO- --timeout=2 http://apiserverwget: download timed out/ # exit创建带app=bookstore标签的pod,预期结果,访问被限制$ kubectl run busybox --rm -ti --image=busybox --labels app=bookstore,role=frontend /bin/shIf 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.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: name: web-deny-allspec: podSelector: matchLabels: app: web ingress: []$ kubectl apply -f web-deny-all.yamlnetworkpolicy "web-deny-all" created

创建网络策略

# cat web-deny-all.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: name: web-allow-allnamespace: defaultspec: podSelector: matchLabels: app: web ingress: - {}$ kubectl apply -f web-allow-all.yamlnetworkpolicy "web-allow-all" created# 需要注意deny跟allow的细微差别就是[]与{},其中{}代表- from: podSelector: {} namespaceSelector: {}

namespace限制

a.禁止namespace中非白名单流量

创建网络策略

# cat default-deny-all.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: name: default-deny-all namespace: defaultspec: podSelector: {} ingress: []# kubectl apply -f default-deny-all.yaml

说明:

  • namespace: default 该策略部署于default

  • podSelector为{}指匹配所有pod,因而该策略对default命名空间的所有pod都有效

  • ingress未指定,因而对于所有进入流量都禁止

b.禁止其他namespace流量

kubectl create namespace secondarykubectl run web --namespace secondary --image=nginx \ --labels=app=web --expose --port 80

创建网络策略

kind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: namespace: secondary name: web-deny-other-namespacesspec: podSelector: matchLabels:  ingress: - from: - podSelector: {}
访问测试
# default命名空间访问$ kubectl run busybox --rm -ti --image=busybox /bin/shIf you don't see a command prompt, try pressing enter./ # wget -qO- --timeout=2 http://web.secondarywget: download timed out/ # exit# secondary命名空间访问$ kubectl run busybox --rm -ti --image=busybox --namespace=secondary /bin/shIf 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.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: namespace: default name: web-allow-all-namespacesspec: podSelector: matchLabels: app: web ingress: - from: - namespaceSelector: {}# kubectl apply -f web-allow-all-namespaces.yaml# kubectl create namespace secondary

说明:

  • app: web网络策略应用到该标签pod

  • namespaceSelector: {}匹配所有命名空间

访问测试

# kubectl run busybox --rm -ti --image=busybox --namespace=secondary /bin/shIf 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.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata:name: web-allow-prodspec: 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.yamlkind: NetworkPolicyapiVersion: networking.k8s.io/v1metadata: name: web-allow-all-ns-monitoring namespace: defaultspec: 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/shIf you don't see a command prompt, try pressing enter./ # wget -qO- --timeout=2 http://web.defaultwget: download timed out
(访问限制)/ # exit
# kubectl run busybox --rm -ti --image=busybox --labels type=monitoring /bin/shIf you don't see a command prompt, try pressing enter./ # wget -qO- --timeout=2 http://web.defaultwget: download timed out(访问限制)# kubectl run busybox --rm -ti --image=busybox --namespace=other /bin/shIf you don't see a command prompt, try pressing enter./ # wget -qO- --timeout=2 http://web.defaultwget: download timed out(访问限制)# kubectl run busybox --rm -ti --image=busybox --namespace=other --labels type=monitoring /bin/shIf you don't see a command prompt, try pressing enter./ # wget -qO- --timeout=2 http://web.default<!DOCTYPE html><html><head>...(允许访问)

来源:本文转自公众号 DCOS,点击查看原文

还不过瘾?还想了解更多大型企业 DevOps/DevSecOps 落地方案?与他们进行面对面交流?

GOPS 全球运维大会 2022 · 深圳站,工行、农行、中行等大型金融企业实战分享,敬请期待~

近期好文

中国信通院云大所牛晓玲:XOps浪潮下,企业IT提质增效的新思考

“高效运维”公众号诚邀广大技术人员投稿

投稿邮箱:jiachen@greatops.net,或添加联系人微信:greatops1118。
点个“在看”,一年不宕机

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存