Skip to content

本文为 SPIFFE 官方规范 The SPIFFE Workload Endpoint 的中文译本,仅供学习参考;若与英文原文有出入,以英文版为准。

SPIFFE 工作负载端点(The SPIFFE Workload Endpoint)

稳定性:稳定(Stable) —— 参见 STABILITY.md

本备忘录的状态

本文档为互联网社区规定了一项身份端点标准,并欢迎各方就改进提出讨论与建议。本文档的分发不受限制。

摘要

为联网的工作负载提供可移植、可互操作的密码学身份,或许正是 SPIFFE 的核心用例。为完整地满足这一需求,社区必须就一种标准化的方式达成共识,以便在运行时既能检索身份,又能消费与身份相关的服务。

SPIFFE 工作负载端点规范通过定义一个端点来满足这一需求,SPIFFE 可验证身份文档(SVID)及相关服务可从该端点被提供出去。具体而言,它勾勒了如何定位该端点,以及如何提供或消费它。该端点所暴露的具体服务集合超出本文档范围,但有一个显著的例外——SPIFFE 工作负载 API

1. 引言

SPIFFE 工作负载端点是一个 API 端点,工作负载(或者说正在运行的计算进程)可以通过它在运行时访问与身份相关的服务(例如身份签发或身份校验)。该端点可以暴露任意数量的与身份相关的服务,不过至少,在合规环境中运行的工作负载可以期望 SPIFFE 工作负载 API 是可用的。

本文档详述了 SPIFFE 工作负载端点的可访问性与范围、其传输协议、认证流程,以及可扩展性/发现机制。

2. 可访问性

SPIFFE 工作负载端点常常充当初始身份引导(bootstrapping)的机制,包括信任根(roots of trust)的交付与管理。由于工作负载在其早期阶段可能对自身身份或该信任谁尚一无所知,因此保护对该端点的访问非常困难。有鉴于此,SPIFFE 工作负载端点应当(SHOULD)通过一个本地端点来暴露,并且实现者不应(SHOULD NOT)将同一个端点实例暴露给一台以上的主机。将端点及相关流量限制在单台主机之内,能够缓解与初始认证及签发安全相关的引导难题。更多细节请参阅传输认证两节。

3. 传输

SPIFFE 工作负载端点必须(MUST)通过 gRPC 提供,合规的客户端也必须(MUST)支持 gRPC。它可以作为 Unix 域套接字(Unix Domain Socket, UDS)或 TCP 监听套接字暴露出来。实现应当(SHOULD)优先采用 Unix 域套接字传输,不过对于那些采用 Unix 域套接字并不现实或不可能的实现,TCP 也是受支持的。除非底层网络允许工作负载端点服务器基于源 IP 地址(例如通过 localhost 或链路本地网络)或其他强网络层断言(例如通过 SDN 策略)对工作负载进行强认证,否则禁止(MUST NOT)使用 TCP 传输。

作为针对服务端请求伪造(Server Side Request Forgery, SSRF)攻击的加固措施,每个发往 SPIFFE 工作负载端点的客户端请求都必须(MUST)包含静态 gRPC 元数据键 workload.spiffe.io,其值为 true(区分大小写)。不包含此元数据键/值的请求必须(MUST)被 SPIFFE 工作负载端点拒绝(更多信息请参阅错误码一节)。这可以防止攻击者利用某个 SSRF 漏洞访问 SPIFFE 工作负载端点——除非该漏洞同时还赋予攻击者对出站 gRPC 元数据的控制权。

3.1 传输安全

尽管 gRPC 强烈倾向于使用传输层安全(Transport Layer Security),但这里禁止(MUST NOT)将其设为必需。由于 SPIFFE 工作负载端点常常交付并管理信任根,我们无法指望工作负载事先就掌握当前生效的信任根。因此,在早期阶段,除了凭借工作负载 API 实现所处的特权地位之外,工作负载可能没有任何办法校验所出示身份的真实性。这也是 SPIFFE 工作负载端点实例不应被暴露给一台以上主机的又一个原因。更多信息请参阅认证一节。

4. 定位端点

客户端可以被显式地配置套接字位置,或者可以利用众所周知的环境变量 SPIFFE_ENDPOINT_SOCKET。如果没有被显式配置,合规的客户端必须(MUST)回退到使用该环境变量。

SPIFFE_ENDPOINT_SOCKET 环境变量的值被构造为一个 RFC 3986 URI。scheme 必须(MUST)被设为 unixtcp 之一,分别表示该端点通过 Unix 域套接字或 TCP 监听套接字提供。

