NetworkPolicy
随着微服务的流行,越来越多的云服务平台需要大量模块之间的网络调用。Kubernetes 在 1.3 引入了 Network Policy,Network Policy 提供了基于策略的网络控制,用于隔离应用并减少攻击面。它使用标签选择器模拟传统的分段网络,并通过策略控制它们之间的流量以及来自外部的流量。
在使用 Network Policy 时,需要注意
  • v1.6 以及以前的版本需要在 kube-apiserver 中开启 extensions/v1beta1/networkpolicies
  • v1.7 版本 Network Policy 已经 GA,API 版本为 networking.k8s.io/v1
  • v1.8 版本新增 EgressIPBlock 的支持
  • v1.21版本新增 endPort 的支持用于设置端口范围(需要配置 --feature-gates=NetworkPolicyEndPort=true
  • 网络插件要支持 Network Policy,如 Calico、Romana、Weave Net 和 trireme 等,参考 这里

Kubernetes 版本
Networking API 版本
v1.5-v1.6
extensions/v1beta1
v1.7+
networking.k8s.io/v1

默认情况下,所有 Pod 之间是全通的。每个 Namespace 可以配置独立的网络策略,来隔离 Pod 之间的流量。
v1.7 + 版本通过创建匹配所有 Pod 的 Network Policy 来作为默认的网络策略,比如默认拒绝所有 Pod 之间 Ingress 通信
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
默认拒绝所有 Pod 之间 Egress 通信的策略为
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Egress
甚至是默认拒绝所有 Pod 之间 Ingress 和 Egress 通信的策略为
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
而默认允许所有 Pod 之间 Ingress 通信的策略为
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
ingress:
- {}
默认允许所有 Pod 之间 Egress 通信的策略为
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
egress:
- {}
而 v1.6 版本则通过 Annotation 来隔离 namespace 的所有 Pod 之间的流量,包括从外部到该 namespace 中所有 Pod 的流量以及 namespace 内部 Pod 相互之间的流量:
kubectl annotate ns <namespace> "net.beta.kubernetes.io/network-policy={\"ingress\": {\"isolation\": \"DefaultDeny\"}}"

通过使用标签选择器(包括 namespaceSelector 和 podSelector)来控制 Pod 之间的流量。比如下面的 Network Policy
  • 允许 default namespace 中带有 role=frontend 标签的 Pod 访问 default namespace 中带有 role=db 标签 Pod 的 6379 端口
  • 允许带有 project=myprojects 标签的 namespace 中所有 Pod 访问 default namespace 中带有 role=db 标签 Pod 的 6379 端口
# v1.6 以及更老的版本应该使用 extensions/v1beta1
# apiVersion: extensions/v1beta1
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
ingress:
- from:
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: tcp
port: 6379
另外一个同时开启 Ingress 和 Egress 通信的策略为
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:
  • 允许 default namespace 中带有 role=frontend 标签的 Pod 访问 default namespace 中带有 role=db 标签 Pod 的 6379 端口
  • 允许带有 project=myprojects 标签的 namespace 中所有 Pod 访问 default namespace 中带有 role=db 标签 Pod 的 6379 端口
  • 允许 default namespace 中带有 role=db 标签的 Pod 访问 10.0.0.0/24 网段的 TCP 5987 端口

以 calico 为例看一下 Network Policy 的具体用法。
首先配置 kubelet 使用 CNI 网络插件
kubelet --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin ...
安装 calio 网络插件
# 注意修改 CIDR,需要跟 k8s pod-network-cidr 一致,默认为 192.168.0.0/16
kubectl apply -f https://docs.projectcalico.org/v3.0/getting-started/kubernetes/installation/hosted/kubeadm/1.7/calico.yaml
首先部署一个 nginx 服务
$ kubectl run nginx --image=nginx --replicas=2
deployment "nginx" created
$ kubectl expose deployment nginx --port=80
service "nginx" exposed
此时,通过其他 Pod 是可以访问 nginx 服务的
$ kubectl get svc,pod
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/kubernetes 10.100.0.1 <none> 443/TCP 46m
svc/nginx 10.100.0.16 <none> 80/TCP 33s
NAME READY STATUS RESTARTS AGE
po/nginx-701339712-e0qfq 1/1 Running 0 35s
po/nginx-701339712-o00ef 1/1 Running 0
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
Hit enter for command prompt
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
/ #
开启 default namespace 的 DefaultDeny Network Policy 后,其他 Pod(包括 namespace 外部)不能访问 nginx 了:
$ cat default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
$ kubectl create -f default-deny.yaml
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
Hit enter for command prompt
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out
/ #
最后再创建一个运行带有 access=true 的 Pod 访问的网络策略
$ cat 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"
$ kubectl create -f nginx-policy.yaml
networkpolicy "access-nginx" created
# 不带 access=true 标签的 Pod 还是无法访问 nginx 服务
$ kubectl run busybox --rm -ti --image=busybox /bin/sh
Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
Hit enter for command prompt
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out
/ #
# 而带有 access=true 标签的 Pod 可以访问 nginx 服务
$ kubectl run busybox --rm -ti --labels="access=true" --image=busybox /bin/sh
Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
Hit enter for command prompt
/ # wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
/ #
最后开启 nginx 服务的外部访问:
$ cat nginx-external-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: front-end-access
namespace: sock-shop
spec:
podSelector:
matchLabels:
run: nginx
ingress:
- ports:
- protocol: TCP
port: 80
$ kubectl create -f nginx-external-policy.yaml

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

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

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: default
spec:
podSelector: {}

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: {}

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

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: []

  • 强制集群内部流量经过某公用网关(这种场景最好通过服务网格或其他代理来实现);
  • 与 TLS 相关的场景(考虑使用服务网格或者 Ingress 控制器);
  • 特定于节点的策略(你可以使用 CIDR 来表达这一需求不过你无法使用节点在 Kubernetes 中的其他标识信息来辩识目标节点);
  • 基于名字来选择服务(不过,你可以使用 标签 来选择目标 Pod 或名字空间,这也通常是一种可靠的替代方案);
  • 创建或管理由第三方来实际完成的“策略请求”;
  • 实现适用于所有名字空间或 Pods 的默认策略(某些第三方 Kubernetes 发行版本 或项目可以做到这点);
  • 高级的策略查询或者可达性相关工具;
  • 生成网络安全事件日志的能力(例如,被阻塞或接收的连接请求);
  • 显式地拒绝策略的能力(目前,NetworkPolicy 的模型默认采用拒绝操作, 其唯一的能力是添加允许策略);
  • 禁止本地回路或指向宿主的网络流量(Pod 目前无法阻塞 localhost 访问, 它们也无法禁止来自所在节点的访问请求)。

最近更新 1yr ago
复制链接
大纲
API 版本对照表
网络策略
Namespace 隔离
Pod 隔离
简单示例
使用场景
禁止访问指定服务
只允许指定 Pod 访问服务
禁止 namespace 中所有 Pod 之间的相互访问
禁止其他 namespace 访问服务
只允许指定 namespace 访问服务
允许外网访问服务
不支持场景
参考文档