Linkerd 中 mTLS 保护应用程序通信

简介: Linkerd 中 mTLS 保护应用程序通信

Linkerd 中 mTLS  保护应用程序通信

网络异常,图片无法展示
|

安全性是云原生应用程序的重中之重,虽然安全性是一个非常广泛的话题,但 Linkerd 依然可以发挥重要作用:其双向 TLS(mTLS)功能是为了在 Kubernetes 中实现零信任的安全方法。零信任安全是一种 IT 安全模型,要求试图访问专用网络上资源的每一个人和每一台设备(无论位于网络边界之内还是之外)都必须进行严格的身份验证。

网络异常,图片无法展示
|


什么是 mTLS

在云环境中越来越普遍的通信安全方法是零信任方法,虽然对零信任安全的全面处理超出了本节的范围,但核心目标是将应用程序的安全边界缩小到尽可能小的级别。例如,与其在数据中心周围设置防火墙,对进入的流量实施安全保护,留下"软内部"而不进一步验证,不如让数据中心的每个应用在自己的边界实施安全。这种零信任的方法自然适合云环境,因为云环境中的底层硬件和网络基础设施不在你的控制之下。

Linkerd 安全模型通过在服务之间提供透明的双向 TLS 通信来实现零信任安全,双向 TLS (mTLS) 是一种传输安全形式,可提供通信的机密性和身份验证。换句话说,不仅通信被加密,而且身份也在连接的两端进行验证(mTLS 的双向组件与浏览器使用的 TLS 不同,它只验证连接的服务器端,mTLS 验证客户端和服务器的身份)。Linkerd 的目标是通过使用 mTLS 对 Kubernetes Pod 之间的通信进行身份验证和加密,允许你对 Kubernetes 集群采用零信任方法。

验证是特别重要的,虽然大多数人认为 TLS 的价值在于加密,但验证连接两边实体的身份也同样重要。毕竟只有在你能相信与你通信的另一方的实体是他们所说的人的情况下,加密才是有用的--来自不良行为者的加密信息仍然是来自不良行为者的信息。

在 Linkerd 中,通过 mTLS 验证的身份与 Kubernetes ServiceAccounts 相关联。这意味着 Linkerd 的 mTLS 身份系统使用与 Kubernetes 用于为集群上的工作负载建立身份和访问控制的完全相同的模式,而不是发明一个新框架。

使用 Linkerd 的 mTLS

Linkerd 的设计原则之一是,复杂性是安全的敌人。配置东西越难,使用它的可能性就越小; 选项和设置越多,就越有可能不小心以不安全的方式进行配置。

默认情况下,Linkerd 为所有网格中的 pod-to-pod 通信启用了 mTLS,只要双方都注入了数据平面代理,那么恭喜你:你已经在服务之间验证了加密的 mTLS。事实上,前面我们使用的 Emojivoto 应用程序中就已经在使用 mTLS 了,只是我们没有意识到而已。

对对于 Linkerd 自动添加 mTLS 的功能,有几个需要注意的地方。

  • 两个端点都必须在网格中。Linkerd 需要同时处理客户端和服务器端的连接才能发挥其 mTLS 的魔力。
  • 在 Linkerd 2.8.1 和更早的版本中,Linkerd 只能为 HTTP 和 gRPC 流量添加 mTLS,即使如此,也无法针对某些类型的权限、主机或 Header 执行该操作,这些限制在 Linkerd 2.9 中已经被移除,它将 mTLS 添加到所有的 TCP 流量中,不管是什么协议。
  • 客户端发起 TLS 的连接不能由 Linkerd 进行 mTLS。相反,Linkerd 会将把这些连接视为 TCP 流量。请注意,这也意味着 Linkerd 只能为这些连接提供 TCP 级别的指标。

接下来让我们来了解下 mTLS 是如何工作的,以及如何验证我们的连接是否确实具有 mTLS。

Linkerd Identity 组件

在前面讲解 Linkerd 架构的时候我们就讨论过 Linkerd 控制平面的 Identity 组件,它作为 CA 或证书颁发机构所扮演的角色。证书颁发机构是颁发数字证书并使身份组件成为数字证书颁发者的实体。

Linkerd 使用的证书与网站用来验证其身份的 TLS 证书“类型”相同。与网站不同,这些证书不经过 Verisign 等第三方实体的验证,因为它们不需要验证,它们仅供 Linkerd 代理在集群内使用