如果 scheme 被设为 unix,则 authority 组成部分禁止(MUST NOT)被设置,而 path 组成部分必须(MUST)被设为 SPIFFE 工作负载端点 Unix 域套接字的绝对路径(例如 unix:///path/to/endpoint.sock)。scheme 与 path 组成部分是强制的,不得设置任何其他组成部分。

如果 scheme 被设为 tcp,则 authority 的 host 组成部分必须(MUST)被设为一个 IP 地址,而 authority 的 port 组成部分必须(MUST)被设为 SPIFFE 工作负载端点 TCP 监听套接字的 TCP 端口号。scheme、host 与 port 组成部分是强制的,不得设置任何其他组成部分。举例来说,tcp://127.0.0.1:8000 是有效的,而 tcp://127.0.0.1:8000/foo 则无效。

5. 认证

SPIFFE 工作负载端点常常充当初始身份引导的机制。因此,可以预期工作负载并不持有任何可用来认证自身的“秘密”材料。为了适配这一至关重要的用例,SPIFFE 工作负载端点禁止(MUST NOT)要求对其客户端进行任何直接认证。

作为对直接客户端认证的替代,实现者应当(SHOULD)执行带外(out-of-band)的真实性检查。这可能包括诸如内核自省(kernel introspection)或编排器询问(orchestrator interrogation)之类的技术。举例来说,可以通过检查内核套接字状态来了解是哪个进程在调用该 API。另一种做法是允许编排器将一个 Unix 域套接字放入某个特定容器中,从而将该容器的属性/身份告知 SPIFFE 工作负载端点实现。随后,这些信息便可被用作一种认证机制。

需要指出的是,尽管实现这一点所采用的方法是特定于实现的,但所选用的方法禁止(MUST NOT)要求工作负载主动参与其中。

6. 错误码

客户端在与 SPIFFE 工作负载端点交互时,可能会遇到若干错误情形。例如,客户端请求可能遗漏了强制的安全头(更多信息请参阅“传输”一节),或者 SPIFFE 工作负载端点实现可能仍在初始化中或因其他原因不可用。

收到不包含强制安全头的客户端请求时,实现必须(MUST)以 gRPC 状态码 “InvalidArgument” 作出响应。遇到 “InvalidArgument” 状态码的客户端不应(SHOULD NOT)重试,因为这表明客户端实现中出现了错误,且该错误不可恢复。

如果 SPIFFE 工作负载端点实现正在运行但不可用,例如它仍在初始化或正在执行负载卸载(load shedding),客户端将收到 gRPC 状态码 “Unavailable”。收到此状态码的客户端,或者无法抵达 SPIFFE 工作负载端点的客户端,可以(MAY)采用退避策略重试。

最后,如果某个 SPIFFE 工作负载端点服务并未为某个给定的调用方/客户端定义身份,该服务应当(SHOULD)以 gRPC 状态码 “PermissionDenied” 作出响应。收到此状态码的客户端可以(MAY)采用退避策略重试,因为如果服务实现是最终一致(eventually consistent)的,便可能遇到这样的响应。

有关错误情形与状态码的汇总,请参阅附录 A

7. 可扩展性与所提供的服务

SPIFFE 工作负载端点可以暴露多种与身份相关的服务,例如身份签发或身份校验。各项服务是通过使用 gRPC/Protobuf 的服务(service)原语来暴露的。要扩展 SPIFFE 工作负载端点,必须引入一个新的(具有唯一名称的)服务。

由于本规范承诺提供强可移植性,作者们认为允许对现有逻辑服务进行扩展有违 SPIFFE 的精神。倘若通过向现有逻辑服务添加端点来提供额外功能,那么一旦某个依赖此功能的工作负载从一个 SPIFFE 合规环境迁移到另一个,便无法作出可移植性保证。有鉴于此,诸如 SPIFFE 工作负载 API 之类的现有 gRPC 逻辑服务禁止(MUST NOT)被直接扩展。相反,该端点可以通过添加 SPIFFE 规范集合中未曾描述的、相互独立的逻辑服务来加以增强。

尽管所有 SPIFFE 工作负载端点实现都必须(MUST)暴露 SPIFFE 工作负载 API,但有时可能难以知晓在某个给定环境中还支持哪些额外服务。有鉴于此,端点实现者应当(SHOULD)包含对 gRPC 服务端反射(Server Reflection)的支持。如果客户端遇到一个不支持 gRPC 服务端反射的端点,它应当(SHOULD)假定唯一可用的服务就是 SPIFFE 工作负载 API 中所定义的那些。

附录 A. 错误码清单

本节列举了 SPIFFE 工作负载端点实现可能返回的各种错误码、它们可能在何种条件下被返回,以及应当如何处理它们。有关这些状态码的更多信息,请参阅错误码一节以及 gRPC Code 包文档

错误码触发条件客户端行为
InvalidArgument客户端请求中未包含 gRPC 安全头。更多信息请参阅传输一节。报告错误,不要重试。
UnavailableSPIFFE 工作负载端点实现无法处理该请求。采用退避策略重试。
PermissionDenied客户端无权执行所请求的操作。视具体实现而定,这可能表示工作负载在其身份或信任域完成置备之前就已启动。采用退避策略重试。
Unimplemented服务器未实现所请求的 RPC。报告错误,不要重试。

内容基于 SPIFFE/SPIRE 官方开源资料整理与翻译,仅供学习交流;商标与版权归各自所有者。SPIFFE 与 SPIRE 是 CNCF 项目。