测试双向 TLS

通过本任务,将学习如何:

  • 验证 Istio 双向 TLS 认证配置
  • 手动对认证功能进行测试

开始之前

本任务假设已有一个 Kubernetes 集群:

  • 安装启用全局双向 TLS 认证功能的 Istio:

    $ kubectl apply -f install/kubernetes/istio-demo-auth.yaml

    或者

    使用 Helm 进行安装,设置 global.mtls.enabledtrue.

从 Istio 0.7 开始,可以使用认证策略来给命名空间中全部/部分服务配置双向 TLS 功能。(在所有命名空间中重复此操作,就相当于全局配置了)。这部分内容可参考认证策略任务

  • 接下来进行演示应用的部署,首先是注入 Envoy sidecar 的 httpbin 以及 sleep。为简单起见,我们将演示应用安装到 default 命名空间。如果想要部署到其他命名空间,可以在下一节的示例命令中加入 -n yournamespace

    如果使用的是手工 Sidecar 注入,可使用如下命令:

    $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@)
    $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@)

    如果集群设置了自动注入 Sidecar,就只需要简单的使用 kubectl 就可以完成部署了。

    $ kubectl apply -f @samples/httpbin/httpbin.yaml@
    $ kubectl apply -f @samples/sleep/sleep.yaml@

检查 Istio 双向 TLS 认证的配置

检查 Citadel

检查集群内是否运行了 Citadel:

$ kubectl get deploy -l istio=citadel -n istio-system
NAME            DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
istio-citadel   1         1         1            1           1m

如果 “AVAILABLE” 列值为 1,则说明 Citadel 已经成功运行。

检查服务配置

  • 检查安装模式。如果缺省启用了双向 TLS(也就是在安装 Istio 的时候使用了 istio-demo-auth.yaml),会在 Configmap 中看到未被注释的 authPolicy: MUTUAL_TLS 一行:

    $ kubectl get configmap istio -o yaml -n istio-system | grep authPolicy | head -1
  • 检查认证策略。双向 TLS 的策略还能够以服务为单位进行启用(或停用)。如果存在仅对部分服务生效的策略,那么这部分服务原有的来自 Configmap 的策略就会被覆盖。不幸的是,目前没有快速的方法能够方便的获取某个服务的对应策略,只能在命名空间内获取所有策略。

    $ kubectl get policies.authentication.istio.io -n default -o yaml
  • 检查目标规则。从 Istio 0.8 开始,会使用目标规则的流量策略来对客户端进行配置,决定是否使用双向 TLS。为了向后兼容,缺省流量策略来自 Configmap 中的标志(也就是说,如果设置了 authPolicy: MUTUAL_TLS,那么缺省流量策略也会是 MUTUAL_TLS )。如果使用针对部分服务的认证策略覆盖了原有配置,那么就要通过目标规则来实现了。跟认证策略类似,验证这一设置的方法也是需要通过获取全部规则的方式来进行:

    $ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml

    注意目标规则的范围不仅限于单一命名空间,所以需要验证所有命名空间的规则。

校验密钥和证书的安装情况

为了完成双向 TLS 认证功能,Istio 会自动在所有 Sidecar 容器中安装必要的密钥和证书。

$ kubectl exec $(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.name}) -c istio-proxy -- ls /etc/certs
cert-chain.pem
key.pem
root-cert.pem

cert-chain.pem 是 Envoy 的证书,会在需要的时候提供给对端。而 key.pem 就是 Envoy 的私钥,和 cert-chain.pem 中的证书相匹配。root-cert.pem 是用于证书校验的根证书。这个例子中,我们集群中只部署了一个 Citadel,所以所有的 Envoy 共用同一个 root-cert.pem

是用 openssl 工具来检查证书的有效性(当前时间应该处于 Not BeforeNot After 之间)

$ kubectl exec $(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.name}) -c istio-proxy -- cat /etc/certs/cert-chain.pem | openssl x509 -text -noout  | grep Validity -A 2
Validity
        Not Before: May 17 23:02:11 2018 GMT
        Not After : Aug 15 23:02:11 2018 GMT

还可以查看一下客户端证书的 SAN(Subject Alternative Name)

$ kubectl exec $(kubectl get pod -l app=httpbin -o jsonpath={.items..metadata.name}) -c istio-proxy -- cat /etc/certs/cert-chain.pem | openssl x509 -text -noout  | grep 'Subject Alternative Name' -A 1
        X509v3 Subject Alternative Name:
            URI:spiffe://cluster.local/ns/default/sa/default

请参阅 Istio 认证 一节,可以了解更多服务认证方面的内容。

测试认证配置

假设双向 TLS 认证正确启用,在两个注入了 Envoy sidecar 的服务之间的通信应该不会受到影响。然而如果从没有注入 Sidecar 的 Pod 发起连接,或者直接从 Sidecar 发起没有指定客户端证书的连接,就无法访问服务了。下面的例子就演示了这种情况:

  1. sleep 容器访问 httpbin 服务应该可以成功(返回 200

    $ kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c sleep -- curl httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n'
    200
  2. 如果从 sleepproxy 容器中访问 httpbin 服务,就会导致失败

    $ kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n'
    000
    command terminated with exit code 56
    $ kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl https://httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n'
    000
    command terminated with exit code 77
  3. 接下来,如果请求中提供了客户端证书,那么这次请求就会成功

    $ kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl https://httpbin:8000/headers -o /dev/null -s -w '%{http_code}\n' --key /etc/certs/key.pem --cert /etc/certs/cert-chain.pem --cacert /etc/certs/root-cert.pem -k
    200

    Istio 使用 Kubernetes service accounts 作为服务的认证基础,Service account 提供了比服务名称更强的安全性(参考 Identity 获取更多信息)。Istio 中使用的证书不包含服务名,而 curl 需要用这个信息来检查服务认证。因此就需要给 curl 命令加上 -k 参数,在对服务器所出示的证书校验的时候,停止对服务器名称(例如 httpbin.ns.svc.cluster.local )的验证。

  4. 来自没有 Sidecar 的 Pod。可以重新部署另外一个 sleep 应用

    $ kubectl create ns legacy
    $ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy
  5. 等待 Pod 状态变为 Running,在其中发起类似的 curl 命令。由于这个 Pod 没有 Sidecar 协助完成 TLS 通信,因此这一请求会失败。

    $ kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n legacy) -c sleep -n legacy -- curl httpbin.default:8000/headers -o /dev/null -s -w '%{http_code}\n'
    000
    command terminated with exit code 56

清理

$ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@
$ kubectl delete --ignore-not-found=true -f @samples/sleep/sleep.yaml@
$ kubectl delete --ignore-not-found=true ns legacy

See also

展示如何在 HTTPS 服务上启用双向 TLS。

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

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

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

如何渐进式的为现有 Istio 服务添加双向 TLS 支持。

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