SPIFFE 工作负载端点

查看本文大纲

对于网络工作负载而言,可移植且互操作的加密身份可能是 SPIFFE 的核心用例之一。为了完全满足这一要求,社区必须达成一致,以标准化检索身份和在运行时使用身份相关服务的方式。

SPIFFE 工作负载终端点规范通过定义一个终端点来提供 SPIFFE 可验证身份文档(SVIDs)和相关服务。具体而言,它概述了如何定位终端点以及如何服务或使用它。这个终端点所暴露的服务集合超出了本文档的范围,但有一个例外,即 SPIFFE 工作负载 API。

引言

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

本文档详细介绍了 SPIFFE 工作负载终端点的可访问性和范围、传输协议、身份验证过程以及可扩展性/发现机制。

可访问性

SPIFFE 工作负载终端点通常用作初始身份引导的机制,包括传递和管理信任根的过程。由于在早期阶段,工作负载可能对自己的身份或应该信任的对象没有任何先验知识,因此很难确保对终端点的访问安全。因此,SPIFFE 工作负载终端点应通过本地终端点公开,并且实现者不应将同一终端点实例公开给多个主机。将终端点和相关流量限制在单个主机上可以减轻与初始身份验证和签发安全相关的引导问题。更多详细信息,请参见 传输 和 身份验证 部分。

传输

SPIFFE 工作负载终端点必须通过 gRPC 进行提供,并且符合规范的客户端必须支持 gRPC。它可以作为 Unix 域套接字(Unix Domain Socket,UDS)或 TCP 监听套接字公开。实现者应优先选择 Unix 域套接字传输,但如果 Unix 域套接字不可行或不可能,也可以支持 TCP 传输。除非底层网络允许工作负载终端点服务器根据源 IP 地址(例如通过本地主机或链路本地网络)或其他强网络级断言(例如通过 SDN 策略)对工作负载进行强身份验证,否则不得使用 TCP 传输。

为了防止 服务器端请求伪造(SSRF)攻击,每个客户端请求 SPIFFE 工作负载终端点时,都必须包含静态的 gRPC 元数据键 workload.spiffe.io,其值为 true(区分大小写)。未包含此元数据键/值的请求必须被 SPIFFE 工作负载终端点拒绝(有关详细信息,请参见 错误代码 部分)。这样可以防止攻击者利用 SSRF 漏洞访问 SPIFFE 工作负载终端点,除非该漏洞还使攻击者能够控制出站 gRPC 元数据。

传输安全

尽管 gRPC 强烈推荐使用传输层安全(Transport Layer Security,TLS),但不得要求 SPIFFE 工作负载终端点。由于 SPIFFE 工作负载终端点通常传递和管理信任根,我们不能指望工作负载具有对活跃根的先进知识。因此,在早期阶段,工作负载可能无法验证所呈现身份的真实性,除非通过 Workload API 实现的特权位置。这是 SPIFFE 工作负载终端点实例不应公开给多个主机的另一个原因。有关更多信息,请参见 身份验证 部分。

定位终端点

客户端可以显式配置套接字位置,也可以使用名为 SPIFFE_ENDPOINT_SOCKET 的众所周知的环境变量。如果没有显式配置,符合规范的客户端必须回退到环境变量。

SPIFFE_ENDPOINT_SOCKET 环境变量的值结构化为 RFC 3986 URI。方案(scheme)必须设置为 unixtcp,分别表示终端点通过 Unix 域套接字或 TCP 监听套接字提供服务。

