本文为 SPIFFE 官方规范 The SPIFFE Trust Domain and Bundle 的中文译本,仅供学习参考;若与英文原文有出入,以英文版为准。
SPIFFE 信任域与证书包(The SPIFFE Trust Domain and Bundle)
稳定性:稳定(Stable) - 参见 STABILITY.md。
本备忘录的状态
本文档为互联网社区规定了一项身份文档标准,并征求对其改进的讨论与建议。本文档的分发不受限制。
摘要
SPIFFE 标准为一个框架提供了规范,该框架能够跨异构环境与组织为服务引导(bootstrap)并颁发可互操作的身份。它定义了一个称为 "信任域(trust domains)" 的概念,用于划定管理和/或安全边界。信任域隔离各颁发机构(issuing authorities)并区分身份命名空间,但也可以松散地耦合起来以提供联邦(federation)。
本文档描述 SPIFFE 信任域的语义、它们如何表示,以及它们彼此耦合所依据的机制。
1. 引言
SPIFFE 信任域(trust domain)是限定 SPIFFE ID 的基础,表明任一给定 SPIFFE ID 是在哪个领域或权威(authority)之下颁发的。信任域由颁发机构(issuing authority)作为支撑,该机构负责管理其对应信任域内 SPIFFE 身份的签发。虽然信任域的名称只是一个简单的、人类可读的字符串,但同样有必要表达出该信任域颁发机构所使用的加密密钥,以便他方能够验证其所颁发的身份。这些密钥以 "SPIFFE 证书包(SPIFFE bundle)" 的形式表达,并与其所代表的信任域紧密相随。
本规范定义了 SPIFFE 信任域与 SPIFFE 证书包二者的性质与语义。
2. 信任域
SPIFFE 信任域是一个身份命名空间,由拥有一组加密密钥的颁发机构(issuing authority)作为支撑。这些密钥共同充当该信任域内所有身份的加密锚点(cryptographic anchor)。
信任域与支撑它的密钥之间是 1:N 的关系。单个信任域可以由多个密钥以及多种密钥类型来表示。例如,前者(多个密钥)可在根密钥轮换(root rotation)期间使用,而后者(多种密钥类型)则是在使用了不止一种 SVID 类型时避免多协议攻击(multi-protocol attacks)所必需的。
需要注意的是,虽然可以在多个信任域之间共享加密密钥,但我们强烈建议每个权威密钥只在单个信任域中使用。密钥复用会削弱信任域隔离(例如预发布环境与生产环境之间的隔离),并带来额外的安全挑战(例如需要为二级颁发者建立名称约束系统)。关于该主题的更多信息,请参见安全考量(Security Considerations)一节。
关于信任域命名空间如何表示的更多信息,请参见 SPIFFE ID 规范的第 2 节。
3. SPIFFE 证书包
SPIFFE 证书包是一个包含某信任域加密密钥的对象。证书包中的密钥被视为对其所代表的信任域具有权威性,用于证明位于该信任域中的 SVID 的有效性。
SPIFFE 证书包设计用于 SPIFFE 控制平面(control plane)实现的内部以及彼此之间。它们并非意在供工作负载(workload)直接消费,但本规范并不排除此类用法。
在存储或以其他方式管理 SPIFFE 证书包时,务必独立记录该证书包所代表的信任域名称,通常通过使用 SPIFFE 证书包映射(SPIFFE Bundle Map) 或以 <trust_domain_name, bundle> 元组的形式实现。在验证 SVID 时,验证方必须选择与该 SVID 所在信任域相对应的证书包,因此维护这一对应关系是必需的。
请注意,随着信任域证书包中所含密钥的轮换,其内容预期会随时间发生变化。密钥的添加与吊销是通过颁发一个新的证书包来完成的:新证书包中纳入新密钥,并省略已吊销的密钥。按需向工作负载分发证书包内容更新是 SPIFFE 实现的职责。这些更新以何种确切格式和方法交付,不在本规范范围之内。关于向工作负载交付更新的信息,请参见 SPIFFE Workload API 规范。
4. SPIFFE 证书包格式
SPIFFE 证书包表示为符合 RFC 7517 的 JWK Set。选择 JWK 主要有两个原因。其一,它提供了一种灵活的格式来表示各类加密密钥(以及诸如 X.509 之类的文档),从而在未来定义新的 SVID 格式时提供一定程度的前瞻兼容性。其二,它得到了广泛的支持与部署,主要用于跨域联邦(inter-domain federation),而这正是 SPIFFE 项目的核心目标之一。
4.1. JWK Set
本节定义 JWK Set 的各项参数。此处未定义的参数,实现者可以(MAY)按需自行加入,但 SPIFFE 实现禁止(MUST NOT)将这些参数的存在作为其正常运作的前提。
4.1.1. 序列号
应当(SHOULD)设置参数 spiffe_sequence。SPIFFE 控制平面可将该序列号用于多种用途,包括传播测量以及更新排序/取代。当该参数存在时,其值必须(MUST)是一个单调递增的整数,并且每当证书包内容更新时都必须随之改变。
需要注意的是,尽管 JSON 的整数类型是可变宽度、未定义最大上限的,但许多实现可能会将其解析为固定宽度的类型。应当注意确保所得到的类型至少具有 64 位精度,以缓解溢出问题。
4.1.2. 刷新提示
应当(SHOULD)设置参数 spiffe_refresh_hint。刷新提示表明消费方应当多久回查一次更新。证书包发布方可以根据其密钥轮换(key rotation)频率来公布刷新提示。还需注意的是,刷新提示同样会影响密钥撤除(key redaction)的传播速度。当设置该参数时,其值必须(MUST)是一个整数,表示建议消费方采用的刷新间隔(以秒为单位)。顾名思义,该刷新间隔仅仅是一个提示,消费方可以根据具体实现或多或少地频繁检查更新。
4.1.3. 密钥集(Keys)
参数 keys 必须(MUST)存在。其值是一个 JWK 数组。客户端遇到未知的密钥类型或用途时,必须忽略相应的 JWK 元素。关于 keys 参数语义的更多信息,请参见 RFC 7517 的第 5 节。
keys 参数可以包含一个空数组。当某信任域发布空的密钥数组时,即表明该信任域已吊销此前发布过的所有密钥。客户端也可能遇到这样的证书包:经处理后未产生任何可用密钥(即没有任何 JWK 通过下文所述的验证),因而实际上是空的。这可能表明该信任域已迁移到某种客户端无法理解的新密钥类型或用途。在上述两种情形下,工作负载都必须(MUST)将来自该信任域的所有 SVID 视为无效且不受信任。
4.2. JWK
本节定义作为 JWK Set 一部分而包含在内的 JWK 元素的高层要求。每个 JWK 元素代表一个单独的加密密钥,用于对单一类型的 SVID 进行认证。虽然安全使用某个 JWK 的确切要求因 SVID 类型而异,但仍有一些高层要求,我们在本节中加以概述。SVID 规范必须(MUST)为 use 参数定义合适的取值(参见下文的 Public Key Use(公钥用途)一节),并且可以(MAY)按需对其 JWK 元素施加进一步的要求或限制。
实现者不应(SHOULD NOT)包含既未在此处、也未在相应 SVID 规范中定义的参数。
4.2.1. 密钥类型
参数 kty 必须(MUST)设置,其行为遵循 RFC 7517 的第 4.1 节。客户端遇到未知的密钥类型时,必须忽略整个 JWK 元素。
4.2.2. 公钥用途
参数 use 必须(MUST)设置。其值表明该密钥对哪种类型的身份文档(或 SVID)具有权威性。在撰写本文时,仅支持两种 SVID 类型:x509-svid 和 jwt-svid。这些取值区分大小写。关于 use 取值的更多信息,请参见相应的 SVID 规范。客户端遇到 use 参数缺失或 use 取值未知的情形时,必须忽略整个 JWK 元素。
5. SPIFFE 证书包映射
SPIFFE 证书包映射(SPIFFE Bundle Map)是一个包含一组 SPIFFE 证书包的对象。它所包含的证书包被视为对相应的信任域名称具有权威性,该信任域名称也一并包含在内。
SPIFFE 证书包映射设计用于 SPIFFE 控制平面实现之间、SPIFFE 实现与其所服务的工作负载之间,以及内部使用(即作为 SPIFFE 控制平面的实现细节)。
5.1. SPIFFE 证书包映射格式
SPIFFE 证书包映射是 JSON 编码的结构,封装了一个或多个 SPIFFE 证书包以及一些附加元数据。完整的 SPIFFE 证书包映射示例,请参见附录 B. SPIFFE 证书包映射示例。
5.1.1. 信任域
必须(MUST)设置一个静态的 "trust_domains" 键,该键可以(MAY)为空。其内容是一个以信任域名称为键的 SPIFFE 证书包映射表。关于何为有效的信任域名称的更多信息,请参见 SPIFFE ID 规范的第 2 节。
SPIFFE 证书包映射的生产方必须(MUST)确保在给定的 "trust_domains" 元素内各信任域名称的唯一性。只要底层库有能力检测此类重复,消费方就必须(MUST)拒绝其中检测到重复信任域名称键的映射。更多信息请参见安全考量一节。
SPIFFE 证书包映射的生产方应当(SHOULD)从映射所含的各证书包中省略 "refresh_hint" 键,因为刷新提示适用于单个证书包与整个集合。SPIFFE 证书包映射的消费方禁止(MUST NOT)根据映射中任一单独证书包内所存在的 "refresh_hint" 键的值来改变 SPIFFE 证书包映射的刷新行为。
在映射中包含哪些证书包由具体实现决定。这可能是部署所知晓的全部证书包,也可能是根据消费方的身份及使用场景量身定制的响应。具体如何确定不在本文档范围之内。
6. 安全考量
本节概述在实现和部署 SPIFFE 控制平面时应当考虑的与安全相关的事项。
6.1. SPIFFE 证书包刷新提示
SPIFFE 证书包包含一个可选的 refresh_hint 字段,用于表明消费方应当尝试刷新其证书包副本的频率。该值对密钥能以多快的速度轮换有明显影响,同时也影响密钥能以多快的速度被撤除。设定刷新提示的值时应当仔细权衡这一点。
由于该字段并非强制,因此有可能遇到未设置 refresh_hint 的 SPIFFE 证书包。在这种情况下,客户端可以选择通过考察 SVID 的有效期来采用一个合适的间隔。必须认识到,省略 refresh_hint 很可能会影响信任域快速吊销已泄露密钥的能力。客户端应当默认采用一个相对较低的刷新间隔(例如五分钟),以便能够及时获取更新后的信任域证书包。
6.2. 跨信任域复用加密密钥
本规范不鼓励跨信任域共享加密密钥,因为这种做法会削弱信任域隔离并带来额外的安全挑战。当某个根密钥(root key)在多个信任域之间共享时,以下两点就变得至关重要:认证与授权实现要仔细检查身份中的信任域名称部分,并且信任域名称部分能够在授权策略中被轻松且习惯性地表达出来。
假设某个欠考虑(naïve)的实现导入了(即完全信任)某个特定的根密钥,并且认证系统被配置为对任何可链式回溯到该受信根密钥的 SVID 的 SPIFFE 身份进行认证。如果该欠考虑的实现没有被配置为只信任某个特定的信任域,那么在任何信任域中颁发的任何身份都可能通过认证(只要该 SVID 能够链式回溯到受信的根密钥)。
延续上面欠考虑的实现导入某个特定 CA 证书的例子,假设认证过程没有对信任域加以区分,任何能够链式回溯到某个受信根密钥的 SVID 都被认证系统接受。那么,只授权特定信任域的责任就落到了授权系统身上。换言之,授权策略需要被显式地配置为检查 SVID 中的信任域名称部分。这里的安全隐患在于,一个欠考虑的授权实现可能会盲目地相信认证系统已经过滤掉了不受信任的信任域。
总而言之,一种纵深防御的最佳实践是在信任域与根密钥之间维持一一对应的映射,从而减少那些微妙(却可能是灾难性)的认证与授权实现错误。确实在跨信任域复用根密钥的系统应当确保:(a) 签发 SVID 的系统(例如 CA)在签发 SVID 之前正确地实现了授权检查;(b) 依赖方(即消费 SVID 的系统)正确地实现了健壮的、能够区分多个信任域的认证与授权系统。
6.3. JSON 键重复
作为 JSON 数据格式标准的 RFC 8259 规定,一个对象内的名称应当(SHOULD)是唯一的。此外,它强调当 JSON 对象内的名称不唯一时,接收该对象的软件的行为是不可预测的。当使用 JSON 格式来表示 SPIFFE 证书包映射时,这种行为会带来不可接受的安全风险,因为重复信任域名称的存在可能导致在验证 SVID 时选中了错误的证书包。因此,解析 SPIFFE 证书包映射的库采用重复键检测这一点很重要。
附录 A. SPIFFE 证书包示例
在以下示例中,我们为名为 example.com 的信任域配置一个初始的 SPIFFE 证书包,然后演示该证书包在根密钥轮换(root key rotation)期间是如何更新的。
信任域 example.com 的初始 X.509 CA 证书:
Certificate #1:
Data:
Version: 3 (0x2)
Serial Number:
df:d0:ad:fd:32:9f:b8:15:76:f5:d4:b9:e3:be:b5:a7
Signature Algorithm: sha256WithRSAEncryption
Issuer: O = example.com
Validity
Not Before: Jan 1 08:00:45 2019 GMT
Not After : Apr 1 08:00:45 2019 GMT
Subject: O = example.com
X509v3 extensions:
X509v3 Key Usage: critical
Certificate Sign
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Alternative Name:
URI:spiffe://example.com/
[...]请注意,该证书:
- 是自签名的(颁发者 Issuer 与主体 Subject 相同);
- 将 CA 标志设为 true;
- 并且是一个 SVID(带有 spiffe URI SAN)。
example.com 对应的信任域证书包:
Trust bundle #1 for example.com:
{
"spiffe_sequence": 1,
"spiffe_refresh_hint": 2419200,
"keys": [
{
"kty": "RSA",
"use": "x509-svid",
"x5c": ["<base64 DER encoding of Certificate #1>"],
"n": "<base64urlUint-encoded value>",
"e": "AQAB"
}
]
}如 spiffe_sequence 字段所示,上述信任域证书包为第 1 版,并表明客户端应当每 2419200 秒(即 28 天)轮询一次该信任域证书包的更新。请注意,x5c 参数包含按 RFC7517 第 4.7 节 所规定的、经 base64 编码的 DER 证书。密钥专有值(例如 n 和 e)的编码方法在 RFC7518 第 6 节 中有描述。
为应对 example.com 的 CA 证书最终过期,生成了一份替换证书并将其添加到信任域证书包中:
Certificate #2:
Data:
Version: 3 (0x2)
Serial Number:
a4:dc:5f:05:8a:a2:bf:88:9d:a4:fa:1e:9a:a5:db:74
Signature Algorithm: sha256WithRSAEncryption
Issuer: O = example.com
Validity
Not Before: Feb 15 08:00:45 2019 GMT
Not After : Jul 1 08:00:45 2019 GMT
Subject: O = example.com
X509v3 extensions:
X509v3 Key Usage: critical
Certificate Sign
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Alternative Name:
URI:spiffe://example.com/
[...]于 2 月 15 日发布的 example.com 更新后的信任域证书包:
Trust bundle #2 for example.com:
{
"spiffe_sequence": 2,
"spiffe_refresh_hint": 2419200,
"keys": [
{
"kty": “RSA”,
"use": "x509-svid",
"x5c": ["<base64 DER encoding of Certificate #1>"],
"n": "<base64urlUint-encoded value>",
"e": "AQAB"
},
{
"kty": “RSA”,
"use": "x509-svid",
"x5c": ["<base64 DER encoding of Certificate #2>"],
"n": "<base64urlUint-encoded value>",
"e": "AQAB"
}
]
}在信任域证书包 #2 中,请注意 spiffe_sequence 参数已递增,并且 example.com 的第二份根证书已被添加进来。一旦这个新的信任域证书包被发布和分发,验证方将接受由原始根证书或替换根证书签名的 SVID。通过在原始证书过期之前充分提前地发布替换证书,验证方便有充足的机会刷新 example.com 的信任域证书包并获知即将启用的替换证书。
附录 B. SPIFFE 证书包映射示例
以下是一个 SPIFFE 证书包映射的样例:
{
“trust_domains”: {
"example.com": {
“spiffe_sequence”: 12035488,
"keys": [
{
"kty": "RSA",
"use": "x509-svid",
"x5c": ["<base64 DER encoding of Certificate #1>"],
"n": "<base64urlUint-encoded value>",
"e": "AQAB"
},
{
"kty": "RSA",
“kid”: “<JWT key id>”,
"use": "jwt-svid",
"n": "<base64urlUint-encoded value>",
"e": "AQAB"
}
]
}
}
}附录 B.1. 服务端/客户端行为
SPIFFE 证书包映射被设计为以原子方式加载和摄取。其全部内容应当由消费方一次性加载,并在此刻被视为 "世界的当前状态(state of the world)"。如果需要测量传播延迟、确认存活性等,消费方应当使用相关证书包的 "spiffe_sequence",因为许多故障模式都可能导致映射中所表示的各证书包之间出现存活性上的分歧。