互联网是一个充满敌意的环境。安全不是一种选择,而是一种必须。当你设计你的应用程序时,安全必须是设计的一个组成部分,而不是你在以后的阶段添加的东西。当涉及到安全问题时,Dapr 的目标是使应用程序默认为安全的。这意味着你应该在默认情况下获得通用的安全最佳实践,然后你可以对系统进行微调,以满足你的安全要求。Dapr 仍然是一个年轻的项目,我们实现这一目标的旅程还远未结束,但本章介绍了我们如何设计 Dapr 组件,以便我们可以插入安全功能,并以最自然的方式将最佳实践带到你的应用中。

确保分布式应用的安全

为了保证系统的安全,你需要考虑几个挑战,包括访问控制、数据保护、安全通信、入侵和异常检测,以及用户隐私。Dapr 的长期目标是帮助你尽可能地解决这些挑战,使你的应用程序在默认情况下是安全的。

因为 Dapr 还处于起步阶段,我们还有很长的路要走。本章首先讨论了我们在安全方面的一些想法,让你了解Dapr安全的大方向。然后,这一章将介绍我们在 Dapr 中实现的东西。有可能在你阅读这篇文章时,Dapr 已经为你提供了更多的安全功能。当然,如果你有任何反馈或建议,请向 Dapr 代码仓库 提交问题。

让我们从最明显的关注领域开始:访问控制。

访问控制

确保中央服务器的安全是相对容易的,因为你可以完全控制托管环境。例如,你可以设置防火墙规则和负载平衡器策略来限制对特定客户 IP 段和端口的访问。你还可以与中央身份供应商(如活动目录)集成,以实现集中的认证和授权。

:::tips 这两个术语经常被混淆。简单地说,认证回答的是 “你是谁?” 而授权回答的是 “你被允许做什么?” 的问题。 :::

当你在云中托管你的应用程序时,你应该利用云平台提供的安全功能。现代云平台所提供的安全功能与你的办公场所所提供的类似,所以你可以使用熟悉的技术和概念来管理访问控制,如网络安全组、RBAC、基于证书的认证和防火墙。

当你试图管理一个分布式系统的安全时,事情会变得更加复杂,因为你经常需要处理分散的计算资源、不受信任的连接和异质的技术堆栈。在处理常见的安全挑战时,你必须重新考虑你的策略,如建立身份,设置访问控制策略,以及通过网络进行通信。

身份

一个应用程序需要处理两种类型的身份:用户身份和服务身份。一个用户身份识别一个特定的用户,而一个服务身份代表一个服务或一个进程。你可以为这两种类型的身份定义访问控制策略(接下来讨论)。例如,你可以授予一个用户身份对关系数据库表的读取权限,你可以限制一个服务身份进行任何外向连接。

像微软 Azure Active Directory(AAD)这样的服务允许你为用户和服务建立和管理身份。在这种情况下,AAD 被称为可信的身份提供者(IP),而你的应用程序是 IP 的依赖方(RP)。IP 向受信任的 RP 发放安全令牌。RP 从令牌中提取索赔,并根据索赔做出授权决定。例如,当你去租车时,租车公司(RP)需要一个安全令牌,在这种情况下,它是你的驾驶执照,由一个受信任的 IP —— 机动车管理局(DMV)签发。声明(Claim)是关于该令牌的一个特定属性的声明。在驾驶执照的案例中,执照上的名字是 DMV 对持有人的姓名属性的一种声明。因为 RP 相信 IP 和发行的安全令牌,所以 RP 认为令牌的每项声明 —— 姓名、地址、年龄、眼睛颜色等 —— 都是真实的。这种将认证委托给受信任的 IP 的设计被称为基于声明的架构(Claim-based Architecture)。

从 Dapr 的角度来看,建立用户的身份是一个应用的问题。Dapr 可以建立一些实用程序,比如你在 第 1 章 看到的 OAuth 中间件,以促进 OAuth 2.0、WS-Federation 和分散标识符(DID)等流行协议下的认证和授权过程。但 Dapr 不会对你的应用程序选择如何识别用户和施加访问控制策略有意见。

