从青铜到王者,一篇文章讲清楚 Kubernetes 网络策略!
一、Network Policy简介
随着微服务架构的日渐盛行,Serverless 框架的逐步落地,应用上云后带来了模块间网络调用需求的大规模增长,Kubernetes 自 1.3 引入了 Network Policy,其提供以应用为中心, 基于策略的网络控制,用于隔离应用以减少攻击面。
Pod 之间能否通信可通过如下三种组合进行确认:
其他被允许的 Pods(例如:Pod 无法限制对自身的访问)
被允许访问的 namespace
IP CIDR(例如:与 Pod 运行所在节点的通信总是被允许的)
标签选择器
来设定哪些流量可以进入或离开 Pod。同时,当创建基于 IP 的 NetworkPolicy 时,可以基于 IP CIDR 来定义策略。版本变迁:
二、简要介绍
相关说明
在使用 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 中的任何目的地。
该网络策略总结如下:
隔离 default名字空间下 role=db 的 Pod 。
出口限制:允许符合以下条件的 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)入口限制:允许从带有 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/onpremises
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
3) 部署应用
部署 nginx 服务
$ 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
/ #
测试网络策略
如果只让那些拥有标签 access: true
的 Pod 访问 nginx
服务, 那么可以创建一个如下所示的 NetworkPolicy 对象:
$ 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 该策略部署于default
podSelector为{}指匹配所有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网络策略应用到该标签pod
namespaceSelector: {}匹配所有命名空间
访问测试
# 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>
...
(允许访问)
来源:本文转自公众号 DCOS,点击查看原文。
还不过瘾?还想了解更多大型企业 DevOps/DevSecOps 落地方案?与他们进行面对面交流?
GOPS 全球运维大会 2022 · 深圳站,工行、农行、中行等大型金融企业实战分享,敬请期待~