Linkerd 的 CA(Identity 服务)作为 Linkerd 控制平面的一部分部署到集群中。在该部署过程中,Linkerd CLI 将生成一个证书并将其存储在 Linkerd 命名空间中名为 linkerd-identity-token-XXXXX 的 Kubernetes Secret 中。

$ kubectl get secret -n linkerdNAME                                 TYPE                                  DATA   AGElinkerd-identity-issuer              Opaque                                2      11dlinkerd-identity-token-nqhbk         kubernetes.io/service-account-token   3      11d# ......

通过查看 Secret 可以看到一个前缀为 linkerd-identity-token- 的 Secret 对象,我们可以将其导出来进行查看:

$ kubectl get secret -n linkerd linkerd-identity-token-nqhbk -o yamlapiVersion: v1data:  ca.crt: <ca.crt>  namespace: bGlua2VyZA==  token: <token>kind: Secretmetadata:  annotations:    kubernetes.io/service-account.name: linkerd-identity    kubernetes.io/service-account.uid: cdc3d8fd-0e02-4b17-88ce-d2cc0a9e4907  name: linkerd-identity-token-nqhbk  namespace: linkerdtype: kubernetes.io/service-account-token

输出的数据部分中名为 ca.crt 的字段就是在 Linkerd 安装期间生成的 UTF-8 编码的根证书。此证书称为“信任之锚”,因为它是用作颁发给代理的所有证书的基础。

信任锚还用于在安装时创建另一个证书和密钥对:颁发者凭据,这些存储在名为 linkerd-identity-issuer 的单独 Kubernetes Secret 中。颁发者凭据用于向 Linkerd proxy 颁发证书,同样我们也可以来查看该 Secret 的数据。

$ kubectl get secret -n linkerd linkerd-identity-issuer -o yamlapiVersion: v1data:  crt.pem: <crt.pem>  key.pem: <key.pem>kind: Secretmetadata:  labels:    linkerd.io/control-plane-component: identity    linkerd.io/control-plane-ns: linkerd  name: linkerd-identity-issuer  namespace: linkerdtype: Opaque

接下来我们将了解如何使用这些密钥向代理颁发证书以启用 mTLS。

Linkerd 代理如何获取证书

首先,当 Pod 被注入 Linkerd 代理时,该代理会向 Linkerd 的身份服务发送证书签名请求 (CSR)。身份服务使用颁发者凭据向该代理颁发签名证书(CSR 的作用域是运行 Pod 的 Kubernetes ServiceAccount,因此生成的证书与该 ServiceAccount 相关联),证书将在 24 小时后过期。在证书过期前,代理向身份服务发送新的证书签名请求,获取新证书;这个过程在 Linkerd 代理的整个生命周期内都会持续,这称为证书轮换,是一种将证书泄露造成的损失降至最低的自动化方式:在最坏的情况下,任何泄露的证书只能使用 24 小时

linkerd check 命令有一种简单的方法来确保代理都具有由身份服务颁发的证书,我们可以自己通过传递 --proxy 标志来检查代理的状态。

# 使用 --proxy 标志在数据平面检查代理$ linkerd check --proxy# ......linkerd-identity----------------√ certificate config is valid√ trust anchors are using supported crypto algorithm√ trust anchors are within their validity period√ trust anchors are valid for at least 60 days√ issuer cert is using supported crypto algorithm√ issuer cert is within its validity period√ issuer cert is valid for at least 60 days√ issuer cert is issued by the trust anchor
linkerd-identity-data-plane---------------------------√ data plane proxies certificate match CA
# ......
Status check results are √

上面的输出结果中包括一个 linkerd-identity-data-plane 部分,用来指示代理是否正在使用由信任锚颁发的证书。

linkerd-identity-data-plane---------------------------√ data plane proxies certificate match CA

我们也可以在 Linkerd 代理和身份服务中开启 debug 模式,以查看代理将 CSR 发送到身份服务并取回证书。

$ kubectl get deploy -n linkerdNAME                     READY   UP-TO-DATE   AVAILABLE   AGElinkerd-destination      1/1     1            1           11dlinkerd-identity         1/1     1            1           11dlinkerd-proxy-injector   1/1     1            1           11d$ kubectl edit deploy linkerd-identity -n linkerd# ......  spec:    containers:    - args:      - identity      - -log-level=debug  # 设置为 debug 模式      - -log-format=plain      - -controller-namespace=linkerd      - -identity-trust-domain=cluster.local      - -identity-issuance-lifetime=24h0m0s      - -identity-clock-skew-allowance=20s      - -identity-scheme=linkerd.io/tls# ......