Dapr 有可能为建立服务身份提供巨大的帮助。正如你在介绍中看到的,每个 Dapr 实例都由一个独特的字符串名称来识别。如果我们能够强化这个名字,我们就可以把 Dapr ID 作为 Dapr 边车所代表的应用程序的服务身份。例如,一种可能性是 Dapr 与 AAD Pod Identity 整合,它允许 Kubernetes 应用通过 AAD 安全地访问云资源和服务。

访问控制策略

由于 Dapr 侧设备位于服务调用路径上,Dapr 可以提供更多的协助,进行细粒度的访问控制。例如,Dapr 可以对 /foo 路由应用一个访问策略,只允许从明确允许的 Dapr ID 通过特定的 HTTP 动词(如 GET)进行访问。在这个功能由 Dapr 运行时本身实现之前,你可以编写一个自定义的中间件来提供这种过滤。下面的伪代码显示了如何使用 Dapr 中间件定义一个过滤器:

  1. func (m *Middleware) GetHandler(metadata middleware.Metadata)
  2. (func(h fasthttp.RequestHandler) fasthttp.RequestHandler, error) {
  3. ...
  4. return func(h fasthttp.RequestHandler) fasthttp.RequestHandler {
  5. return func(ctx *fasthttp.RequestCtx) {
  6. if (ctx.IsGet() && ctx.Path == "/foo") {
  7. h(ctx);
  8. } else {
  9. // Deny access
  10. }
  11. }
  12. }, nil
  13. }

更有趣的是,在使用消息传递时,Dapr 可以加强类似的访问控制策略。例如,Dapr 边车可以拒绝向某些主题发布的请求,或者禁止应用程序从某些主题读取。这样的策略可以由中间件配置或其他元数据矩阵捕获,并且策略的强化与应用程序代码完全分离。这正是 Dapr 旨在为分布式应用开发者提供的关注点分离。与 Dapr 的跟踪功能相结合,这样的访问控制机制还能提供对访问被授予或拒绝的完全可见性。

网络安全

访问控制也可以在网络层实现。许多微服务系统使用服务网格来管理动态网络环境。Dapr 被设计成能与现有的服务网状结构解决方案(如 Consul 和 Istio)很好地协作。图 4-1 说明了 Dapr 边车如何铺设在服务网格代理 Envoy 之上,以便在由服务网格强化的网络策略之上,提供额外的访问控制和过滤。
image.png

图 4-1. Dapr 边车与服务网格 Enovy 代理一起工作

例如,以下用 HashiCorp 配置语言(HCL)编写的 Consul 服务规则,允许对任何没有前缀的服务进行读取访问,对 app 服务进行写入访问。这些规则还拒绝对 admin 服务的所有访问。这些规则是由 Consul 执行的,而 Dapr(和应用程序)在这种情况下仍然视而不见:

  1. service_prefix "" {
  2. policy = "read"
  3. }
  4. service "app" {
  5. policy = "write"
  6. }
  7. service "admin" {
  8. policy = "deny"
  9. }

有一点需要注意的是,Dapr 提供了相互的 TLS,这个功能在服务网格中很常见。因此,当你配置 Dapr 与服务网格一起工作时,你可能想禁用其中的 TLS 层。请查阅 Dapr 文档 以获得指示和建议。

即使适当的服务访问控制策略已经到位,我们仍然需要更深入一层,保护这些服务运行的数据。我们接下来会看一下数据保护。

数据保护

谈到保护数据,我们需要考虑三个方面:静止的数据、传输中的数据和使用中的数据。

保护静止的数据

保护静态数据的一个常见方法是使用加密。在写这篇文章的时候,Dapr 并没有提供开箱即用的数据加密,但是自动加密/解密功能可能会在将来被添加到它的状态存储中。Dapr 也有一个内置的功能来管理秘密,我们将在本章后面介绍。你可以使用这个功能来管理用户数据的加密密钥。

Dapr 并不关心数据的备份和恢复;我们认为这个责任应该由底层数据存储或通过专门的备份/恢复解决方案来承担。

保护传输中的数据

