随着微服务的流行,越来越多的云服务平台需要大量模块之间的网络调用。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 的支持
  • 网络插件要支持 Network Policy,如 Calico、Romana、Weave Net 和 trireme 等,参考 这里

API 版本对照表

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

网络策略

Namespace 隔离

默认情况下,所有 Pod 之间是全通的。每个 Namespace 可以配置独立的网络策略,来隔离 Pod 之间的流量。

v1.7 + 版本通过创建匹配所有 Pod 的 Network Policy 来作为默认的网络策略,比如默认拒绝所有 Pod 之间 Ingress 通信

  1. apiVersion: networking.k8s.io/v1
  2. kind: NetworkPolicy
  3. metadata:
  4. name: default-deny
  5. spec:
  6. podSelector: {}
  7. policyTypes:
  8. - Ingress

默认拒绝所有 Pod 之间 Egress 通信的策略为

  1. apiVersion: networking.k8s.io/v1
  2. kind: NetworkPolicy
  3. metadata:
  4. name: default-deny
  5. spec:
  6. podSelector: {}
  7. policyTypes:
  8. - Egress

甚至是默认拒绝所有 Pod 之间 Ingress 和 Egress 通信的策略为

  1. apiVersion: networking.k8s.io/v1
  2. kind: NetworkPolicy
  3. metadata:
  4. name: default-deny
  5. spec:
  6. podSelector: {}
  7. policyTypes:
  8. - Ingress
  9. - Egress

而默认允许所有 Pod 之间 Ingress 通信的策略为

  1. apiVersion: networking.k8s.io/v1
  2. kind: NetworkPolicy
  3. metadata:
  4. name: allow-all
  5. spec:
  6. podSelector: {}
  7. ingress:
  8. - {}

默认允许所有 Pod 之间 Egress 通信的策略为

  1. apiVersion: networking.k8s.io/v1
  2. kind: NetworkPolicy
  3. metadata:
  4. name: allow-all
  5. spec:
  6. podSelector: {}
  7. egress:
  8. - {}

而 v1.6 版本则通过 Annotation 来隔离 namespace 的所有 Pod 之间的流量,包括从外部到该 namespace 中所有 Pod 的流量以及 namespace 内部 Pod 相互之间的流量:

  1. kubectl annotate ns <namespace> "net.beta.kubernetes.io/network-policy={\"ingress\": {\"isolation\": \"DefaultDeny\"}}"

Pod 隔离

