Kubernetes的service mesh – 第三部分:将一切加密

导言

在本文中,我们将讲述如何使用linkerd作为service mesh在不需要修改任何应用代码的情况下,也能添加TLS至所有的“服务到服务”HTTP请求中。

注意:这是关于Linkerd、Kubernetes和service mesh的系列文章其中一篇,其余部分包括:

  1. Top-line service metrics
  2. Pods are great, until they’re not
  3. Encrypting all the things (本文)
  4. Continuous deployment via traffic shifting
  5. Dogfood environments, ingress, and edge routing
  6. Staging microservices without the tears
  7. Distributed tracing made easy
  8. Linkerd as an ingress controller
  9. gRPC for fun and profit
  10. The Service Mesh API
  11. Egress
  12. Retry budgets, deadline propagation, and failing gracefully
  13. Autoscaling by top-line metrics

在该系列的第一部分,我讲述了当linkerd被作为service mesh安装后如何监控顶层指标(成功率、延迟、请求率)。本文中,我将讲述service mesh的另一优势:它允许你将应用程序的协议与网络上使用的协议分离。换句话说,应用可以使用与在电缆上传输字节不同的协议。

在不需要数据转换的情况下,linkerd可以利用这个解耦功能进行自动的协议升级。linkerd可以做协议升级的例子包括HTTP/1.x至HTTP/2,thrift至thrift-mux,还有HTTP至HTTPS,这也是本文的主题。

Kubernetes的service mesh

当linkerd在Kubernetes中作为service mesh部署后,我们使用DaemonSet在每个宿主机上部署一个linkerd实例。对于HTTP服务而言,pod可以通过使用http_proxy环境变量发送HTTP流量到它们本地的linkerd。(非HTTP流量情况下会稍微复杂些)

我们之前曾讲过在TLS中通过在连接的两端代理始发TLS和终止TLS,来使用linkerd“打包”HTTP请求的基本模式。然而,现在我们有了service mesh,问题显然变得简单了。将全部的跨宿主机通信加密是service mesh提供TLS证书的关键。

接下来举具体例子来说。接下来的例子的前两步与本系列PRAT 1相同,安装linkerd并安装简单的微服务“hello world”应用程序。如果你已经做过这些了,可以直接跳到第三步。

第一步:安装Linkerd

使用Kubernetes配置文件安装Linkerd。它会将Linkerd作为DaemonSet安装,且运行在Kubernetes的default命名空间:

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd.yml

你可以通过查看Linkerd的管理页面确认是否安装成功:

INGRESS_LB=$(kubectl get svc l5d -o jsonpath="{.status.loadBalancer.ingress[0].*}")
open http://$INGRESS_LB:9990 # on OS X

如果集群不支持外部负载均衡,使用hostIP:

HOST_IP=$(kubectl get po -l app=l5d -o jsonpath="{.items[0].status.hostIP}")
open http://$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[2].nodePort}') # on OS X

第二步:安装样例应用程序

在default命名空间下安装两个Service,“hello”和“world”。这些应用程序依赖于Kubernetes downward API提供的nodeName来发现Linkerd。为了检测你的集群是否支持nodeName,你可以运行:

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/node-name-test.yml

然后看它的日志:

kubectl logs node-name-test

如果你看到了一个IP就说明支持。接下来继续部署hello world应用程序如下:

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world.yml

如果你看到报错“server can’t find…”,就部署旧版本hello-world,它依赖于hostIP而非nodeName:

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world-legacy.yml

这两个Service——“hello”和“world”——功能在一起使得高度可扩展,“hello world”微服务(“hello”Service调用“world”Service完成这个请求)。

你可以通过给Linkerd的外部IP发送请求来查看此操作:

http_proxy=$INGRESS_LB:4140 curl -s http://hello

或者直接使用hostIP:

http_proxy=$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}') curl -s http://hello

你应该可以看到字符串“Hello world”。

第三步:为使用TLS配置linkerd

现在linkerd已经安装好了,我们可以使用它来给流量加密了。我们在每一个宿主机上安装TLS证书,并为使用这些证书而配置linkerd。

我们将使用一个我们自己生成的全局证书。因为这个证书没有绑定公共DNS域名,我们就不需要使用Let’s Encrypt这样的服务了。我们可以生成自己的CA证书,并使用它来签署我们的mesh证书(“自签名”)。我们将为每个Kubernetes节点分配三样东西:CA证书,mesh密钥和mesh证书。

接下来的这个脚本使用我们自己生成的样本证书。请不要在生产环境中使用这些证书。

第四步:为Kubernetes部署证书并配置变更

我们已经做好准备为给流量加密而更新linkerd。我们将如Kubernetes secrets对象那样分配样例证书。

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/certificates.yml

现在,我们将通过这个配置文件配置linkerd并重启它以使用这些证书:

kubectl delete ds/l5d configmap/l5d-config
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd-tls.yml

第五步:成功

此时,linkerd应该透明地包裹TLS中的这些服务之间的所有通信。我们通过运行与之前相同的命令来验证这一点:

http_proxy=$INGRESS_LB:4140 curl -s http://hello

或者使用hostIP:

http_proxy=$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}') curl -s http://hello

如果一切顺利进行,你应该仍然可以看到字符串“Hello world”。但是实际上,hello和world服务之间的通信正在被加密。我们可以通过直接向端口4141发送HTTPS请求来进行验证,其中,linkerd正在监听来自其他linkerd实例的请求:

curl -skH 'l5d-dtab: /svc=>/#/io.l5d.k8s/default/admin/l5d;' https://$INGRESS_LB:4141/admin/ping

或者使用hostIP:

curl -skH 'l5d-dtab: /svc=>/#/io.l5d.k8s/default/admin/l5d;'
https://$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[1].nodePort}')/admin/ping

这里,我们要求用curl进行HTTPS调用,并告诉它跳过TLS验证(因为curl期望一个站点,而不是linkerd)。我们还添加了一个dtab override来将请求路由到linkerd实例自己的管理界面。如果一切顺利,你应该再次看到一个成功的“pong”响应。恭喜! 你已加密了跨服务流量。

结束语

在这篇文章中,我们已经讲述了如何使用诸如linkerd的service mesh来透明地加密Kubernetes集群中的所有跨节点通信。我们还使用TLS来确保linkerd实例可以验证它们是否与其他linkerd实例进行通信,从而防止中间人攻击(和配置错误)。当然,这个应用程序仍然不知道任何这些变化。

TLS是一个复杂的主题,我们已经考虑了一些重要的安全注意事项以使演示变得简单些。在你于生产集群上尝试此过程之前,请确保你已经花时间充分了解了相关步骤。

最后,将TLS添加到通信中只是service mesh可以实现的许多事情之一。一定要查看本系列的其余文章以了解更多。

本文译者:平凡,欢迎关注微信公众号