Dapr 边车在相互通信时使用 SSL 隧道,因此数据是通过安全通道进行交换的。这避免了诸如窃听和中间人攻击等威胁。

当 Dapr 发送和接收消息骨干网的消息时,它使用该服务要求的认证方法对相应的服务进行认证。大多数这些服务在交换数据时需要安全通道。

如果需要,应用程序可以应用额外的数据集成保护,如加密和数字签名。这些都是应用方面的问题,不在 Dapr 的范围内。

你可能会想,是否可以开发一个加密/解密中间件,在数据被放到电线上之前提供自动加密/解密。这将是可行的,但有一个注意事项:Dapr 追踪中间件被插入到中间件堆栈的顶部。这意味着数据在进入加密层之前可能会被记录为纯文本(当记录消息体被启用时)。我们认为这是一个合理的设计,因为加密的日志更难在诊断中使用。如果你需要在记录之前窜改一些敏感数据,你可以写一个自定义导出器,在把日志写到下游系统之前混淆某些数据字段,如个人身份信息(PII,Personally Identifiable Information)。

最后,保护传输中的数据的最好方法是根本就不传输数据。我们将在 第 5 章 介绍的 Dapr 行为体模型提供了状态封装,因此行为体的状态只由行为体本身维护。例如,你可以将一个订单封装为一个角色,而不是在各个服务间发送采购订单。对订单的所有可能的操作都是通过行为体所暴露的方法进行的。

对于保护静态数据和保护传输中的数据,都有既定的方法和协议。保护使用中的数据,也就是我们接下来要简单介绍的,要棘手得多。

保护使用中的数据

无论数据在静止状态和运输过程中如何加密和保护,它通常都是以纯文本方式处理。黑客获得运行程序的概率很低。然而,这种攻击往往针对高价值的目标,如金融信息或访问密钥。一些最严重的信息泄露被追溯到无赖的内部人员,他们有时拥有管理权限。

边缘地区的情况甚至更加复杂。例如,边缘计算(与云计算相比)的一个独特挑战是,对手可以将一个被篡改的设备加入到计算平面,以捕获敏感的用户信息。

幸运的是,有一些可能的方法来保护数据免受特权用户的影响。例如,保密计算使用一个可信的执行环境(TEE,Trusted Execution Enviornment,又称飞地)。基于软件或硬件的 TEE 创建了一个孤立的执行环境,任何人都无法从外部观察到,甚至是系统管理员。TEE 可以要求证明(一种软件证明其身份的机制),以确保只有经批准的代码被执行。

在算法层面,安全的多方计算(MPC,Multiparty Computation)允许多方共同计算其输入的函数,同时保持输入的私密性,而同态加密允许在加密数据上进行计算(如 AI 训练和推理)。

在撰写本文时,Dapr 没有计划在使用过程中帮助保护数据。Dapr 帮助保密计算的一个可能方式是与 Open Enclave SDK 集成,以封装飞地的创建、代码部署、证明以及可信组件和不可信组件之间的通信。Dapr 也可以被扩展以支持某些 MPC 场景。例如,一个定制的 Dapr 中间件可以提供同态加密,这样就可以在加密的同时处理数据。此外,一些机器学习模型可以适应在加密的数据上运行推理,而加密的结果只能由原始数据所有者解密。

安全的通信

当我们在上一节讨论保护传输中的数据时,我们触及了安全通信。然而,我们还需要考虑另一个传输通道:Dapr 边车和它所服务的应用进程之间的通信通道。在一个边车架构中,边车和它所连接的应用程序处于同一个安全域中,在这个安全域中,它们可以用纯文本进行交流。然而,我们从客户那里听说,他们希望在 Dapr 边车和应用程序之间有安全通道的深入保护。这是有道理的,因为 Dapr 边车也作为一个独立的进程工作。在写这篇文章的时候,这个功能已经被放在路线图上了;请咨询在线文档的更新。

入侵和异常情况检测

尽管 Dapr 没有开箱即用的入侵或异常检测功能,但与此类系统的集成并不是一个牵强的目标。例如,一个定制的 Dapr 中间件可以将数据包重复发送到一个基于机器学习的异常检测系统,用于训练和推理,这可以在不干扰应用本身的情况下透明地完成。