如果方案设置为 unix,则授权组件不得设置,路径组件必须设置为 SPIFFE 工作负载终端点 Unix 域套接字的绝对路径(例如 unix:///path/to/endpoint.sock)。方案和路径组件是强制的,不得设置其他组件。

如果方案设置为 tcp,则授权的主机组件必须设置为 IP 地址,授权的端口组件必须设置为 SPIFFE 工作负载终端点 TCP 监听套接字的 TCP 端口号。方案、主机和端口组件是强制的,不得设置其他组件。例如,tcp://127.0.0.1:8000 是有效的,而 tcp://127.0.0.1:8000/foo 是无效的。

身份验证

SPIFFE 工作负载终端点通常用作初始身份引导的机制。因此,预期工作负载没有任何可用于自身身份验证的“秘密”材料。为了适应这一非常重要的用例,SPIFFE 工作负载终端点不得要求直接对其客户端进行身份验证。

实现者应该执行带外真实性检查,而不是直接的客户端身份验证。这可以包括内核检查或编排工具询问等技术。例如,可以通过检查内核套接字状态来了解调用 API 的进程是哪个。另一种方法是允许编排工具将 Unix 域套接字放入特定容器中,向 SPIFFE 工作负载终端点实现传递容器的属性/身份信息。然后可以将此信息用作身份验证机制。

应注意,虽然如何实现这一点的方法是特定于实现的,但所选择的方法不得要求工作负载积极参与其中。

错误代码

在与 SPIFFE Workload 端点交互时,客户端可能会遇到多种错误条件。例如,客户端请求可能省略了必需的安全头部(请参阅传输部分获取更多信息),或者 SPIFFE Workload 端点实现可能仍在初始化或无法使用。

如果收到不包含必需安全头部的客户端请求,实现必须使用 gRPC 状态码 “InvalidArgument” 进行响应。如果客户端收到 “InvalidArgument” 状态码,不应重试,因为这表示客户端实现有误,不可恢复。

如果 SPIFFE Workload 端点实现正在运行但不可用,例如仍在初始化或执行负载均衡,客户端将收到 gRPC 状态码 “Unavailable”。如果客户端收到这个状态码,或者无法到达 SPIFFE Workload 端点,可以使用指数退避重试。

最后,如果给定调用者/客户端没有为 SPIFFE Workload 端点服务定义身份,服务应使用 gRPC 状态码 “PermissionDenied” 进行响应。如果客户端收到这个状态码,可以使用指数退避重试,因为在实现最终一致性的情况下可能会遇到此类响应。

请参阅 附录 A 获取错误条件和代码的摘要。

可扩展性和提供的服务

SPIFFE Workload 端点可以提供多种与身份相关的服务,例如身份发放或身份验证。通过使用 gRPC/Protobuf 服务原语来公开单个服务。为了扩展 SPIFFE Workload 端点,必须引入一个新的(唯一命名的)服务。

由于本规范承诺提供强大的可移植性,作者认为允许扩展现有逻辑服务与 SPIFFE 的精神相悖。如果通过向现有逻辑服务添加端点来提供附加功能,那么在从一个符合 SPIFFE 的环境移动到另一个环境时,无法保证可移植性。因此,不能直接扩展现有的 gRPC 逻辑服务,如 SPIFFE Workload API。相反,可以通过添加 SPIFFE 规范集中未描述的独立逻辑服务来增强端点。

虽然所有 SPIFFE Workload 端点实现都必须公开 SPIFFE Workload API,但有时很难知道给定环境中支持哪些附加服务。因此,端点实现者应该包含对 gRPC Server Reflection 的支持。如果客户端遇到不支持 gRPC Server Reflection 的端点,应假设唯一可用的服务是 SPIFFE Workload API 中定义的那些。

附录 A. 错误代码列表

本节列出了 SPIFFE Workload 端点实现可能返回的各种错误代码、返回条件以及如何处理它们。请参阅 错误代码 部分和 gRPC Code package 文档 以获取有关这些代码的更多信息。

代码 条件 客户端行为
InvalidArgument 客户端请求中未包含 gRPC 安全头部。请参阅 传输部分 获取更多信息。 报告错误,不要重试。
Unavailable SPIFFE Workload 端点实现无法处理请求。 使用指数退避重试。
PermissionDenied 客户端无权执行请求的操作。根据实现的情况,这可能表示工作负载在身份或信任域被配置之前就已启动。 使用指数退避重试。