当身份服务重新更新后,我们可以观察对应 Pod 的日志信息:

$ kubectl logs -f -n linkerd deploy/linkerd-identity -c identity

上面的命令会输出很多日志到控制台,在 Linkerd 身份日志输出中,我们可以看到 Request BodyResponse Body 相关的输出,其中包括一个很长的 UTF-8 编码数据,即 CSR 和颁发给代理的证书:

# ......time="2022-08-30T07:39:17Z" level=debug msg="Issuer has been updated"time="2022-08-30T07:39:17Z" level=info msg="starting admin server on :9990"time="2022-08-30T07:39:17Z" level=info msg="starting gRPC server on :8080"time="2022-08-30T07:39:17Z" level=debug msg="Validating token for linkerd-identity.linkerd.serviceaccount.identity.linkerd.cluster.local"I0830 07:39:17.291787       1 request.go:1123] Request Body: {"kind":"TokenReview","apiVersion":"authentication.k8s.io/v1","metadata":{"creationTimestamp":null},"spec":{"token":"<......>"},"status":{"user":{}}}# ......I0830 07:39:17.291969       1 round_trippers.go:435] curl -k -v -XPOST  -H "Accept: application/json, */*" -H "Content-Type: application/json" -H "User-Agent: controller/v0.0.0 (linux/amd64) kubernetes/$Format" -H "Authorization: Bearer <masked>" 'https://10.96.0.1:443/apis/authentication.k8s.io/v1/tokenreviews'I0830 07:39:17.293883       1 round_trippers.go:454] POST https://10.96.0.1:443/apis/authentication.k8s.io/v1/tokenreviews 201 Created in 1 milliseconds# ......I0830 07:39:17.294241       1 request.go:1123] Response Body: {"kind":"TokenReview","apiVersion":"authentication.k8s.io/v1","metadata":{"creationTimestamp":null,"managedFields":[{"manager":"controller","operation":"Update","apiVersion":"authentication.k8s.io/v1","time":"2022-08-30T07:39:17Z","fieldsType":"FieldsV1","fieldsV1":{"f:spec":{"f:token":{}}}}]},"spec":{"token":"<token>"},"status":{"authenticated":true,"user":{"username":"system:serviceaccount:linkerd:linkerd-identity","uid":"cdc3d8fd-0e02-4b17-88ce-d2cc0a9e4907","groups":["system:serviceaccounts","system:serviceaccounts:linkerd","system:authenticated"],"extra":{"authentication.kubernetes.io/pod-name":["linkerd-identity-5d9b874d66-m77ps"],"authentication.kubernetes.io/pod-uid":["2f147d37-1616-487a-b7aa-1805b84c026a"]}},"audiences":["https://kubernetes.default.svc.cluster.local"]}}# ......

接下来我们再来查看下 Emojivoto 应用服务之间的安全性。首先我们可以使用 linkerd viz edges 命令来查看下 Pod 之间是如何连接的。

$ linkerd viz edges po -n emojivotoSRC                           DST                         SRC_NS        DST_NS      SECUREDvote-bot-6d7677bb68-jvxsg     web-5f86686c4d-58p7k        emojivoto     emojivoto   √web-5f86686c4d-58p7k          emoji-696d9d8f95-5vn9w      emojivoto     emojivoto   √web-5f86686c4d-58p7k          voting-ff4c54b8d-xhjv7      emojivoto     emojivoto   √prometheus-7bbc4d8c5b-5rc8r   emoji-696d9d8f95-5vn9w      linkerd-viz   emojivoto   √prometheus-7bbc4d8c5b-5rc8r   vote-bot-6d7677bb68-jvxsg   linkerd-viz   emojivoto   √prometheus-7bbc4d8c5b-5rc8r   voting-ff4c54b8d-xhjv7      linkerd-viz   emojivoto   √prometheus-7bbc4d8c5b-5rc8r   web-5f86686c4d-58p7k        linkerd-viz   emojivoto   √

我们可以看到上面的输出结果中最后包含一列 SECURED,表示是否是安全的连接,下面的值均为 ,表示是安全的连接。

然后我们再次使用 linkerd viz tap 命令来捕获实时流量,在输出的信息中也包含一个 tls=true 的标签值,如下所示:

