Please enable Javascript to view the contents

Kubernetes 之网络隔离(内附十多种使用场景)

 ·  ☕ 7 分钟

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
  • 运行一个临时的 Pod 测试网络

验证默认情况下,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. 参考


微信公众号
作者
微信公众号