1. Kubernetes 中的网络隔离
Kuberntes 自 1.3 引入了 Network Policy(网络策略) ,通过 ipBlock、podSelector、namespaceSelector 定义实体,控制其 From(Ingress)、To(Egress)的流量行为。
但 Kubernetes 只是定义了网络策略,具体实现依赖网络插件。目前,Calico、Cilium、Weave Net 等网络插件都支持网络隔离功能。
不同的 Kubernetes 版本对网络隔离支持的程度不一样。1.3~1.6 需要在 kube-apiserver 中开启 extensions/v1beta1/networkpolicies
,1.7 之后可以直接使用,1.8 新增了 Egress 、IPBlock 支持。
如果没有配置任何网络策略,默认情况下流量行为将没有任何限制。
2. 网络隔离对象的字段属性
下面是官方文档中的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| 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
|
为了更直观地看到网络策略的组成,我画了一张示意图:
一个 NetworkPolicy 包含四个部分:
- 需要控制的 Pods
- 策略类型,Ingress、Egress 可选
- Ingress 策略,包括 Entity 和端口、协议定义
- Egress 策略,包括 Entity 和端口、协议定义
其中的 Entity 有三种类型可选,ipBlock 指定一个 IP 范围,namespaceSelector 指定匹配的命名空间,podSelector 指定匹配的 Pods 。
如果针对同一个实体,定义了多个 NetworkPolicy,那么命中其中一项即可通行。
3. 网络隔离示例
在 Ingress 和 Egress 的配置过程中会遇到两个特殊对象 []
,{}
。如果是 []
,那么表示不指向任何实体,通常用于禁用流量;而 {}
表示全部实体,通常用于放行流量。
另外一个值得注意的地方是,列表表示或的关系。
1
2
3
4
5
6
| egress:
- ports:
- port: 443
protocol: TCP
- to:
- namespaceSelector: {}
|
表示允许全部的 443 端口 TCP 流量,同时允许全部命名空间的任意端口流量。
1
2
3
4
5
6
| egress:
- ports:
- port: 443
protocol: TCP
to:
- namespaceSelector: {}
|
表示允许指向全部命名空间的 443 端口 TCP 流量。
3.1 拒绝访问 Pod 的全部流量
1
| kubectl run --generator=run-pod/v1 web --image=nginx --labels app=web --expose --port 80
|
验证默认情况下,Kubernetes 对流量没有进行限制。
1
2
3
4
5
6
7
| kubectl run --generator=run-pod/v1 --rm -i -t --image=alpine test-$RANDOM -- sh
/ # wget -qO- http://web
<!DOCTYPE html>
<html>
<head>
...
|
包含 app=web 标签的 Pods 将拒绝全部访问流量。Ingress 为 []
的含义是匹配的实体为空。
1
2
3
4
5
6
7
8
9
10
11
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-deny-all
spec:
podSelector:
matchLabels:
app: web
ingress: []
EOF
|
1
2
3
4
| kubectl run --generator=run-pod/v1 --rm -i -t --image=alpine test-$RANDOM -- sh
/ # wget -qO- --timeout=2 http://web
wget: download timed out
|
1
2
3
| kubectl delete pod web
kubectl delete service web
kubectl delete networkpolicy web-deny-all
|
3.2 仅允许指定的 Pods 访问应用
1
| kubectl run --generator=run-pod/v1 apiserver --image=nginx --labels app=bookstore,role=api --expose --port 80
|
仅允许包含标签 app=bookstore 的 Pods 访问应用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: api-allow
spec:
podSelector:
matchLabels:
app: bookstore
role: api
ingress:
- from:
- podSelector:
matchLabels:
app: bookstore
EOF
|
1
2
3
4
| kubectl run --generator=run-pod/v1 test-$RANDOM --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://apiserver
wget: download timed out
|
1
2
3
4
| kubectl run --generator=run-pod/v1 test-$RANDOM --rm -i -t --image=alpine --labels app=bookstore,role=frontend -- sh
/ # wget -qO- --timeout=2 http://apiserver
<!DOCTYPE html>
<html><head>
|
1
2
3
| kubectl delete pod apiserver
kubectl delete service apiserver
kubectl delete networkpolicy api-allow
|
3.3 允许访问 Pod 的全部流量
1
| kubectl run --generator=run-pod/v1 web --image=nginx --labels=app=web --expose --port 80
|
Ingress 设置为 {}
空的含义是允许全部来源。
1
2
3
4
5
6
7
8
9
10
11
12
13
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-all
namespace: default
spec:
podSelector:
matchLabels:
app: web
ingress:
- {}
EOF
|
1
2
3
4
5
| kubectl run --generator=run-pod/v1 test-$RANDOM --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web
<!DOCTYPE html>
<html><head>
|
1
2
| kubectl delete pod,service web
kubectl delete networkpolicy web-allow-all
|
3.4 拒绝其他命名空间的访问流量
1
| kubectl run --generator=run-pod/v1 web --namespace default --image=nginx --labels=app=web --expose --port 80
|
仅允许 default 命名空间内的 Pods 相互访问,不允许其他命名空间的 Pods 访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
namespace: default
name: deny-from-other-namespaces
spec:
podSelector:
matchLabels:
ingress:
- from:
- podSelector: {}
EOF
|
1
2
3
4
5
| kubectl create namespace foo
kubectl run --generator=run-pod/v1 test-$RANDOM --namespace=foo --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
|
1
2
3
4
5
| kubectl run --generator=run-pod/v1 test-$RANDOM --namespace=default --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
<!DOCTYPE html>
<html>
|
1
2
3
4
| kubectl delete pod web
kubectl delete service web
kubectl delete networkpolicy deny-from-other-namespaces
kubectl delete namespace foo
|
3.5 允许全部命名空间访问指定的 Pods
1
| kubectl run --generator=run-pod/v1 web --image=nginx --namespace default --labels=app=web --expose --port 80
|
仅允许 default 命名空间下,标签为 app=web 的 Pod 被访问。default 下的其他 Pod 不允许被访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
namespace: default
name: web-allow-all-namespaces
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector: {}
EOF
|
1
2
3
4
5
6
7
| kubectl create namespace secondary
kubectl run --generator=run-pod/v1 test-$RANDOM --namespace=secondary --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
<!DOCTYPE html>
<html>
<head>
|
1
2
3
4
| kubectl delete pod web -n default
kubectl delete service web -n default
kubectl delete networkpolicy web-allow-all-namespaces -n default
kubectl delete namespace secondary
|
3.6 允许指定命名空间访问指定的 Pods
1
| kubectl run --generator=run-pod/v1 web --image=nginx --labels=app=web --expose --port 80
|
1
2
3
4
| kubectl create namespace dev
kubectl label namespace/dev purpose=testing
kubectl create namespace prod
kubectl label namespace/prod purpose=production
|
仅允许命名空间包含标签 purpose=production 的 Pod 访问指定的应用,其他应用不允许访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-prod
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector:
matchLabels:
purpose: production
EOF
|
1
2
3
4
| kubectl run --generator=run-pod/v1 test-$RANDOM --namespace=dev --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
|
1
2
3
4
5
6
| kubectl run --generator=run-pod/v1 test-$RANDOM --namespace=prod --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
<!DOCTYPE html>
<html>
<head>
|
1
2
3
4
| kubectl delete networkpolicy web-allow-prod
kubectl delete pod web
kubectl delete service web
kubectl delete namespace {prod,dev}
|
3.7 仅允许指定空间中的指定 Pod 访问应用
此功能需要 Kubernetes 1.11 及以上版本。
1
| kubectl run --generator=run-pod/v1 web --image=nginx --labels=app=web --expose --port 80
|
1
2
| kubectl create namespace other
kubectl label namespace/other team=operations
|
命名空间和 Pods 需要同时满足要求时,才可以访问指定的应用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-allow-all-ns-monitoring
namespace: default
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector: # chooses all pods in namespaces labelled with team=operations
matchLabels:
team: operations
podSelector: # chooses pods with type=monitoring
matchLabels:
type: monitoring
EOF
|
1
2
3
4
| kubectl run --generator=run-pod/v1 test-$RANDOM --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
|
1
2
3
4
| kubectl run --generator=run-pod/v1 test-$RANDOM --labels type=monitoring --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
|
1
2
3
4
| kubectl run --generator=run-pod/v1 test-$RANDOM --namespace=other --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
wget: download timed out
|
1
2
3
4
5
6
| kubectl run --generator=run-pod/v1 test-$RANDOM --namespace=other --labels type=monitoring --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://web.default
<!DOCTYPE html>
<html>
<head>
|
1
2
3
4
| kubectl delete networkpolicy web-allow-all-ns-monitoring
kubectl delete namespace other
kubectl delete pod web
kubectl delete service web
|
3.8 仅允许访问 Pods 的指定端口
1
| kubectl run --generator=run-pod/v1 apiserver --image=ahmet/app-on-two-ports --labels=app=apiserver
|
这个应用将在 8000 端口返回 Hello 响应,而在 5000 端口返回监控数据。下面将 Pod 暴露到 Service 上:
1
| kubectl create service clusterip apiserver --tcp 8001:8000 --tcp 5001:5000
|
仅允许包含标签 role=monitoring 的 Pod 访问应用的 5000 端口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: api-allow-5000
spec:
podSelector:
matchLabels:
app: apiserver
ingress:
- ports:
- port: 5000
from:
- podSelector:
matchLabels:
role: monitoring
EOF
|
1
2
3
4
5
6
7
| kubectl run --generator=run-pod/v1 test-$RANDOM --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://apiserver:8001
wget: download timed out
/ # wget -qO- --timeout=2 http://apiserver:5001/metrics
wget: download timed out
|
1
2
3
4
5
6
7
8
9
| kubectl run --generator=run-pod/v1 test-$RANDOM --labels=role=monitoring --rm -i -t --image=alpine -- sh
/ # wget -qO- --timeout=2 http://apiserver:8001
wget: download timed out
/ # wget -qO- --timeout=2 http://apiserver:5001/metrics
http.requests=1
go.goroutines=5
go.cpus=4
|
1
2
3
| kubectl delete pod apiserver
kubectl delete service apiserver
kubectl delete networkpolicy api-allow-5000
|
3.9 使用多个选择器指定访问来源
1
| kubectl run --generator=run-pod/v1 db --image=redis:4 --port 6379 --expose --labels app=bookstore,role=db
|
可以同时配置多个允许的来源,只需要其中一个匹配即可访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: redis-allow-services
spec:
podSelector:
matchLabels:
app: bookstore
role: db
ingress:
- from:
- podSelector:
matchLabels:
app: bookstore
role: search
- podSelector:
matchLabels:
app: bookstore
role: api
- podSelector:
matchLabels:
app: inventory
role: web
EOF
|
1
2
3
4
5
6
| kubectl run --generator=run-pod/v1 test-$RANDOM --labels=app=inventory,role=web --rm -i -t --image=alpine -- sh
/ # nc -v -w 2 db 6379
db (10.59.242.200:6379) open
(works)
|
1
2
3
4
5
6
| kubectl run --generator=run-pod/v1 test-$RANDOM --labels=app=other --rm -i -t --image=alpine -- sh
/ # nc -v -w 2 db 6379
nc: db (10.59.252.83:6379): Operation timed out
(traffic blocked)
|
1
2
3
| kubectl delete pod db
kubectl delete service db
kubectl delete networkpolicy redis-allow-services
|
3.10 禁止 Pod 的出口流量
1
| kubectl run --generator=run-pod/v1 web --image=nginx --port 80 --expose --labels app=web
|
禁止包含标签 app=foo 的 Pod 任何出口流量。
1
2
3
4
5
6
7
8
9
10
11
12
13
| cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: foo-deny-egress
spec:
podSelector:
matchLabels:
app: foo
policyTypes:
- Egress
egress: []
EOF
|
1
2
3
4
5
6
7
| kubectl run --generator=run-pod/v1 --rm --restart=Never --image=alpine -i -t -l app=foo test -- ash
/ # wget -qO- --timeout 1 http://web:80/
wget: bad address 'web:80'
/ # wget -qO- --timeout 1 http://www.example.com/
wget: bad address 'www.example.com'
|
1
2
| kubectl delete pod,service web
kubectl delete networkpolicy foo-deny-egress
|
3.11 拒绝外部出口流量
1
| kubectl run --generator=run-pod/v1 web --image=nginx --port 80 --expose --labels app=web
|
允许包含标签 app=foo 的 Pods 访问全部 53 端口的服务,同时允许访问全部命名空间的应用。这里的 ports 和 to 是或的关系。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: foo-deny-external-egress
spec:
podSelector:
matchLabels:
app: foo
policyTypes:
- Egress
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
- to:
- namespaceSelector: {}
EOF
|
1
2
3
4
5
6
7
8
9
10
| kubectl run --generator=run-pod/v1 --rm --restart=Never --image=alpine -i -t -l app=foo test -- ash
/ # wget -O- --timeout 1 http://web:80
Connecting to web (10.59.245.232:80)
<!DOCTYPE html>
<html>
/ # wget -O- --timeout 1 http://www.example.com
Connecting to www.example.com (93.184.216.34:80)
wget: download timed out
|
1
2
| kubectl delete pod,service web
kubectl delete networkpolicy foo-deny-external-egress
|
4. 参考