先把场景钉死,不谈大厂平台,也不谈服务网格。你从机房租了两台物理机,一台跑网关和六个业务进程,另一台跑数据库、队列、定时任务和几个内部接口,加起来三十来个服务,全部靠本机回环地址和内网地址互相调用。某天安全评审问一句:这批服务之间,凭什么相信对面是它自己?多数团队答不上来,因为真实答案往往是"共用一张内网证书"或者"请求头里带一个写死在配置文件里的 token"。
这篇文章就围着这一台租来的服务器写。SPIFFE 是一套规范,SPIRE 是它的参考实现,两者在 2022 年 9 月一起从 CNCF 毕业(2018 年进沙箱,2020 年进孵化)。但毕业不等于该上,我见过太多团队把 SPIRE 当成了"装完就零信任"的开关,结果 Server 单点跑在办公网一台旧机器上,Agent 没人托管,注册条目写在一张便利贴里。下面按"问题—规范边界—部署形态—证明链—取舍—运维—成本—避坑"的顺序讲清楚,中间会给出几个你在自己机器上五分钟就能复现的验证动作。
失效的方式其实只有四类,但每一类都会在你最忙的时候爆。第一类是过期:一年前某人用脚本签了张证书,压在镜像里,到期那天凌晨三点全线握手失败,而当初签的人早就不在了。第二类是泄露:token 写在配置中心、打进镜像层、进过一次 git 历史,之后无论怎么改,历史里的那一份永远有效,因为它是长期凭据。第三类是人员流动:签发私钥在某台跳板机的 home 目录下,持有者离职,你既不敢删也不想留。第四类最难受——无法按工作负载粒度吊销。八个服务共用一张服务端证书,其中一个被怀疑有问题,吊销就是把八个一起停掉,于是没人愿意走这个流程,风险就一直挂着。
手工管理为什么必然失控?算一下就清楚了。三十个服务、三套环境、双向 mTLS,就是接近两百张证书,每次轮换都是一次变更窗口,还要同步更新信任包。更麻烦的是"每个服务一张证书"这个正确做法,恰恰是手工时代最做不到的:证书一多,人们会本能地合并,合并之后粒度就丢了,粒度一丢,出问题时的处置就只剩"全停"这一个选项。这不是纪律问题,是工具的边际成本问题。
共享 token 的性质更差。它是持有者凭据,谁拿到谁就是那个服务,服务端没有任何办法区分"真的是订单服务"和"某个进程读到了那个字符串"。所以判断一套身份体系靠不靠谱,我一般只看两件事:凭据有没有和具体进程绑定,以及轮换是不是不需要人参与。这两件事同时满足,剩下的才是工程细节。
顺带说一句现实约束:很多中小团队的机器是租来的,一台机器上跑几十个进程是常态,运维人数个位数。任何需要"每次上线手动做一步"的方案,在这样的人机比下都活不过半年。这也是我倾向于把身份体系做成平台能力而不是流程规范的原因,哪怕平台本身要花几周搭。
SPIFFE 是一组规范文档,核心只规定四件事。其一是身份怎么命名:SPIFFE ID 是一个 URI,形如 spiffe://信任域/工作负载标识,比如 spiffe://acme.com/billing/payments,前面一段是信任域,后面一段在域内唯一。其二是身份怎么装进可验证的凭证里:SVID(SPIFFE 可验证身份文档)支持两种格式,X509-SVID 是一张携带 SPIFFE ID 的 X.509 证书,JWT-SVID 是一个携带同样 ID 的 JWT。其三是身份怎么取:Workload API 提供 SVID、私钥和信任包,调用方不需要自带任何认证凭据。其四是不同信任域之间怎么互认:靠交换公开的信任包,也就是联邦。
两种 SVID 的分工要分清。X509-SVID 用在 mTLS 握手里,对端在 TLS 层就验完了身份,这是主力形态。JWT-SVID 用在握手会被中断的场景——两个服务之间隔着七层代理或负载均衡,mTLS 在代理处就终结了,只能把身份断言塞进请求里往下传。规范自己的建议很明确:能用 X509-SVID 就用,因为 JWT 是持有者凭据,在传输途中被拿到就能被重放。这条建议值得抄进你们的设计规范里,别为了省事全站 JWT。
更要紧的是 SPIFFE 不管什么,这几点搞混了会直接导致项目返工。它不做授权决策:拿到 spiffe://acme.com/billing/payments 这个身份,只说明对面确实是账单服务,不代表它能调支付接口,"谁能调谁"是策略引擎或者业务自己的事。它不替代 IAM:人的身份、云厂商的账号体系、运维权限,是另一套东西,SPIFFE 只管工作负载。它不下发加密配置:TLS 版本、套件、要不要强制校验证书,得你自己在服务端和客户端配。它不存机密:数据库口令、第三方 API 密钥这类东西该放密钥管理系统还是放密钥管理系统,SVID 只是让服务能证明自己是谁,从而有资格去换取那些机密。规范还明确把"工作负载之间的隔离强度"列为范围外——它假设同一个节点上的工作负载已经隔离到无法互相偷凭据,至于这个隔离是容器、虚拟机还是 Unix 权限实现的,它不管,你自己保证。
把这条边界写进设计文档,能省掉后面无数的口水。我见过最典型的扯皮是:上了 SPIRE 之后,业务方说"我们零信任了",安全方说"你的服务还是能调所有接口"。两边都对,因为前者只解决了身份,后者要的是授权。
SPIRE 的组件就三个,理解清楚比背配置重要。SPIRE Server 是一个信任域的签发机构,保存注册条目、保存签名密钥、验证节点、给通过验证的 Agent 签 SVID,业务进程永远不直接访问它。SPIRE Agent 跟着机器走,一台物理机、一台虚机、一个 Kubernetes 节点上跑一个,负责向 Server 证明本机身份、缓存本机有权的注册条目、在本机暴露 Workload API、识别连进来的进程并下发 SVID。Workload API 是业务唯一需要打交道的东西。
Server 的部署有三条硬约束。一是存储:SPIRE 的 DataStore 已经不再支持插件化,只能用内置 SQL,后端可选 SQLite、PostgreSQL、MySQL,官方把持久化和高可用都归到 DataStore 这个组件上。SQLite 只适合单机验证,真要两台以上 Server 就上 PostgreSQL 或 MySQL。二是密钥:KeyManager 可选 disk(私钥落盘)、memory(不落盘,重启后必须重新证明)、aws_kms(交给 KMS)。三是监听:Server 默认端口 8081,Agent 侧要能稳定连上它,跨机房时这条链路的抖动会被放大。
在租用机型上排布时,我一般给的建议是:Server 单独占机,不与业务混部,至少两台;数据库那台内存给宽一点,条目数和节点数上去了之后它是第一个吃紧的;Agent 与业务同机,不增加任何机器成本。如果你只有两台机器,那就 Server 与数据库各占一台,Agent 全部署在业务机上——这个拓扑能跑,但没有高可用,属于过渡形态。像一万网络这类机房提供的机型,从入门配置到双路大内存机型都有得选,Server 这类组件对 CPU 要求不高,内存和磁盘可靠性反而更要紧。
还有一个常被忽略的点:Server 的 CA 默认 TTL 是 24 小时,也就是说签名密钥自己会滚,不需要人去操心十年根证书那种事。这带来一个反直觉的结论——你真正需要备份的不是某把密钥,而是 Datastore 里的注册条目和已证明节点信息,以及一套能在 Server 全损时重新拉起的配置。密钥丢了可以重新签,条目丢了等于整个身份体系的定义没了。
Workload API 的设计是整个体系里最巧妙的一环。它是一个 gRPC 接口,默认绑在 Unix 域套接字上(SPIRE Agent 的默认路径是 /tmp/spire-agent/public/api.sock,Windows 下是命名管道),业务进程连上来,不需要携带任何 token、密码或者证书。那服务端怎么知道你是谁?SPIRE 的做法是带外识别:Agent 拿到调用的进程 PID,去内核问这个进程的 uid、gid、可执行文件路径,再去 kubelet 或者 Docker 问命名空间、服务账号、镜像 ID,用这些"原生属性"拼出一组 selector,与缓存的注册条目做匹配。
这个设计的直接后果是:业务进程手上从来没有需要保管的长期私钥。私钥由 Agent 侧的 KeyManager 生成,配成 memory 时连磁盘都不碰(代价是 Agent 重启后必须重新做节点证明),配成 disk 时也只是 Agent 的数据目录,不是业务的配置目录。业务拿到的是一份短期的密钥和证书,以及一份信任包,过期前自己再去 Workload API 取新的。这才是"不用把私钥落盘"的准确含义——不是说磁盘上一个密钥文件都没有,而是没有任何长期密钥需要被分发给业务。这两个表述差得很远,前者会让你在老系统上卡住。
我们可以在自己机器上做个五分钟的验证。在业务机上执行 spire-agent api fetch x509,指定 socket 路径和一个输出目录,就能拿到该进程的 X509-SVID、私钥和信任包;用 openssl 看一下证书的 URI SAN 字段,SPIFFE ID 就在那里。再把这个进程以另一个系统用户身份跑一遍,会发现拿不到身份——因为 selector 不匹配。这个实验能一次性讲明白三件事:身份是从内核元数据推出来的、粒度可以到单个 Unix 用户、以及调用方确实不需要任何 secret。
当然现实里有大量改不了代码的老程序。SPIFFE Helper 这个工具就是给它们准备的:它去 Workload API 取 SVID、私钥和信任包,写到磁盘的指定位置,拉起子进程,盯着过期时间提前刷新,换完给子进程发信号让它重载。这条路能用,但你要清楚代价——私钥确实落盘了。我的处理是写到 tmpfs,绝不写到能进备份、能进快照的持久化卷,并且给这个文件设严格的属主和权限。同理,Envoy 走 SDS 从 Agent 取证书这条路,好处是应用二进制完全不知道 SPIFFE 的存在,坏处是多了一层代理要运维。取舍标准是:能改代码就走 Workload API,改不了但能接受落盘就用 Helper,本来就在用 Envoy 就走 SDS。
证明链是 SPIRE 最有价值也最容易被讲错的部分。顺序是固定的:先节点证明(node attestation),再工作负载证明(workload attestation)。Agent 自己也是个工作负载,它必须先向 Server 证明"我跑在哪台机器上",拿到自己的 SVID,之后才有资格为本机的进程签身份。Server 不信 Agent 的自述,它要的是机器之外有东西替它背书。
三种形态,背书来源完全不同。物理机和独立服务器:最硬的是 TPM 里预置了 DevID 证书的 tpm_devid 插件,退而求其次是用现有 PKI 证书的 x509pop,或者用已有 SSH 证书的 sshpop。云上虚机和云主机:aws_iid 用 AWS 实例身份文档,Server 侧拿着文档去 AWS 接口校验;gcp_iit 用 GCP 实例身份令牌;Azure 侧有 azure_imds 和 azure_msi。这一类共同点是"有个外部控制面替它说话",你不需要在机器上放任何长期凭据。容器与 Kubernetes:k8s_psat 用投影 ServiceAccount 令牌,Server 侧走 TokenReview 校验。
join_token 这个插件必须单独说。它的用法是 Server 生成一个令牌,交给 Agent 用于引导。方便是真方便,但它的性质恰恰是你想消灭的那个东西——一个由人分发给机器的长期凭据。所以它只适合引导阶段和实验室,不要作为生产路径。判断标准很简单:如果你的节点证明过程里有一个"某人把某个字符串复制到了某台机器上"的动作,那就还在老模型里。
节点证明过了之后是工作负载证明,这里决定的是爆炸半径。可用的 selector 包括 Unix 的 uid 和 gid、systemd 的 unit Id 和 FragmentPath、Docker 的 label 和 image_id、Kubernetes 的 namespace 和 service account。注意这些是"能被内核或编排器验证的原生属性",不是进程自己声明的字符串,这是整套体系可信的根基。我一般建议在一台跑几十个服务的物理机上,优先用 systemd unit 或者 gid 来区分进程,粒度足够,也够稳定;用 uid 区分时要小心 root 起的一大堆进程会共用一个标识,等于把粒度又丢了。
还有一个容易踩的细节:一个注册条目只能包含节点 selector 或者工作负载 selector,不能混写。节点侧的条目用 -node 标志创建,它定义的是节点别名——凡满足该条目标签的节点都归到这个别名下,工作负载条目的 parent ID 可以直接指向别名,这样节点扩缩容时不用改工作负载条目。这个设计在物理机上特别有用:给一批用途相同的机器打同一个别名,业务身份与具体机器解耦。
一条注册条目由三部分构成:一个 SPIFFE ID、一组一个或多个 selector、一个 parent ID。Server 只会把某节点有权的那部分条目下发给该节点的 Agent,Agent 缓存起来并保持更新。工作负载证明时,Agent 把从进程身上发现的 selector 与缓存条目比对,命中就下发对应的 SVID。parent ID 可以是某个具体节点的 SVID(比如用 aws_iid 证明后自动得到的 spiffe://example.org/agent/aws_iid/账号/区域/实例ID 这种形式),也可以是一个节点别名。
粒度能到多细,取决于你用哪种 selector。最细可以到"某个 systemd unit 下的这一个进程",最粗可以到"某台机器上的所有进程",中间还有"同一个 gid 的所有进程"、"同一个 Kubernetes 命名空间下的同一个服务账号"。粒度不是越细越好,太细会导致条目爆炸、上线一个新服务就要改一次注册库;太粗则回到共用身份的老路。我的经验值是按"需要单独审计和单独吊销的最小单元"来定——如果一个服务出问题时你会想单独掐掉它,它就该有自己的 SPIFFE ID。
条目本身要版本化。官方给了 entry create、entry show、entry update、entry delete 这一套命令,也有 Entry API 可以程序化调用,但我强烈建议把条目定义写成人可读的文件、放进代码仓库、走评审和 CI 下发。原因很实际:注册条目就是你的身份策略本体,它如果只存在于某台机器的数据库里,那它既不可审计也不可重建。这个动作不需要任何额外工具,就是别偷懒。
信任域是比条目更高一层的边界,它对应一个信任根。规范给出的划分建议很清楚:物理位置不同的(不同机房、不同区域)、安全实践不同的(生产环境与测试、实验室环境),应该放在不同的信任域里。名字长度官方建议不超过 255 字符。信任包(trust bundle)就是验证用的那组 CA 根证书和公钥,它会随 Workload API 一起下发给工作负载,而且内容会频繁轮换——这一点在设计缓存策略时要考虑进去,别把信任包当成一次性的静态文件。
两个信任域要互认,靠的不是合并,而是交换公开的信任包,私钥从头到尾不出各自域。承载这个交换的是 bundle endpoint——一个对外提供本域信任包的 URL。SPIRE 支持两种 profile:https_web 走标准 Web PKI,证书可以走 ACME(比如 Let's Encrypt)自动签发,也可以配置证书文件路径手工管理;https_spiffe 走 SPIFFE 自己的 mTLS,对端出示 X509-SVID,因此必须在配置里用 endpoint_spiffe_id 指定期望的对端 SPIFFE ID。用 https_spiffe 时第一次拉取需要一个引导信任包,配置里对应 bootstrap_bundle_path,它只用于认证这第一次连接,之后用存下来的 bundle 刷新。
选哪个 profile 不难判断。内部 SPIRE 对 SPIRE、双方都在自己的机房里、不需要中间设备看流量,选 https_spiffe,信任模型自洽。要对外暴露给合作方、中间有负载均衡或者 WAF 需要终止 TLS、或者你没有现成的 SPIFFE 体系来引导,选 https_web。要提醒的是,某些发行版把 bundle endpoint 的 profile 做成了一旦启用就不可变更,要改就得重装,所以动手前先想清楚。另外信任包也可以不通过 endpoint 分发,SPIRE 有 BundlePublisher 插件能把它推送到对象存储、Kubernetes ConfigMap、甚至某些云的角色信任锚点,Notifier 插件则能在 bundle 更新时推送事件,这两类插件在多机房场景里比 endpoint 更好用。
网络与链路这一层必须单独算。Agent 到 Server 是一条长期连接加定期同步,Agent 侧默认同步间隔 5 秒,同步失败后的退避参数是:间隔上限为 48 倍同步间隔、封顶 8 分钟,倍数 1.5,抖动 10%。多 Server 地址时 Agent 默认用 round robin 轮询 DNS 返回的结果。这些默认值在小规模部署里够用,但一旦 Agent 与 Server 跨机房,链路抖动的表现形式你要提前知道:它不是让已建立的业务连接掉线,而是新进程拿不到身份、条目更新下不来。所以跨机房部署时,Agent 侧要能稳定访问 Server 的监听端口,链路质量监控要跟 Server 的可用性告警放在一起看,别只监控业务端口。
至于嵌套 SPIRE,那是另一种形态:同一个信任域内部做多级 CA,根 Server 持根 CA,下游 Server 注册为父级的工作负载(启动参数带 -downstream),可以根到区域再到集群层层下去,叶子上签出的身份依然能一路验到根。它解决的是"一个域内规模太大",而联邦解决的是"域与域之间要互认"。别把两者搞混,更别一上来就全上。
把 SPIRE 放回它该在的位置,才能做出正确的取舍。下面这张表按五个维度横向列了五类常见做法,粒度、轮换、成本、故障影响面和适用规模都在里面。表里不写具体产品名,因为同类产品的差异远小于类别之间的差异。
| 方案 | 身份粒度 | 轮换方式 | 运维成本 | 故障时的影响面 | 适用规模 |
|---|---|---|---|---|---|
| 脚本加自签 CA 手工签发 | 到服务或到机器,看人勤快程度 | 人工触发,通常一年一次 | 随服务数线性上升,几十个服务即失控 | 证书过期即全线握手失败 | 十个服务以内 |
| 密钥管理系统自动签发 | 到服务,需要自己定义命名规范 | 自动,但仍需向系统证明调用者身份 | 中等,主要花在调用方接入与令牌管理 | 系统不可用则新签发停滞,已签发继续有效 | 十到上百个服务,跨环境通用 |
| 服务网格自带 mTLS | 到服务账号,跟随编排系统身份 | 自动,由网格控制面负责 | 网格本身的运维成本较高 | 控制面异常时新连接建立受影响 | 已全面容器化且已用网格的团队 |
| SPIRE 加 Workload API | 到单个进程,可由内核元数据界定 | 自动,默认在生命周期过半时轮换 | 前期偏高,Server 高可用是主要工作量 | Agent 异常时已发出身份仍有效到过期,新进程取不到身份 | 几十到上千个服务,混合形态场景 |
| 云厂商原生工作负载身份 | 到实例或到服务账号,由厂商定义 | 自动,厂商托管 | 低,但绑定单一厂商 | 跟随厂商控制面的可用性 | 全部 workload 都在同一家云上 |
判读这张表的方法,不是看哪一行优点多,而是按三个问题过滤。第一个问题:你的工作负载是不是跨形态的——既有租来的物理机,又有云主机,又有容器?只要答案是"是",云厂商原生身份和网格自带 mTLS 就都有覆盖不到的地方,SPIRE 的价值立刻凸显,因为它是唯一能在这三种形态上给出同一套命名和同一套获取方式的方案。第二个问题:需不需要按工作负载粒度审计和吊销?不需要的话,密钥管理系统那条线性价比更高,因为它顺带解决机密存储。第三个问题:身份的消费者是谁?如果只有服务网格在消费,那网格自带的够了;如果数据库、消息队列、内部 API、CI 系统都要认这个身份,SPIFFE 这种中立格式才划算。
还有一条判读经验:看这套方案的"最弱一环落在哪里"。手工签发的最弱一环是人和流程,密钥管理系统的最弱一环是调用方怎么向系统证明自己(这一步往往又变回长期令牌),网格的最弱一环是控制面本身的可用性,SPIRE 的最弱一环是节点证明的强度。你选的不是最好的方案,是选一个你最有能力守住的最弱一环。
说句实话,大部分读到这里的团队其实不该上 SPIRE。判据我给得很具体:服务数量在五个以内、调用关系是星型(全部只调一个中心服务)、全部跑在同一机房同一批机器上、没有外部审计要求、团队里没有专人能花两周以上做平台建设——这五条里满足三条以上,就别上。用一个自签 CA 加一个签发脚本加一个配置中心,把证书和信任包当配置项管理,配合定期轮换的定时任务,够用了。省下来的时间拿去做备份和监控,收益更大。
另一类不值得上的是:全栈已经完全容器化、已经在用服务网格、而且短期没有跨集群跨云的打算。这种情况下网格自带的 mTLS 已经解决了主要问题,SPIRE 的增量主要是"跨集群身份统一"和"非容器工作负载也能接入",如果这两件事你都不需要,就别为了技术先进性引入第二套身份体系——两套身份体系共存的复杂度,比没有身份体系还高。
反过来,值得上的信号也很明确。多机房或者多云,机器形态不统一(物理机、云主机、容器混跑);数据库和中间件需要按服务身份做授权,而不是按 IP 或者按一个共用的账号;客户或者等保评测要你拿得出"某次访问是哪个工作负载在什么时间发起的"这种颗粒度的记录;团队规模在扩张,新服务上线速度开始超过人工流程的跟进速度。这几条里命中两条,SPIRE 的投入就能回本。
还有一个隐性判断:你的机器是租的还是自建的,其实影响不大,真正影响的是这台机器上要跑多少个彼此不信任的服务。租来的物理机因为成本高,往往一台塞得更满,反而比自建小集群更需要按进程粒度的身份。这也是我在这篇文章里一直拿租用服务器当背景的原因。
前面讲了 Server 的三个约束,这里展开成可执行的方案。高可用层面:DataStore 是唯一的高可用承载点,插件化已被取消,内置 SQL 后端在 SQLite 之外还有 PostgreSQL 和 MySQL。两台 Server 共享一个外部数据库是最直接的形态,数据库本身的高可用是另一套工程(主从、备份、故障切换),不要指望 SPIRE 替你解决。Server 前面挂负载均衡时,Agent 侧配置多个 Server 地址即可,默认按 round robin 轮询。
密钥层面:KeyManager 的 disk、memory、aws_kms 三个选项里,生产环境我一般选 disk 或者 KMS,memory 意味着每次重启都要重新做节点证明,在物理机上配合 TPM 还能接受,在云主机上会让重启变成一次变更事件。别忘了 CA 默认 TTL 24 小时这件事——签名密钥是滚动的,所以不要写任何依赖"某把固定密钥"的下游逻辑。如果公司已经有 PKI 体系,可以通过 UpstreamAuthority 接进来,SPIRE 内置支持磁盘 CA、AWS Private CA、GCP CA Service、Vault 的 PKI 引擎、cert-manager 的 Issuer,以及上级 SPIRE Server(也就是嵌套模式)。接不接外部 PKI 的判断标准是:审计上是否需要把身份体系挂到公司已有的根下,是就接,不是就用 SPIRE 自签,别为了"看起来正规"多引入一个依赖。
灾备层面,真正要保的东西按顺序排是这样的:先是注册条目(建议文件化、进代码仓库、CI 下发,这样数据库全损也能重建),再是 Datastore 的常规备份,最后是签名密钥。恢复演练要做一次真的——把测试环境的数据库清空,用仓库里的条目文件重建,看看多久能恢复身份下发。很多团队直到真出事才发现条目只在数据库里有。
审计别漏掉。Server 的 audit_log_enabled 默认是 false,需要显式打开。没有审计日志的身份体系,在合规评审面前等于没做,因为你说不清"谁在什么时候以什么身份取了什么身份"。日志往哪儿送、留多久,按你们的数据留存规定来,这里不展开。
信任域的划分我也给个实操建议:生产、预发、实验环境分开,这是规范明确建议的;跨机房是否分域,取决于运维是否是同一批人、安全基线是否一致。同一批人管、同一套基线,就用一个域,多机房靠联邦或者干脆不分;两批人管、基线不同,就该分域,然后通过联邦互认。分域的代价是每次跨域调用都要多验一层 bundle,不要为了组织结构的整洁而过度拆分。
先说轮换参数,因为多数故障都源于把它们设错了。Server 侧默认值是:X509-SVID 默认 TTL 1 小时,JWT-SVID 默认 TTL 5 分钟,CA 与签名密钥默认 TTL 24 小时,Agent SVID 的 TTL 跟随 X509-SVID 的默认值。Agent 侧的轮换策略是:默认在生命周期过半时发起轮换;如果你设了 availability_target(表示希望优雅应对 Server 或 Agent 停机的时长,最小 24 小时),则在剩余寿命达到该值时轮换,并且要求宽限期(寿命减去 availability_target)不小于 12 小时,否则退回按生命周期一半轮换的策略。
这组数字直接决定你的 TTL 该怎么定。TTL 1 小时、默认过半轮换,意味着正常情况下每半小时换一次;如果你把 TTL 压到 5 分钟想"更安全",那么时钟只要差 2 分钟,有效窗口就被吃掉四成,换成大面积握手失败。所以我的建议是:TTL 保持默认的 1 小时量级,把精力放在时钟上。时钟这件事没有讨价还价的余地,X.509 校验收的是有效期,NTP 必须可靠、必须有偏移告警,虚拟机上的时钟漂移比物理机更常见,租来的云主机尤其要盯。
四类故障的处置方法,按发生频率排。
其一,Agent 挂了。已发出的 SVID 在有效期内照样能用,所以正在跑的业务连接不会立刻断,但新起的进程、需要续期的服务会拿不到身份,表现为"重启之后起不来"而不是"运行中报错"。处置:Agent 必须由 systemd 托管、开机自启、崩溃自动拉起,并且把 Agent 的健康检查纳入业务机器的基础监控,与 CPU、磁盘同级。这台机器重装系统之后,Agent 需要重新做节点证明,如果用的是 join_token,就要重新走一遍人工分发——这就是为什么生产不该用 join_token。
其二,Server 挂了。Agent 侧有缓存的条目和已发出的 SVID,短期内业务无感;同步失败会按退避策略重试,间隔上限封顶 8 分钟。真正的问题是这段时间你改不了注册条目,新服务上不了线。处置:Server 至少两台、前面有负载均衡、有独立的存活与就绪检查(Server 支持开启健康检查端点),并且把"注册条目下发失败"做成一条独立告警,别等到有人上线新服务才发现。
其三,数据库挂了。条目读不出来,Agent 拿不到授权条目,SVID 续签会失败,故障表现与 Server 挂掉类似但恢复更慢。处置:数据库按独立组件对待,做备份与故障切换演练,别因为它"只是 SPIRE 的存储"就降低运维等级。
其四,信任包没换过来。表现为跨信任域或者跨 Server 轮换之后,部分服务突然验不了对端,而且是渐进式的——先是一部分,然后越来越多。处置:确认端点的刷新提示是否合理(SPIRE 里对应 refresh_hint,不设置时会根据 bundle 中密钥的寿命推算),确认联邦端点可达,确认中间没有设备缓存了旧的 bundle 响应。这一类故障最容易被误判成"证书问题",实际是信任包同步问题。
你可以在测试环境里把这几类故障各演一遍,每类不超过半小时,收益超过读十篇文档。具体做法:kill 掉 Agent 进程观察已建连接与新连接的行为差异;把测试机的时钟往前拨几分钟观察握手失败的时间点;把 Server 的监听端口在防火墙上临时封掉观察 Agent 的重连节奏。这些都是运维侧的可观测性验证,不涉及任何越权操作。
成本这件事要拆成两部分算:组件占用的机器资源,和人的投入。机器资源这部分其实很轻。SPIRE Server 和 Agent 都是单个 Go 二进制,常驻内存占用在百 MB 量级,具体数值随注册条目数和 Agent 数量变化,以你自己的实测为准,别照抄任何"官方推荐配置"里的数字。最小拓扑是:一到两台机器跑 Server,一台跑数据库(小规模可以与 Server 同机,生产建议分开),每台业务机跑一个 Agent——Agent 这一层是零额外机器成本的,它跟业务共享那台你已经付了钱的服务器。
机型选择上,Server 这类组件对 CPU 核数不敏感,更看重内存余量、磁盘可靠性和网络稳定。按官网公开的起步价区间,大陆节点华西 ¥599 起、华东 ¥699 起、华南 ¥799 起、华北 ¥899 起,美洲 ¥1699 起、欧洲 ¥1299 起、非洲 ¥899 起,中国香港节点 ¥1500 起,具体机型、带宽与报价以官网实时信息或咨询确认为准。用这个档位的机器跑 Server 是够的;数据库那台建议把内存往上抬一档。裸金属海外节点有 E5-2620 32G/1T ¥999 起到 E5-2698v4 双路 32G/1T ¥3999 的区间,中国香港、新加坡、日本的大陆优化机型 E5-2620 单路 100M 为 ¥3199、300M 为 ¥7499,这些都属于官网挂牌价,同样以实时报价为准。高防大带宽类节点 ¥700 起,如果你的 bundle endpoint 需要对外暴露给合作方,这类节点会更合适。
这里给一个明确的地区选择结论:如果你的业务主体在大陆、只需要一套身份体系,那么 Server 放大陆节点、海外机房只部署 Agent,是成本更低的做法,代价是跨机房链路抖动要纳入监控。反过来,如果海外节点上的服务彼此调用频繁且要求低抖动,就应该在当地单独建一个信任域,用联邦与大陆互认,而不是让每一次身份同步都跨一次海。中国香港自营节点走 CN2 GIA 优化线路、免备案,起步 ¥1500 起,适合做对外服务的入口,但别把它当成 Server 的唯一落点——身份体系的可用性不应该依赖单一地理位置。
一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;具体机型、带宽与报价以官网实时信息或咨询确认为准。选机器时我给客户的一条实际建议是:先把 Server 和数据库那两三台的数量定下来,剩下的业务机按现有规模走,SPIRE 不会改变你的业务机型选型,它只改动你对"机器之间怎么互相认识"的实现方式。
至于人的投入,这才是大头。按前面的落地顺序,熟悉 Go 和 Linux 的工程师,把最小可用环境跑通需要一到两周,做到双 Server 高可用加审计再加一周,往后是持续优化。这笔账要算在平台建设里,不要算在"某次安全整改"里,否则一定半途而废。
第一条,join_token 上生产。问题是它本质上还是一个人分发的长期凭据;为什么危险,因为它把整套体系的信任根重新交回给了"谁拿到了那个字符串";怎么判断,看你的节点上线流程里有没有一步是"人工复制一段令牌";怎么避,物理机走 TPM DevID 或现有 PKI 证书,云主机走实例身份文档或实例身份令牌,容器走投影 ServiceAccount 令牌,令牌只留在引导阶段并在用完后失效。
第二条,selector 写得太宽。典型是直接用 uid 0 去匹配,结果一台机器上所有以 root 起的进程拿到同一个身份;为什么危险,因为粒度一宽,出事时的处置又变成"全停";怎么判断,拿 entry show 把你所有的条目列一遍,看有没有一条条目能命中超过三个实际服务;怎么避,物理机上优先用 systemd unit 或者独立 gid,容器里用命名空间加服务账号,一个服务一个条目,宁可条目多一点。
第三条,TTL 拍脑袋设短而时钟没人管。问题是短 TTL 加时钟偏差会直接把有效窗口吃掉;为什么危险,表现为大面积、无规律的握手失败,排查方向极易跑偏;怎么判断,看你的 NTP 偏移有没有告警、有没有纳入基础监控;怎么避,TTL 保持默认的一小时量级,时钟同步与偏移告警作为机器的基础项,和业务监控同级。
第四条,拿 SPIFFE ID 直接当授权结论。问题是身份只回答"它是谁",不回答"它能干什么";为什么危险,因为一旦某处代码默认"有身份即放行",整个授权体系就形同虚设;怎么判断,检查你的服务里有没有"验完身份就直接放行"的分支;怎么避,把授权判断独立出来,策略显式声明哪个 SPIFFE ID 能访问哪个接口,身份与授权分别审计。
第五条,Agent 无人托管。问题是它不在任何监控里、崩溃不自动拉起、机器重装后没人记得重新注册;为什么危险,因为它挂掉的那一小时是静默的,等有人重启业务才发现;怎么判断,随便找一台业务机,问一句"这台机器上的 Agent 挂了谁会收到告警",答不上来就是有问题;怎么避,systemd 托管加开机自启,Agent 健康检查进基础监控,机器交付流程里加一条 Agent 注册与验证的步骤。
按这个顺序走,能把返工概率压到最低。先在一台业务机上用 SQLite 起一个 Server 加一个 Agent,挑三个服务接进 Workload API,目标只有一个:能拿到 SVID、mTLS 能通。这一步不追求高可用,也不追求覆盖全量,纯粹是为了让团队看见身份长什么样。接着把注册条目写成文件、进代码仓库、走评审与 CI 下发,同时把 selector 从宽松收到严格——这一步做完,你的身份策略才是可审计、可重建的。然后换 PostgreSQL,Server 扩到两台加负载均衡,把审计日志打开,再把前面列的那四类故障在测试环境里各演一遍,确认告警都能响。
这三步走完,才轮到进阶项:老程序接不进 Workload API 的用 SPIFFE Helper 或者 Envoy SDS;有跨机房、跨团队互认需求的再谈联邦与 bundle endpoint;规模大到单个信任域扛不住的再考虑嵌套。别反过来,一上来就 Federation 加 Nested SPIRE 加多信任域,那是大厂在几百人平台团队支撑下才玩得起的配置,中小团队照抄的结果通常是"没人真正理解这套东西是怎么工作的",而这本身就是最大的风险。
最后给一句立场性的判断:零信任不是买来的,也不是装上去的,它是把每一个身份的来龙去脉搞清楚之后的结果。SPIFFE 和 SPIRE 提供的是一套把来龙去脉变成可验证、可轮换、可审计的机制,它不会替你做决定,但它能让你的决定真正被执行下去。对一台跑着几十个服务的租来服务器来说,这已经算是相当划算的一笔投入了。
一台租来的物理机上跑几十个服务,值得上 SPIRE 吗?看两个条件。一是这些服务之间是否存在真实的信任边界——如果它们本质是同一个应用的几个模块、共享同一份数据、出事时一起停,那身份细分带来的收益有限;二是你是否需要对单个服务做单独吊销与审计。两个条件都成立就值得,因为这种情况下手工证书管理和共用 token 都撑不住。反过来,五个服务以内、调用关系简单,用签发脚本加配置中心管理更划算,别为了技术先进性引入一个需要长期维护的平台组件。
SPIRE 能替代现有的密钥管理系统吗?不能,两者的职责边界很清楚。SPIFFE 只定义工作负载的身份标识与身份文档格式,以及通过 Workload API 获取的方式;数据库口令、第三方 API 密钥这类机密该放哪儿还是放哪儿。它们的关系是配合:服务先用 SVID 证明自己是谁,再凭这个身份去密钥管理系统换取它有权获取的机密,这样替换掉的是"为了拿机密而长期保存的引导令牌",而不是密钥管理系统本身。把两者当替代品,通常会得到一套既存不住机密又发不好身份的东西。
没有 TPM 的云主机怎么做节点证明?优先用云平台自身的实例身份机制:AWS 侧用实例身份文档,Server 拿到文档后去平台接口校验;GCP 侧用实例身份令牌;Azure 侧有实例元数据服务和托管身份两条路。这一类方式的共同点是机器之外有控制面替它背书,你不需要在机器上预置任何长期凭据,这正是要追求的效果。如果是租来的独立服务器、不存在任何云控制面,那就看主板是否有可用的 TPM 并走 DevID 路线;实在没有任何可用机制时才会退到 x509pop(用现有 PKI 证书)或 sshpop。join token 只应作为引导手段,用完即失效,不要拿它当长期方案。
SVID 默认一小时有效期,会不会频繁断连导致业务抖动?正常情况下不会,因为轮换是在后台完成的:Agent 侧默认的轮换策略是在生命周期过半时换新,应用在到期前主动去 Workload API 取新的即可,连接不需要重建。真正会导致抖动的是两个变量——时钟不准和 TTL 设得太短。时钟偏差会直接吃掉证书的有效窗口,TTL 压到分钟级则会让任何一点抖动都变成握手失败。所以保持默认的一小时量级,把时钟同步与偏移告警做扎实,比调 TTL 有效得多。另外提醒一句,JWT-SVID 的默认有效期只有 5 分钟,它本来就是给七层代理场景设计的短命凭据,别在需要长连接的场景里拿它替代 X509-SVID。
Agent 挂了,已经建立的连接会立刻断吗?不会。已经签发出去的 SVID 在有效期内继续可用,正在跑的连接不受影响,业务不会立刻报错。真正受影响的是新起的进程和需要续签的服务,表现为"重启之后起不来"或者"扩容出来的新实例连不上"。所以 Agent 必须交给 systemd 托管、开机自启、崩溃自动拉起,并把它的健康检查纳入机器的基础监控。托管和监控这两件事做到位,Agent 故障就从事故降级成了告警。还有一点容易被忽略:如果 Agent 用的是 memory 型 KeyManager,私钥不落盘,重启之后必须重新完成一次节点证明才能恢复签发,排变更窗口时要把这一步算进去。
已经在用服务网格了,还需要 SPIRE 吗?取决于身份的消费者是谁。如果只有网格在消费身份,网格自带的 mTLS 已经够用,再引入第二套体系反而增加复杂度。需要 SPIRE 的典型情况是:网格之外还有数据库、消息队列、CI 系统、物理机上的老服务要认同一个身份体系;或者存在跨集群、跨云、跨机房的统一命名需求。这几种情况下 SPIFFE 的价值在于"中立格式",它不绑定某一种运行时,能让容器内外的工作负载用同一套身份说话。判断的方法很土但有效:把所有需要验证调用方身份的组件列成一张表,逐个标注它能不能识别 SPIFFE ID,只要有两个以上识别不了,就该考虑用 SPIRE 做统一身份源。
信任域该怎么切,跨机房要不要各建一个?规范给出的建议是按物理位置和按安全实践切:不同机房或区域、生产与测试或实验室环境,原则上放在不同信任域。但落到实操,判断标准是有没有同一批人按同一套基线运维。同一批人、同一套基线,多机房共用一个域完全可以,靠联邦或链路优化解决互通;两批人管、基线不一致,就该分域,通过交换公开信任包实现互认。过度拆分会让每次跨域调用都多一层验证,收益抵不上复杂度。
上了 mTLS 之后,数据库口令这类东西是不是就不用管了?口令还是要管,但管法变了。SPIRE 解决的是"服务如何证明自己是谁",有了这个基础,数据库侧可以改成按工作负载身份授权,口令本身则从长期静态值变成按需短期获取——服务先用 SVID 证明身份,再凭身份换取短期凭据。这样口令泄露的窗口从"永久"变成"几分钟",也不再需要人手工轮换。需要说明的是,这一步依赖数据库侧或者中间件侧支持按身份或短期凭据鉴权,支持不到位的环节仍然按原来的方式管,别指望一次改造覆盖全部。
SPIFFE 官方文档《SPIFFE Concepts》(spiffe.io):SPIFFE ID 的 URI 格式与示例、信任域的定义与划分建议、SVID 两种格式及 X509-SVID 优先的建议、Workload API 提供的三类内容、trust bundle 的组成与轮换特性,以及工作负载隔离机制属规范范围外的说明。
SPIFFE 官方文档《Registering workloads》(spiffe.io):注册条目由 SPIFFE ID、selector 集合与 parent ID 三部分构成,Agent 缓存并更新条目,entry create/update/show/delete 命令,节点别名与 -node 标志的用法,unix:gid、k8s:ns、k8s:sa 等 selector 示例,以及单条目不可混用节点 selector 与工作负载 selector 的约束。
SPIFFE 官方文档《SPIRE Server Configuration Reference》(spiffe.io):default_x509_svid_ttl 默认 1h、default_jwt_svid_ttl 默认 5m、ca_ttl 默认 24h、agent_ttl 继承 X509-SVID 默认值、bind_port 默认 8081、trust_domain 长度建议不超过 255 字符、ca_key_type 默认 ec-p256、audit_log_enabled 默认 false;DataStore 插件化已取消仅支持内置 SQL(SQLite、PostgreSQL、MySQL);NodeAttestor 内置插件清单(aws_iid、azure_imds、azure_msi、gcp_iit、join_token、k8s_psat、sshpop、tpm_devid、x509pop);KeyManager(disk、memory、aws_kms)与 UpstreamAuthority(disk、aws_pca、awssecret、gcp_cas、vault、spire、cert-manager)、Notifier 与 BundlePublisher 插件清单;federation 段的 bundle_endpoint、两种 profile、endpoint_spiffe_id 与 bootstrap_bundle_path 说明。
SPIFFE 官方文档《SPIRE Agent Configuration Reference》(spiffe.io):socket_path 默认值 /tmp/spire-agent/public/api.sock 与 Windows 命名管道默认值,KeyManager disk 与 memory 的差异(memory 不持久化私钥、重启需重新证明),availability_target 的最小值 24h 与宽限期 12h 约束及默认按生命周期一半轮换的策略,experimental 段的 sync_interval 默认 5 秒、sync_retry_backoff 的 48 倍上限与 8 分钟封顶、倍数 1.5 与抖动 10%、server_load_balancing_config 默认 round robin。
SPIFFE 官方文档《Working with SVIDs》(spiffe.io):Workload API 不要求调用方持有认证凭据、由实现侧通过内核元数据识别调用者;spire-agent api fetch x509 的用法与输出内容;SPIFFE Helper 的取证书、拉起子进程、监听过期、发信号重载四类能力。
CNCF 于 2022 年 9 月 20 日发布的 SPIFFE 与 SPIRE 毕业公告(CNCF 官方新闻稿及多家媒体转载):项目 2018 年进入沙箱、2020 年进入孵化、2022 年毕业的时间线,以及毕业时列举的生产用户名单。
第三方整理的 CNCF 项目历史资料(cncf-atlas 的 SPIRE History 页面):SPIRE 当前处于 1.15.x 版本线,不同来源记录的具体补丁号存在差异,公开资料口径不一,以 SPIRE 官方 Release 页面为准。
Red Hat OpenShift 文档《Zero Trust Workload Identity Manager》第 12 章:https_spiffe 与 https_web 两种 bundle endpoint profile 的对比(认证方式、证书管理、信任模型、适用场景),联邦配置的高层步骤,以及部分发行版中 profile 一旦启用不可变更的注意事项。
第三方技术文章(startwithidentity《Top 6 Open-Source Machine and Workload Identity Tools》、appscale.blog 的 SPIFFE/SPIRE 综述、medium 上的 SPIRE 实现解读):机器身份工具横向对比、SPIRE 版本号与部分实现细节的二手描述,其中版本号与具体实现描述口径不一,涉及版本的内容均以官方 Release Notes 为准。
一万网络官网(idc10000.net)公开报价页面:大陆各区域起步价(华西 ¥599 起、华东 ¥699 起、华南 ¥799 起、华北 ¥899 起)、海外与中国香港节点起步价(美洲 ¥1699 起、欧洲 ¥1299 起、非洲 ¥899 起、中国香港 ¥1500 起)、裸金属与大陆优化机型挂牌价、高防大带宽 ¥700 起;文中涉及价格均以官网实时信息或咨询确认为准。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品