双向 TLS 迁移
本任务阐述如何将 Istio 服务的请求从明文模式平滑过渡至双向 TLS 模式,并确保在整个迁移过程中不干扰在线流量的正常通信。
针对多服务跨网络通信的应用场景,通常希望逐步将所有服务迁移到 Istio 网格中。如此一来,在迁移过程中,将出现只有部分服务注入了 Envoy sidecar 的情况。对于一个已注入 sidecar 的服务而言,一旦开启服务的双向 TLS 通信模式,那么传统客户端(即:没有 Envoy 的客户端)将无法与之通信,因为这些客户端没有注入 Envoy sidecar 和客户端证书。为了解决这个问题,Istio 认证策略提供了一种 “PERMISSIVE” 模式。当启用 “PERMISSIVE” 模式时,服务可以同时接收 HTTP 和双向 TLS 请求。
这样,便可以将 Istio 服务的通信模式配置为双向 TLS,与此同时,不干扰其与传统服务之间的通信。此外,可以使用 Grafana dashboard 检查哪些服务仍然向 “PERMISSIVE” 模式的服务发送明文请求,然后选择在这些服务迁移结束后关闭目标服务的 “PERMISSIVE” 模式,将其锁定为只接收双向 TLS 请求。
开始之前
准备一个 Kubernetes 集群并部署好 Istio,不要开启全局双向 TLS (如:可以使用安装步骤中提供的 demo 配置 profile,或者将安装选项
global.mtls.enabled
设置为 false)。demo 准备
$ 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
(使用 curl 命令)从每个 sleep pod (命名空间为
foo
,bar
或legacy
)分别向httpbin.foo
发送 http 请求。所有请求都应成功响应,返回 HTTP code 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
NAMESPACE NAME AGE
istio-system grafana-ports-mtls-disabled 3m
$ kubectl get destinationrule --all-namespaces
NAMESPACE NAME HOST AGE
istio-system istio-multicluster-destinationrule *.global 35s
istio-system istio-policy istio-policy.istio-system.svc.cluster.local 35s
istio-system istio-telemetry istio-telemetry.istio-system.svc.cluster.local 33s
配置客户端发送双向 TLS 请求
设置 DestinationRule
,配置 Istio 服务发送双向 TLS 请求。
$ cat <<EOF | kubectl apply -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.foo
和 sleep.bar
开始向 httpbin.foo
发送双向 TLS 请求。因为 sleep.legacy
没有注入 sidecar,DestinationRule
不会对其起作用,所以 sleep.legacy
仍然向 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
sleep.foo to httpbin.foo: 200
sleep.bar to httpbin.foo: 200
sleep.legacy to httpbin.foo: 200
也可以指定一部分客户端使用 DestinationRule 中设置的 ISTIO_MUTUAL
双向 TLS 通信模式。 检查 Grafana to monitor 验证设置起效后,再扩大作用范围,最终应用到所有的 Istio 客户端服务。
锁定为双向 TLS
当所有客户端服务都成功迁移至 Istio 之后,注入 Envoy sidecar,便可以锁定 httpbin.foo
只接收双向 TLS 请求。
$ cat <<EOF | kubectl apply -n foo -f -
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 -it -- 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.legacy
和 sleep.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
现在,foo
和 bar
命名空间都强制执行仅双向 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
清除
清除所有资源。
$ kubectl delete ns foo bar legacy
Namespaces foo bar legacy deleted.