通过使用标签选择器(包括 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 端口
  1. # v1.6 以及更老的版本应该使用 extensions/v1beta1
  2. # apiVersion: extensions/v1beta1
  3. apiVersion: networking.k8s.io/v1
  4. kind: NetworkPolicy
  5. metadata:
  6. name: test-network-policy
  7. namespace: default
  8. spec:
  9. podSelector:
  10. matchLabels:
  11. role: db
  12. ingress:
  13. - from:
  14. - namespaceSelector:
  15. matchLabels:
  16. project: myproject
  17. - podSelector:
  18. matchLabels:
  19. role: frontend
  20. ports:
  21. - protocol: tcp
  22. port: 6379

另外一个同时开启 Ingress 和 Egress 通信的策略为

  1. apiVersion: networking.k8s.io/v1
  2. kind: NetworkPolicy
  3. metadata:
  4. name: test-network-policy
  5. namespace: default
  6. spec:
  7. podSelector:
  8. matchLabels:
  9. role: db
  10. policyTypes:
  11. - Ingress
  12. - Egress
  13. ingress:
  14. - from:
  15. - ipBlock:
  16. cidr: 172.17.0.0/16
  17. except:
  18. - 172.17.1.0/24
  19. - namespaceSelector:
  20. matchLabels:
  21. project: myproject
  22. - podSelector:
  23. matchLabels:
  24. role: frontend
  25. ports:
  26. - protocol: TCP
  27. port: 6379
  28. egress:
  29. - to:
  30. - ipBlock:
  31. cidr: 10.0.0.0/24
  32. ports:
  33. - protocol: TCP
  34. 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 网络插件

  1. kubelet --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin ...

安装 calio 网络插件

  1. # 注意修改 CIDR,需要跟 k8s pod-network-cidr 一致,默认为 192.168.0.0/16
  2. kubectl apply -f https://docs.projectcalico.org/v3.0/getting-started/kubernetes/installation/hosted/kubeadm/1.7/calico.yaml

首先部署一个 nginx 服务

  1. $ kubectl run nginx --image=nginx --replicas=2
  2. deployment "nginx" created
  3. $ kubectl expose deployment nginx --port=80
  4. service "nginx" exposed

此时,通过其他 Pod 是可以访问 nginx 服务的

  1. $ kubectl get svc,pod
  2. NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. svc/kubernetes 10.100.0.1 <none> 443/TCP 46m
  4. svc/nginx 10.100.0.16 <none> 80/TCP 33s
  5. NAME READY STATUS RESTARTS AGE
  6. po/nginx-701339712-e0qfq 1/1 Running 0 35s
  7. po/nginx-701339712-o00ef 1/1 Running 0
  8. $ kubectl run busybox --rm -ti --image=busybox /bin/sh
  9. Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
  10. Hit enter for command prompt
  11. / # wget --spider --timeout=1 nginx
  12. Connecting to nginx (10.100.0.16:80)
  13. / #

开启 default namespace 的 DefaultDeny Network Policy 后,其他 Pod(包括 namespace 外部)不能访问 nginx 了:

  1. $ cat default-deny.yaml
  2. apiVersion: networking.k8s.io/v1
  3. kind: NetworkPolicy
  4. metadata:
  5. name: default-deny
  6. spec:
  7. podSelector: {}
  8. policyTypes:
  9. - Ingress
  10. $ kubectl create -f default-deny.yaml
  11. $ kubectl run busybox --rm -ti --image=busybox /bin/sh
  12. Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
  13. Hit enter for command prompt
  14. / # wget --spider --timeout=1 nginx
  15. Connecting to nginx (10.100.0.16:80)
  16. wget: download timed out
  17. / #

最后再创建一个运行带有 access=true 的 Pod 访问的网络策略

  1. $ cat nginx-policy.yaml
  2. kind: NetworkPolicy
  3. apiVersion: networking.k8s.io/v1
  4. metadata:
  5. name: access-nginx
  6. spec:
  7. podSelector:
  8. matchLabels:
  9. run: nginx
  10. ingress:
  11. - from:
  12. - podSelector:
  13. matchLabels:
  14. access: "true"
  15. $ kubectl create -f nginx-policy.yaml
  16. networkpolicy "access-nginx" created
  17. # 不带 access=true 标签的 Pod 还是无法访问 nginx 服务
  18. $ kubectl run busybox --rm -ti --image=busybox /bin/sh
  19. Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
  20. Hit enter for command prompt
  21. / # wget --spider --timeout=1 nginx
  22. Connecting to nginx (10.100.0.16:80)
  23. wget: download timed out
  24. / #
  25. # 而带有 access=true 标签的 Pod 可以访问 nginx 服务
  26. $ kubectl run busybox --rm -ti --labels="access=true" --image=busybox /bin/sh
  27. Waiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: false
  28. Hit enter for command prompt
  29. / # wget --spider --timeout=1 nginx
  30. Connecting to nginx (10.100.0.16:80)
  31. / #

最后开启 nginx 服务的外部访问:

  1. $ cat nginx-external-policy.yaml
  2. apiVersion: networking.k8s.io/v1
  3. kind: NetworkPolicy
  4. metadata:
  5. name: front-end-access
  6. namespace: sock-shop
  7. spec:
  8. podSelector:
  9. matchLabels:
  10. run: nginx
  11. ingress:
  12. - ports:
  13. - protocol: TCP
  14. port: 80
  15. $ kubectl create -f nginx-external-policy.yaml

使用场景

禁止访问指定服务

  1. kubectl run web --image=nginx --labels app=web,env=prod --expose --port 80

NetworkPolicy - 图1

网络策略

  1. kind: NetworkPolicy
  2. apiVersion: networking.k8s.io/v1
  3. metadata:
  4. name: web-deny-all
  5. spec:
  6. podSelector:
  7. matchLabels:
  8. app: web
  9. env: prod

只允许指定 Pod 访问服务

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

NetworkPolicy - 图2

网络策略

  1. kind: NetworkPolicy
  2. apiVersion: networking.k8s.io/v1
  3. metadata:
  4. name: api-allow
  5. spec:
  6. podSelector:
  7. matchLabels:
  8. app: bookstore
  9. role: api
  10. ingress:
  11. - from:
  12. - podSelector:
  13. matchLabels:
  14. app: bookstore

禁止 namespace 中所有 Pod 之间的相互访问

NetworkPolicy - 图3

  1. apiVersion: networking.k8s.io/v1
  2. kind: NetworkPolicy
  3. metadata:
  4. name: default-deny
  5. namespace: default
  6. spec:
  7. podSelector: {}

禁止其他 namespace 访问服务

  1. kubectl create namespace secondary
  2. kubectl run web --namespace secondary --image=nginx \
  3. --labels=app=web --expose --port 80

NetworkPolicy - 图4

网络策略

  1. kind: NetworkPolicy
  2. apiVersion: networking.k8s.io/v1
  3. metadata:
  4. namespace: secondary
  5. name: web-deny-other-namespaces
  6. spec:
  7. podSelector:
  8. matchLabels:
  9. ingress:
  10. - from:
  11. - podSelector: {}

只允许指定 namespace 访问服务

  1. kubectl run web --image=nginx \
  2. --labels=app=web --expose --port 80

NetworkPolicy - 图5

网络策略

  1. kind: NetworkPolicy
  2. apiVersion: networking.k8s.io/v1
  3. metadata:
  4. name: web-allow-prod
  5. spec:
  6. podSelector:
  7. matchLabels:
  8. app: web
  9. ingress:
  10. - from:
  11. - namespaceSelector:
  12. matchLabels:
  13. purpose: production

允许外网访问服务

  1. kubectl run web --image=nginx --labels=app=web --port 80
  2. kubectl expose deployment/web --type=LoadBalancer

NetworkPolicy - 图6

网络策略

  1. kind: NetworkPolicy
  2. apiVersion: networking.k8s.io/v1
  3. metadata:
  4. name: web-allow-external
  5. spec:
  6. podSelector:
  7. matchLabels:
  8. app: web
  9. ingress:
  10. - ports:
  11. - port: 80
  12. from: []

参考文档