双向 TLS 迁移

本任务阐述如何将 Istio 服务的请求从明文模式平滑过渡至双向 TLS 模式,并确保在整个迁移过程中不干扰在线流量的正常通信。

在调用其他工作负载时,Istio 会自动配置工作负载 sidecar 以使用双向 TLS。 默认情况下,Istio 使用 PERMISSIVE 模式配置目标工作负载。 当启用 PERMISSIVE 模式时,服务可以接受明文和双向 TLS 流量。 为了只允许双向 TLS 流量,需要将配置更改为 STRICT 模式。

您可以使用 Grafana dashboard 检查哪些服务仍然向 PERMISSIVE 模式的服务发送明文请求,然后选择在这些服务迁移结束后,将其锁定为只接收双向 TLS 请求。

开始之前

在此任务中,您可以通过创建示例工作负载并修改策略以在工作负载之间强制执行 STRICT 双向 TLS 来尝试迁移过程。

设置集群

  • 创建两个命名空间:foobar,并在这两个命名空间上部署 httpbinsleep 和 Sidecar。

    ZipZipZipZip
    $ 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
    
  • 创建另一个命名空间 legacy,并在没有 Sidecar 的情况下部署 sleep

    Zip
    $ kubectl create ns legacy
    $ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy
    
  • (使用 curl 命令)从每个 Sleep Pod (命名空间为 foobarlegacy)分别向 httpbin.foo 发送 http 请求。所有请求都应成功响应,返回 HTTP code 200。

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

通过命名空间锁定到双向 TLS

当所有客户端服务都成功迁移至 Istio 之后,注入 Envoy sidecar,便可以锁定 httpbin.foo 只接收双向 TLS 请求。

$ kubectl apply -n foo -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: "default"
spec:
  mtls:
    mode: STRICT
EOF

此时,源自 sleep.legacy 的请求将响应失败。

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

如果您安装 Istio 时带有参数 values.global.proxy.privileged=true,那么您可以使用 tcpdump 来验证流量是否被加密。

$ kubectl exec -nfoo "$(kubectl get pod -nfoo -lapp=httpbin -ojsonpath={.items..metadata.name})" -c istio-proxy -- sudo tcpdump dst port 80  -A
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

当分别从 sleep.legacysleep.foo 发送请求时,您将在输出中看到纯文本和加密文本。

若无法将所有服务迁移至 Istio (注入 Envoy sidecar),则必须开启 PERMISSIVE 模式。 然而,开启 PERMISSIVE 模式时,系统默认不对明文请求进行认证或授权检查。 推荐使用 Istio 授权来为不同的请求路径配置不同的授权策略。

锁定整个网格的 mTLS

$ kubectl apply -n istio-system -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: "default"
spec:
  mtls:
    mode: STRICT
EOF

现在,foobar 命名空间都强制执行仅双向 TLS 流量,因此您应该会看到来自 sleep.legacy 的请求访问两个命名空间的服务都失败了。

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

清除

  1. 删除网格范围的身份验证策略。

    $ kubectl delete peerauthentication -n foo default
    $ kubectl delete peerauthentication -n istio-system default
    
  2. 删除用于测试的命名空间。

    $ kubectl delete ns foo bar legacy
    Namespaces foo bar legacy deleted.
    
这些信息有用吗?
Do you have any suggestions for improvement?

Thanks for your feedback!