双向 TLS 的迁移

本文任务展示了如何在不中断通信的情况下,把现存 Istio 服务的流量从明文升级为双向 TLS

在实际情况中,集群中可能包含 Istio 服务(注入了 Envoy sidecar)以及非 Istio 服务(没有注入 Envoy sidecar 的服务,下文简称为存量服务)。存量服务无法使用 Istio 签发的密钥/证书来进行双向 TLS 通信。我们希望安全的、渐进的启用双向 TLS。

开始之前

  • 理解 Istio 认证策略以及相关的双向 TLS 认证概念。

  • 已成功在 Kubernetes 集群中部署 Istio,并且没有启用双向 TLS 支持(也就是使用安装步骤中所说的 install/kubernetes/istio-demo.yaml 进行部署,或者在 Helm 安装时设置 global.mtls.enabled 的值为 false)。

  • 为了演示目的,创建三个命名空间,分别是 foobar 以及 legacy,然后在 foobar 中分别部署注入 Istio sidecar 的 httpbin 以及 sleep 应用,最后在 legacy 命名空间中运行未经注入的 sleep 应用。

ZipZipZipZipZip

  1. $ kubectl create ns foo
  2. $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
  3. $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n foo
  4. $ kubectl create ns bar
  5. $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n bar
  6. $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n bar
  7. $ kubectl create ns legacy
  8. $ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy
  • 检查部署情况:从任意一个命名空间选一个 sleep pod,发送 http 请求到 httpbin.foo。所有的请求都应该能返回 HTTP 200。
  1. $ for from in "foo" "bar" "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.foo: %{http_code}\n"; done
  2. sleep.foo to httpbin.foo: 200
  3. sleep.bar to httpbin.foo: 200
  4. sleep.legacy to httpbin.foo: 200
  • 确认系统中不存在认证策略和目标规则:
  1. $ kubectl get policies.authentication.istio.io --all-namespaces
  2. No resources found.
  3. $ kubectl get destinationrule --all-namespaces
  4. No resources found.

配置服务器使其同时能接收双向 TLS 以及明文流量

在认证策略中有一个 PERMISSIVE 模式,这种模式让服务器能够同时接收明文和双向 TLS 流量。下面就把服务器设置为这种模式:

  1. $ cat <<EOF | istioctl create -n foo -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "example-httpbin-permissive"
  6. namespace: foo
  7. spec:
  8. targets:
  9. - name: httpbin
  10. peers:
  11. - mtls:
  12. mode: PERMISSIVE
  13. EOF

接下来再次发送流量到 httpbin.foo,确认所有请求依旧成功。

  1. $ for from in "foo" "bar" "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.foo: %{http_code}\n"; done
  2. 200
  3. 200
  4. 200

配置客户端进行双向 TLS 通信

利用设置 DestinationRule 的方式,让 Istio 服务进行双向 TLS 通信。

  1. $ cat <<EOF | istioctl create -n foo -f -
  2. apiVersion: "networking.istio.io/v1alpha3"
  3. kind: "DestinationRule"
  4. metadata:
  5. name: "example-httpbin-istio-client-mtls"
  6. spec:
  7. host: httpbin.foo.svc.cluster.local
  8. trafficPolicy:
  9. tls:
  10. mode: ISTIO_MUTUAL
  11. EOF

这样一来,sleep.foosleep.bar 就会开始使用双向 TLS 和 httpbin.foo 进行通信了。而 sleep.legacy 因为没有进行 sidecar 注入,因此不受 DestinationRule 配置影响,还是会使用明文和 httpbin.foo 通信。

现在复查一下,所有到 httpbin.foo 的通信是否依旧成功:

  1. $ for from in "foo" "bar" "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.foo: %{http_code}\n"; done
  2. 200
  3. 200
  4. 200

还可以在 DestinationRule 中指定一个客户端的子集所发出的请求来是用双向 TLS 通信,然后使用 Grafana 验证配置执行情况,确认通过之后,将策略的应用范围扩大到该服务的所有子集。

锁定使用双向 TLS (可选)

把所有进行过 sidecar 注入的客户端到服务器流量都迁移到双向 TLS 之后,就可以设置 httpbin.foo 只支持双向 TLS 流量了。

  1. $ cat <<EOF | istioctl create -n foo -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "example-httpbin-permissive"
  6. namespace: foo
  7. spec:
  8. targets:
  9. - name: httpbin
  10. peers:
  11. - mtls:
  12. mode: STRICT
  13. EOF

这样设置之后,sleep.legacy 的请求就会失败。

  1. $ for from in "foo" "bar" "legacy"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "sleep.${from} to httpbin.foo: %{http_code}\n"; done
  2. 200
  3. 200
  4. 503

也就是说,如果不能把所有服务都迁移到 Istio (进行 Sidecar 注入)的话,就只能使用 PERMISSIVE 模式了。然而在配置为 PERMISSIVE 的时候,是不会对明文流量进行授权和鉴权方面的检查的。我们推荐使用 RBAC 来给不同的路径配置不同的授权策略。

清理

移除所有资源

  1. $ kubectl delete ns foo bar legacy
  2. Namespaces foo bar legacy deleted.

相关内容

基础认证策略

介绍如何使用 Istio 认证策略设置双向 TLS 和基本的终端用户认证。

安全

描述 Istio 的授权与鉴权功能。

基于 Istio 的 Micro-Segmentation 授权

描述 Istio 的授权功能以及如何在各种用例中使用它。

Citadel 的健康检查

如何在 Kubernetes 中启用 Citadel 的健康检查。

HTTP 服务的访问控制

展示为 HTTP 服务设置基于角色的访问控制方法。

Istio Service 健康检查

展示如何对 Istio service 进行健康检查。