Dapr 确实提供了主动的速率限制,以避免过多的服务调用。这是通过一个社区贡献的中间件完成的。该实现使用 Tollbooth,这是一个通用的中间件,用于基于 Token Bucket 算法的 HTTP 请求的速率限制。该算法以固定的速度将令牌放入一个假想的桶中。每个请求都会消耗一定数量的令牌,与信息大小成正比。当一个请求进来时,如果桶里有足够的代币可以 “花” 在请求上,这个请求就会通过。否则,请求会被拒绝。当一个请求被拒绝时,响应将包含诸如 X-Rate-Limit-Limit 等头信息,以保持最大的请求限制和 X-Rate-Limit-Duration,以保持限速器的持续时间。这些都是给客户端的信号,使其放弃请求。

速率限制可以用来抵御某些类型的攻击。例如,一个正常的用户将不能每秒提交超过几次的登录凭证。如果你看到许多快速连续的登录尝试,你的服务很可能受到试图猜测密码的恶意脚本的攻击;速率限制可以防止这种暴力的努力。然而,速率限制可能是一把双刃剑,因为它不能区分合法和恶意的流量。当你的网站遇到拒绝服务(DoS)或分布式拒绝服务(DDoS)攻击时,尽管你可以使用速率限制来避免你的服务器过载,但你也会拒绝合法流量,因为一个简单的速率限制系统只控制整个流量。

一些高级的速率限制策略,例如只限制未经授权的请求(例如通过检查授权头的存在),可以更好地防御 DDoS 攻击。如果社区中的安全专家能够帮助我们进一步改进速率限制中间件,我们会非常高兴。当涉及到安全问题时,我们希望能公开、坦诚地对待它。

在前面的讨论中,我们已经谈到了 Dapr 现在如何在安全的某些方面提供帮助,它很可能在未来能够提供帮助,以及在某些方面需要社区的帮助来做进一步的改进。本章的其余部分更详细地介绍了 Dapr 目前提供的功能。由于 Dapr 正处于快速发展阶段,请查阅 在线文档 以了解最新的安全功能更新。

Dapr 安全功能

在撰写本文时,Dapr 提供了少量的安全功能,包括秘密存储、秘密 API 和双向 TLS(mTLS)。

秘密存储

秘密存储被设计用来存储敏感信息,如证书和密码。数据库和秘密存储的区别在于,秘密存储采用额外的机制和约束来进一步保护所保存的信息,例如:

  • 受限的 API 表面和访问控制:秘密存储通常需要特定的凭证和服务器角色。
  • 静止状态下的加密:一些秘密存储在静止时自动用存储控制的密钥或用户提供的密钥对秘密数据进行加密。请注意,Kubernetes 秘密存储将秘密作为 Base64 编码的数据保存在底层 etcd 存储中。这些数据默认是不加密的,但你可以选择加入静态加密。更多细节请参见 Kubernetes 文档
  • 硬件保护:像 Azure Key Vault 这样的安全存储使用硬件安全模块(HSM),利用专门的硬件来保护秘密。

与其他组件一样,Dapr 通过一个秘密存储接口支持可插入的秘密存储。在撰写本文时,Dapr 支持以下的秘密存储:

  • Kubernetes Secret
  • Azure Key Vault
  • HashiCorp Vault
  • AWS Secrets Manager
  • Google Cloud KMS
  • Google Secret Manager

Dapr 提供了对 Kubernetes Secret 的内置支持,所以不需要特别的配置来设置。其他秘密存储可以通过秘密存储描述清单来描述。例如,下面的清单描述了一个 Azure Key Vault:

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: azurekeyvault
  5. spec:
  6. type: secretstores.azure.keyvault
  7. metadata:
  8. - name: vaultName
  9. value: "<your key vault name>"
  10. - name: spnTenantId
  11. value: "<your service principal tenant ID>"
  12. - name: spnClientId
  13. value: "<your service principal app ID>"
  14. - name: spnCertificateFile
  15. value : "<pfx certificate file local path>"