$ linkerd viz tap deploy web -n emojivotoreq id=0:0 proxy=in  src=10.244.1.165:47130 dst=10.244.1.176:8080 tls=true :method=GET :authority=web-svc.emojivoto:80 :path=/api/listreq id=0:1 proxy=out src=10.244.1.176:42096 dst=10.244.1.188:8080 tls=true :method=POST :authority=emoji-svc.emojivoto:8080 :path=/emojivoto.v1.EmojiService/ListAllrsp id=0:1 proxy=out src=10.244.1.176:42096 dst=10.244.1.188:8080 tls=true :status=200 latency=2731µs# ......

到这里面我们就了解了 Linkerd 的 Identity 组件如何向数据平面中的 Linkerd 代理颁发证书,以及 Linkerd 在代理中的 mTLS 实现如何使用这些证书来加密通信并验证双方的身份。

自动轮换控制器平面 TLS 凭证

Linkerd 的自动 mTLS 功能使用一组 TLS 凭据为代理生成 TLS 证书:信任锚(trust anchor)、颁发者证书(issuer certificate)和私钥(private key)。虽然 Linkerd 每 24 小时自动轮换数据平面代理的 TLS 证书,但它不会轮换用于颁发这些证书的 TLS 凭据。接下来我们来了解下如何使用 Cert-manager 进行自动轮换颁发者证书和私钥。

Cert-manager

Cert-manager 是一个非常流行的云原生证书管理工具。Cert-manager 将证书和证书颁发者作为 CRD 资源类型添加到 Kubernetes 集群中,简化了获取、更新和使用这些证书的过程。它可以从各种受支持的来源发布证书,包括 Let's EncryptHashiCorp VaultVenafi 以及私有 PKI。它将确保证书有效并且是最新的,并尝试在证书到期前的配置时间更新证书。

网络异常,图片无法展示
|

cert manager

Cert-manager 的安装也非常简单,可以直接使用官方提供的资源清单文件一键安装,如下所示:

$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml

默认会将相关资源安装到一个名为 cert-manager 的命名空间中:

$ kubectl get pods -n cert-managerNAME                                      READY   STATUS    RESTARTS   AGEcert-manager-55649d64b4-kdcfk             1/1     Running   0          43scert-manager-cainjector-666db4777-cp2dn   1/1     Running   0          43scert-manager-webhook-6466bc8f4-5lvw5      1/1     Running   0          43s

颁发证书

接下来我们使用 step 工具创建一个签名密钥对,并将其存储在 Kubernetes 的一个 Secret 对象中。

$ step certificate create root.linkerd.cluster.local ca.crt ca.key \  --profile root-ca --no-password --insecureYour certificate has been saved in ca.crt.Your private key has been saved in ca.key.

然后将生成的 ca.crtca.key 保存到 Secret 对象中:

$ kubectl create secret tls linkerd-trust-anchor --cert=ca.crt --key=ca.key -nsecret/linkerd-trust-anchor created

有了 Secret,我们可以创建一个引用密钥的证书颁发者 Issuer 资源:

$ cat <<EOF | kubectl apply -f -apiVersion: cert-manager.io/v1kind: Issuermetadata:  name: linkerd-trust-anchor  namespace: linkerdspec:  ca:    secretName: linkerd-trust-anchorEOF$ kubectl get issuer -n linkerdNAME                   READY   AGElinkerd-trust-anchor   True    84s

然后我们就可以颁发证书了,并将它们写入到一个 Secret 对象中,最后,我们可以创建一个 cert-manager "Certificate" 资源, 它使用这个 Issuer 来生成所需的证书:

$ cat <<EOF | kubectl apply -f -apiVersion: cert-manager.io/v1kind: Certificatemetadata:  name: linkerd-identity-issuer  namespace: linkerdspec:  secretName: linkerd-identity-issuer  duration: 48h  renewBefore: 25h  issuerRef:    name: linkerd-trust-anchor    kind: Issuer  commonName: identity.linkerd.cluster.local  dnsNames:  - identity.linkerd.cluster.local  isCA: true  privateKey:    algorithm: ECDSA  usages:  - cert sign  - crl sign  - server auth  - client authEOF$ kubectl get certificate -n linkerdNAME                      READY   SECRET                    AGElinkerd-identity-issuer   True    linkerd-identity-issuer   17s

在上面的资源清单文件中,duration 指示 cert-manager 将证书视为有效期 48 小时,而 renewBefore 指示 cert-manager 将尝试在当前证书到期前 25 小时颁发新证书。

