双向 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 应用。

    $ kubectl create ns foo
    $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
    $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n foo
    $ kubectl create ns bar
    $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n bar
    $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n bar
    $ kubectl create ns legacy
    $ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy
  • 检查部署情况:从任意一个命名空间选一个 sleep pod,发送 http 请求到 httpbin.foo。所有的请求都应该能返回 HTTP 200。

    $ 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
    sleep.foo to httpbin.foo: 200
    sleep.bar to httpbin.foo: 200
    sleep.legacy to httpbin.foo: 200
  • 确认系统中不存在认证策略和目标规则:

    $ kubectl get policies.authentication.istio.io --all-namespaces
    No resources found.
    $ kubectl get destionationrule --all-namespaces
    No resources found.

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

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

cat <<EOF | istioctl create -n foo -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
  name: "example-httpbin-permissive"
  namespace: foo
spec:
  targets:
  - name: httpbin
    peers:
  - mtls:
      mode: PERMISSIVE
EOF

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

$ 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
200
200
200

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

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

cat <<EOF | istioctl create -n foo -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
  name: "example-httpbin-istio-client-mtls"
spec:
  host: httpbin.foo.svc.cluster.local
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
EOF

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

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

$ 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
200
200
200

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

锁定使用双向 TLS (可选)

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

cat <<EOF | istioctl create -n foo -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
  name: "example-httpbin-permissive"
  namespace: foo
spec:
  targets:
  - name: httpbin
    peers:
  - mtls:
      mode: STRICT
EOF

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

$ 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
200
200
503

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

清理

移除所有资源

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

See also

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

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

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

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

展示如何在服务网格中进行基于角色的访问控制。

运维人员如何使用现有根证书配置 Citadel 进行证书以及密钥的签发。