一旦你定义了 Azure Key Valut,你就可以使用 Azure 工具,如 Azure CLI 来存储其中的秘密。下面的 Azure CLI 命令样本在 Azure Key Vault 中创建了一个名为 redisPassword 的秘密:

  1. $ az keyvault secret set --name redisPassword --vault-name <your key vault name> --value "<your Redis password>"

然后当您定义其他组件时,您可以通过使用 secretKeyRef 元素来引用您的秘密存储中的秘密。下面的示例组件清单定义了一个 Redis 状态存储。该存储的密码作为 redisPassword 秘密保存在 Azure Key Vault 中,并通过 secretKeyRef 元素进行引用:

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: statestore
  5. spec:
  6. type: state.redis
  7. metadata:
  8. - name: redisHost
  9. value: "redis-master:6379"
  10. - name: redisPassword
  11. secretKeyRef:
  12. name: redisPassword
  13. auth:
  14. secretStore: azurekeyvault

Dapr 的秘密存储功能只为 Dapr 内部使用而设计。后来,我们的客户要求提供一个秘密 API,通过它,应用程序代码可以访问保存的秘密。我们将很快介绍这个 API。

你不太可能需要自己实现一个秘密存储。但如果你需要的话,下面的部分将提供一个简单的介绍。

实现一个秘密存储

Dapr 将秘密存储定义为一个简单的接口,包含一个 Init 方法和一个 GetSecret 方法,如下面的代码片段所示:

  1. type SecretStore interface {
  2. Init(metadata Metadata) error
  3. GetSecret(req GetSecretRequest) (GetSecretResponse, error)
  4. }

GetSecretRequest 结构包含一个键和一个键/值对集合,作为元数据附加到请求中:

  1. type GetSecretRequest struct {
  2. Name string `json:"name"`
  3. Metadata map[string]string `json:"metadata"`
  4. }

Dapr 不以任何方式解释元数据。相反,它只是将所有附加的元数据传递给秘密存储实现。这种设计是为了处理这样的情况:秘密存储需要比秘钥名称更多的信息来访问一个秘密。请求这样的元数据使得使用你的安全存储的代码的可移植性降低。尽管其他商店可以选择忽略元数据并照常运作,但相同的元数据密钥可能会被多个秘密商店意外地选择,并且会被不同的商店解释为不同的内容,从而导致不可预测的错误。强烈建议你在 Init 方法中捕获足够的信息,这样秘密就可以只通过秘密密钥的名称来检索。

GetSecretResponse 结构持有秘密数据的键/值对:

  1. type GetSecretResponse struct {
  2. Data map[string]string `json:"data"`
  3. }

如果没有找到请求的密钥,你的代码应该返回一个 error 对象。

如前所述,当您定义任何 Dapr 清单时,您可以使用 secretKeyRef 元素来引用秘密。Dapr 还引入了一个秘密 API,允许应用程序通过代码访问秘密。我们将在下一节介绍这个 API。

秘密 API

Dapr 暴露了一个秘密 API,应用程序可以使用该 API 从秘密存储的端点 v1.0/secrets/<secret store name>/<secret key name> 检索一个秘密。

这个方便的 API 使应用程序很容易检索到秘密并在其代码中使用它们。其目的是帮助开发者避免在其代码中保存敏感信息,如密码和连接字符串。该 API 是只读的,这意味着应用程序代码不能通过它创建或更新任何秘密密钥。另一方面,操作可以根据需要更新秘密,直接对秘密存储进行修改而不影响代码。例如,他们可以旋转证书,更新密码,以及修改与具有适当访问权限的数据库账户的连接字符串。

讽刺的是,这个 API 的缺点恰恰是它的便利性。一旦配置好,应用程序代码就可以请求任何秘密密钥,而不需要认证。如前所述,在边车架构下,Dapr 边车和应用程序代码被认为是在同一个安全域。因此,允许直接访问秘密并不一定是错误的设计。然而,有一个潜在的危险:如果应用程序代码被破坏,对手可以获得对秘密存储中所有秘密的访问。