此时,cert-manager 现在可以使用此证书资源获取 TLS 凭据,该凭据将存储在名为 linkerd-identity-issuer 的 Secret 中,要验证您新颁发的证书,我们可以运行下面的命令:

$ kubectl get secret linkerd-identity-issuer -o yaml -n linkerdapiVersion: v1data:  ca.crt: <ca.crt>  crt.pem: <crt.pem>  key.pem: <key.pem>  tls.crt: <tls.crt>  tls.key: <tls.key>kind: Secretmetadata:  name: linkerd-identity-issuer  namespace: linkerdtype: Opaque

现在我们只需要通知 Linkerd 使用这些凭据就可以了。由于我们是通过 linkerd 命令行工具进行安装的,Linkerd 控制平面默认会通过 --identity-external-issuer 标志进行安装,该标志指示 Linkerd 从 linkerd-identity-issuer 的 Secret 读取证书。每当更新存储在 Secret 中的 certificatekey 时, identity 服务将自动检测此更改并重新加载新凭据。

这样我们就设置了 Linkerd 控制平面 TLS 凭据的自动轮换,如果你想监控更新过程,你可以检查服务发出的 IssuerUpdated 事件:

$ kubectl get events --field-selector reason=IssuerUpdated -n linkerd
LAST SEEN   TYPE     REASON          OBJECT                        MESSAGE2m37s       Normal   IssuerUpdated   deployment/linkerd-identity   Updated identity issuer

可以看到已经执行了 Updated identity issuer


相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
相关文章
|
2月前
|
Kubernetes 应用服务中间件 nginx
K8S(05)核心插件-ingress(服务暴露)控制器-traefik
K8S(05)核心插件-ingress(服务暴露)控制器-traefik
30 0
|
2月前
|
缓存 监控 安全
Istio架构及工作原理
【2月更文挑战第17天】从 Istio 的设计和实现原理可以看出,它是采用模块化设计,并且各个模块之间高度解耦,Proxy 专注于负责服务之间的通信,Pilot 专注于流量控制,Mixer 专注于策略控制以及监控日志功能,而 Citadel 专注于安全。
|
6月前
|
Kubernetes 安全 Cloud Native
Service Mesh和Kubernetes:加强微服务的通信与安全性
Kubernetes已经成为云原生应用程序的事实标准,它为容器编排和管理提供了出色的解决方案。然而,微服务架构的广泛采用使得服务之间的通信变得复杂,同时安全性和可观测性需求也在不断增加。这正是Service Mesh技术的用武之地。本文将深入探讨Service Mesh如Istio和Linkerd如何增强Kubernetes集群中微服务的通信和安全性。
|
7月前
|
传感器 Cloud Native 物联网
gRpc的四种通信方式详细介绍
gRpc的四种通信方式详细介绍
131 0
|
12月前
|
设计模式 缓存 开发框架
「第二部:容器和微服务架构](10) API网关模式与客户端直接通信2
「第二部:容器和微服务架构](10) API网关模式与客户端直接通信2
|
12月前
|
消息中间件 开发框架 负载均衡
「第二部:容器和微服务架构](9) API网关模式与客户端直接通信
「第二部:容器和微服务架构](9) API网关模式与客户端直接通信
|
12月前
|
负载均衡 Kubernetes Java
Envoy和类似的系统比较
Envoy和类似的系统比较
|
JSON JavaScript 小程序
【小程序】组件通信
【小程序】组件通信
172 0
【小程序】组件通信
|
流计算
最简单的 gRPC 教程—2 通信模式
gRPC 包含四种基础的通信模式: • 一元模式(Unary RPC) • 服务器端流 RPC(Server Sreaming RPC) • 客户端流 RPC(Client Streaming RPC) • 双向流 RPC(Bidirectional Streaming RPC)
172 0
|
存储 Kubernetes 监控
2.2 Kubernetes--网络通讯
k8s的网络模型假定了所有的Pod都在一个可以直接连通的扁平的网络空间中, 这在GCE(Google Compute Engine)里面是线程的网络模型, Kubernetes假定这个网络已经存在. 而在私有云里搭建Kubernetes集群, 就不能假定这个网络已经存在了. 我们需要自己实现这个网络假设, 将不同节点上的Docker容器之间的互相访问先打通, 然后运行Kubernetes.
203 0
2.2 Kubernetes--网络通讯