通过与服务身份的整合,Dapr 能够提供一些自动认证和授权机制来限制对秘密的访问。例如,它可以执行一个策略,只允许一个应用程序访问特定的秘密存储或甚至特定的秘密密钥。然后,即使一个应用程序被破坏,攻击者也无法使用该应用程序来获得共享同一秘密存储的其他应用程序的秘密。

这就是 Dapr 在秘密管理方面提供的全部内容。接下来,我们将换个角度,谈谈 Dapr 如何通过双向 TLS 来保证 Dapr 与 Dapr 之间的通信安全。

双向 TLS(mTLS)

传输层安全(TLS),取代了已被废弃的安全套接字层(SSL)协议,是一个用于客户和服务器之间安全通信的加密协议。默认情况下,TLS 使用服务器的 X.509 证书向客户证明服务器的身份。作为选择,它也可以使用客户的 X.509 证书向服务器证明客户的身份。这就是所谓的双向 TLS。双向TLS(mTLS)经常用于分布式系统中,因为在这样的系统中,很难识别服务器或客户端。相反,组件在一个服务网格中相互调用,所以任何组件都可以是服务器和客户端。双向 TLS 通过交叉检查证书,确保组件之间的所有服务调用在双方都得到验证。

在介绍 mTLS 如何工作之前,我们需要对证书和相关概念做一个简单的解释。如果你已经熟悉了这些概念,你可以跳过下面的小节。

X.509 证书

X.509 证书是一种使用国际 X.509 公钥基础设施(PKI)标准的数字证书。证书是由受信任的证书颁发机构(CA)颁发的,并包含有关代表实体的信息,如经过验证的实体名称和证书的有效期。证书还包括实体的公钥和用实体的私钥编码的签名。PKI 标准保证,如果你能使用公钥来解密数据,那么该信息已经用数据所有者的私钥进行了加密。

当你使用浏览器通过 HTTPS 访问一个网站时,你的浏览器会验证服务器的证书,检查该证书是否由受信任的 CA 签发,以及证书中包含的域名是否与你试图访问的地址相匹配。如果这两项检查都通过了,浏览器就会显示一些视觉提示(如 图 4-2 所示的绿色挂锁),表明你已经安全地连接到一个合法的服务器。
image.png

图 4-2. 一个经过验证的 HTTPS 连接

如果证书检查失败,现代浏览器可能会警告你,你与服务器的连接是不安全的。图 4-3 显示了一个证书检查失败的例子。在这种情况下,与 www.test.com 相关的证书已经过期(截图是在 2020 年 2 月 29 日拍摄的),因此证书检查失败。
image.png

图 4-3. 一个未通过验证的 HTTPS 链接

:::tips 始终通过 HTTPS 访问网站,并始终检查其证书是否有效。这将有助于你避免一些网络钓鱼攻击,在这些攻击中,对手使用一个模仿合法网站的诱饵网站,试图欺骗你向攻击者提供你的个人信息(银行账户或信用卡号码、电子邮件地址、密码等)。当你在你的应用程序代码中访问一个网络服务时,确保你的代码也使用 HTTPS。同样,当你暴露一个公共服务时,总是确保它暴露一个 HTTPS 端点,并保持你的证书是最新的。 :::

一个 CA 也是由证书来识别的。该证书被导入你的计算机上的证书库中。可以有多个级别的 CA;没有父级的权威被称为根 CA。代表根 CA 的证书被导入到受信任的根证书授权商店中。子 CA 需要其父 CA 的签发者证书来签发新的证书。这些证书是连锁的,根证书由根 CA 签发。当验证一个证书时,你需要通过证书链进行追踪,直到你到达一个经过验证的根证书。

申请 X.509 证书

要向 CA 申请证书,申请者首先要生成一个公钥/私钥对。然后,它创建一个证书签署请求(CSR),其中包含其公开密钥和所请求的字段,如域名。它用自己的私钥签署 CSR 并将 CSR 发送给 CA。CA 使用请求者的公钥对 CSR 进行验证。然后它生成一个数字证书,其中包含请求的字段、请求者的公钥和 CA 的公钥。数字证书用 CA 的私钥签名,并发回给请求者。

现在你对证书有了了解,我们可以继续讨论 Dapr mTLS。

Dapr mTLS

mTLS 要求每一方用证书来识别自己,为了获得证书,你需要与一个受信任的 CA 合作。设置一个 CA 并完成证书申请过程并不复杂,但这是开发人员不常喜欢的事情。这就是 Dapr 出现的地方。它自己主持一个 CA 服务,它自动生成证书的过程,并在 Dapr 边车之间建立一个安全的通信通道。当你的应用程序代码使用 Dapr 边车进行通信时,所有的流量都会自动通过安全的 HTTPS 通道发送,你不需要在你的代码中做任何特殊处理。

Dapr mTLS 架构被设计为可以在 Kubernetes 模式和独立模式下工作。这两种模式的主要区别在于根证书的存储位置,如下节所述。

Dapr mTLS 架构

Dapr 带有一个名为 Sentry 的系统组件,作为一个 CA。该 CA 接受用户提供的根证书或生成一个自签名的证书作为根证书。当一个新的 Dapr 边车启动时,根证书被注入到边车中作为可信的根证书。然后,Dapr 边车向 Sentry 请求一个新的工作负载证书。最后,两个 Dapr 侧车用相应的工作负载证书互相认证。图 4-4 说明了整个过程是如何进行的,这里对这些步骤进行了更详细的描述:

  1. 可选的是,操作员将根证书加载到 Kubernetes Secret(在 Kubernetes 模式下)或文件系统中。
  2. Sentry 读取用户提供的根证书,或在必要时,生成一个自签名的证书作为根证书本身。当根证书被替换时,Sentry 会自动拾取新的证书并重建信任链。
  3. 当边车注入器注入 Dapr 边车时,它检索到根证书并将其注入 Dapr 边车中。
  4. 当 Dapr 边车初始化时,它检查是否启用了 mTLS。如果是,它就向 Sentry 发送一个 CSR。Sentry 发出一个工作负载证书,有效期为 24 小时,默认允许 15 分钟的时钟偏移。允许时钟偏移是为了避免由于时钟漂移造成的证书验证错误。
  5. Dapr 边车使用工作负载证书来相互认证,以建立一个安全的加密通信通道。

image.png

图 4-4. Dapr mTLS 架构

配置 Sentry

Sentry 配置是由 Dap r配置文件描述的,该文件看起来像这样:

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Configuration
  3. metadata:
  4. name: default
  5. spec:
  6. mtls:
  7. enabled: true
  8. workloadCertTTL: "24h"
  9. allowedClockSkew: "15m"

当在 Kubernetes 模式下运行时,你可以使用 kubectl 来查看和更新 Dapr 配置:

  1. $ kubectl get configurations/<configuration name> --namespace <Dapr namespace> -o yaml
  2. $ kubectl edit configurations/<configuration name> --namespace <Dapr namespace>

然后删除 Sentry Pod,这样替换的 Pod 就可以接受新的配置:

  1. $ kubectl delete pod --selector=app=dapr-sentry --namespace <Dapr namespace>

当以独立模式运行时,你可以用 --issuer-certificate 开关启动 Sentry 进程,加载一个根证书,并使用 --config开关加载一个自定义配置文件:

  1. $ ./sentry --issuer-credentials $HOME/.dapr/certs --trust-domain cluster.local --config=./my-config.yaml

为了启动启用了 mTLS 的应用程序代码,你需要提供一个启用了 mTLS 的 Dapr 配置文件:

  1. $ dapr run --app-id myapp --config ./mtls-config.yaml node myapp.js

Dapr mTLS 提供了 Dapr 边车设备之间的安全通信通道。它将证书管理的复杂性从开发者那里隐藏起来。

总结

在写这篇文章的时候,Dapr 提供了一套基本的安全功能,包括秘密管理、秘密 API 和 mTLS 支持。一些额外的安全功能正在酝酿之中,我们很高兴听到你的反馈并获得贡献。我们对 Dapr 的基本原理的讨论到此结束。

本书的其余部分将集中讨论使用 Dapr 的各种应用场景和设计模式。