Skip to content

第 1 章 SPIFFE 的历史与动机

原书:Chapter 1. History and Motivation for SPIFFE(p.9–22) 翻译策略:技术直译 + 段落重排;专有项目名、协议名、API 名、CLI 命令、配置字段、证书/加密术语一律保留英文;首次出现的关键概念以"中文(English)"形式给出,后续沿用英文。术语对照见 TERMS.md

章节导言

本章为 SPIFFE 的产生做背景铺垫,并解释它为什么会以今天这个形态出现。

压倒性的动机与需求

走到今天这一步之前,我们先经历过一段痛苦的成长。

1981 年互联网刚开始广泛普及时,整个网络只有 213 台不同的服务器,安全几乎不在人们的考虑范围之内(参考:RFC 1296)。随着互联的计算机越来越多,安全始终是软肋:漏洞被轻易利用,进而引发大规模的"蠕虫"事件,例如 1988 年席卷互联网上大多数 Unix 服务器的 Morris Worm(参考:Morris Worm 报告(Purdue)),以及 2003 年扩散到数十万台 Windows 服务器的 Slammer 蠕虫(参考:Slammer 蠕虫分析(CAIDA))。

几十年过去了,那些传统的外围防线(perimeter)防御模式已经不再适配现代计算架构与不断演化的技术边界。点状解决方案和技术一件件堆叠上去,去补那些底层网络安全概念未能跟上现代化步伐的裂缝。

那么为什么这种"画一道墙"的模式如此普遍?我们又需要做什么来弥补它的短板?

多年以来,我们观察到了三股显著的趋势,它们都把传统的外围防线模式推到了未来网络发展的对立面。

  • 软件不再跑在组织自己掌控的单台服务器上。 自 2015 年以来,新开发的软件几乎都是作为一组微服务(microservice)来构建的,每一个微服务都可以独立扩缩容,或迁移到云服务商。如果你不能精准地圈出"需要被保护的服务到底在哪些机器上",那么你根本就没法在它们周围建起一道墙。
  • 你不能信任一切,哪怕是自己公司内部的软件。 过去,我们以为软件漏洞像苍蝇一样可以一只只拍掉;现在它们更像蜂群。国家漏洞数据库(National Vulnerability Database)平均每年报告超过 15,000 个新软件漏洞(参考:NVD 年度统计)。你写或买的任何软件,在某个时间点大概率都存在某些漏洞。
  • 你也不能完全信任人,他们会犯错、会不满,并且对内部服务拥有完全的访问权限。 首先,每年有数万起成功的攻击是基于网络钓鱼(phishing)(参考:www.comparitech.com/blog/vpn-privacy/phishing-s...)或窃取有效员工凭证发起的。其次,随着云应用和移动办公的兴起,员工需要从多种不同的网络合法地访问资源。建墙已经没有意义了——人每天都得来回翻过那道墙才能完成本职工作。

可以看到,外围安全对今天的企业来说已经不再是现实的解。当外围安全被严格实施时,会把企业挡在微服务和云之外;当它被放松时,又让入侵者轻易进来。2004 年,Jericho 论坛就意识到需要一种"外围安全"的继任者。十年后的 2014 年,Google 发表了关于 BeyondCorp 安全架构的案例研究(参考:research.google/pubs/pub43231)。然而,两者都没能获得广泛的采用。

网络曾经很友好——只要我们别乱跑

互联网最早的用例聚焦在学术界,目的是分享信息而不是阻止访问。当其他组织开始把网络化的计算机系统用于业务敏感场景时,它们严重依赖物理边界和物理出处的检验(physical attestation),以保证访问网络的人确实被授权。那时"来自可信内部人员的威胁"这个概念还没出现。随着网络从学术走向商业,软件也从单体走向微服务,安全成了增长的障碍。

起初,物理上用墙和守卫保护计算机的传统手段,在网络世界里被模拟为:通过防火墙、网络分段、私有地址空间、ACL 来实现。这种做法在当时的网络接入点还很少的背景下是合理的。

图 1.1 网络从大学校园演进到全球互联的过程(见 assets/pages/page-012.png)。

随着网络不断扩张,用户和合作伙伴的接入点越来越多,原本用墙和守卫完成的物理身份验证,被虚拟化为安全地交换和管理密钥、凭证(credentials)、令牌(tokens)——而这些又随着技术与需求演化而变得日益棘手。

图 1.2 随着企业的网络日益复杂——再加上云、SaaS 和移动办公人员——构建并维护外围防线越来越难以为继(见 assets/pages/page-014.png)。

采用公有云

从传统本地部署与数据中心向公有云的迁移,把昨日的痛点都放大了。云端按需创建计算资源的自由度,让组织内的开发和运维团队紧密协作,形成围绕 DevOps 的新团队,专注于软件的自动化部署与管理。公有云所提供的快速演化的动态环境,使团队部署的频率从过去的几个月一次跃升到每天多次。资源的按需申请和释放,让组织能够以更高的速度构建出"专注、单一职责、可独立部署"的服务套件——也就是俗称的微服务。这反过来又强化了跨部署集群识别和访问服务的需求。

这种高度动态、弹性的环境,把那些约定俗成的外围安全思路打碎了。我们需要更好的、与底层网络解耦的服务级交互方式。传统的边界控制以 IP 和端口作为认证、授权和审计的依据,而到了云计算范式下,这些已经无法干净地映射回工作负载本身。

公有云带来的新模式——比如 API 网关、面向多服务工作负载的托管负载均衡——更凸显了对"不依赖网络拓扑和路径"的身份的诉求。保障这些服务间通信的完整性变得更加重要,尤其是当团队需要在不同工作负载间保持一致性的时候。

休斯顿,我们摊上事了

当组织采用容器、微服务、云计算和无服务器函数(serverless function)等新技术时,一个趋势是清晰的:软件的颗粒度更细、数量更多。这既增加了攻击者可利用的潜在漏洞数量,也让外围防线的管理变得越来越不切实际。

要做到"更快、更多",意味着越来越多的组件被部署在自动化基础设施之上,往往是以牺牲安全为代价的。绕过防火墙规则工单、安全组变更这类人工流程的情况,绝非个例。在这种新的现代世界里,依赖网络的访问控制很快就会过期,需要不断维护,无论部署环境如何。

图 1.3 基础设施环境与运维需求随新技术的涌现而越发复杂(见 assets/pages/page-015.png)。

管理和配置这些规则及例外虽然可以自动化,但必须迅速完成——在大规模基础设施里这是一个巨大的挑战。再者,NAT 这类网络拓扑的介入,会让"基于网络位置"的安全变得漏洞百出。基础设施规模越大、变化越快,"人在回路中"的流程根本撑不住——毕竟,没人会愿意花钱养一支整天折腾防火墙规则、却依然追不上变化的小队。

依赖服务器名、DNS 名、网络接口细节这类位置相关属性来识别软件,在弹性扩缩、动态调度的应用世界里存在诸多短板。虽然这些网络概念用得很多,但它们对软件身份的模拟是不充分的——本质上只能算一种"低效的类比"。往应用层看,传统的"用户名+密码"或硬编码凭证,多少也表达了一点身份的意思,但更多是解决授权问题而不是认证。

把安全集成进软件开发生命周期、并更早地引入反馈循环,让开发者对"如何让自己的工作负载被识别、彼此交换信息"这件事有了更强的运营掌控。这一变化使授权策略的决策可以下放给最了解该组件的服务或产品负责人。

重新构想访问控制

组织在采纳公有云之前就存在着一长串问题,公有云采用之后这些痛点更加刺眼——这一切推动了一个共识:传统的外围防线是不够的,需要更好的方案。

"去边界化(deperimeterization)"意味着组织必须想清楚如何识别自己的软件、并在服务之间实施访问控制。

把秘密(Secret)当解药

共享秘密——比如密码、API key——为分布式系统提供了一种简单的访问控制手段。但这种方案本身又带来一堆问题:密码和 API key 很容易泄露(试试在 GitHub 上搜"client_secret"这种关键词看看会发现什么)。在大组织里,泄露后想轮换秘密很麻烦——每个服务都要同步切换,少切一个就可能引发宕机。

像 HashiCorp Vault 这类工具和秘密仓库被开发出来,正是为了缓解秘密管理与生命周期的难题。但市面上其他号称解决这个问题的工具,给出的方案往往更局限、效果也一般(可参考 Secrets at Scale: Automated Bootstrapping of Secrets & Identity in the Cloudwww.youtube.com/watch)。不管用哪种方案,我们最终都会回到同一个问题:工作负载到底怎么访问这个秘密仓库?总得有一把 API key、一个密码、或者别的什么秘密来"再保护一层"。

所有这些解法都会陷入"底部的乌龟还是乌龟"(turtles all the way down)的困局:

  • 想访问数据库或服务这类资源,离不开一个像 API key 或密码这样的秘密;
  • 这个 key 或密码又得被保护起来,比如加密——但解密密钥本身又是一个需要被保护的东西;
  • 解密密钥可以放进秘密仓库——但访问秘密仓库又得有一个密码或 API key 类的凭证;
  • 归根到底,保护一个秘密总会引出新的秘密需要保护。

想跳出这个循环,我们必须找到"底部的乌龟"——也就是说,存在一个秘密,让我们能访问所有其他用于认证和访问控制的秘密。一种思路是在服务部署时人工注入秘密,但这种方式在高度动态的环境里根本撑不住。当组织迁入云端、走快速部署流水线、用上自动扩缩之后,再想为每台新机器手工注入秘密,已经不现实。而一旦某个秘密泄露,让旧凭证失效甚至可能把整个系统拖垮。

把秘密直接嵌入应用代码、免去手工注入这条路更糟。写在源码里的秘密有个怪癖——它总会上演"出现在公开仓库"的好戏(你真的去试刚才那个 GitHub 搜索了吗?)。把秘密塞进镜像的构建步骤里看起来好一些,但这些镜像也可能意外被推到一个公开镜像仓库,或者在内部镜像仓库里被作为下一步 kill chain 的素材被人抽走。

我们要的解是:没有长期秘密(容易被泄露、轮换困难),也无需手工给工作负载注入秘密。要做到这一点——无论是在硬件里还是云服务商那——都必须有一个信任根(root of trust),而所有围绕软件(工作负载)身份的自动化方案都建立在这个根之上。这个身份会成为所有需要认证和授权交互的基石。

关键洞察:为了避免再造一只"底部的乌龟",工作负载必须能在不依赖任何秘密或其他凭证的情况下获得这个身份。

图 1.4 有了"身份基岩"(identity bedrock)之后,就不再是"层层乌龟"了(见 assets/pages/page-019.png)。

通向未来的几个步骤

自 2010 年起,多个团队都在尝试解决"软件身份"这个问题。Google 的 LOAS(Low Overhead Authentication Service),后更名为 ALTS(Application Layer Transport Security,参考:cloud.google.com/security/encryption-in-transit...),定义了一种新的身份格式和线协议,让工作负载从运行时环境获取身份并应用到所有网络通信上。这种做法被叫做"拨号音式安全(dial tone security)"。

另一个例子是 Netflix 内部开发的代号为 Metatron 的方案(参考:www.usenix.org/sites/default/files/conference/p...)。它逐台机器地为软件实例建立身份,借助云 API 来证明机器镜像的可信度,并结合 CI/CD 集成把机器镜像和代码身份加密绑定在一起。这种软件身份以 X.509 证书的形式呈现,用来做服务间通信的双向认证;同时还配套开发了一个秘密服务,承载了基于该身份之上的秘密管理。

Facebook 等公司也在做类似的工作(参考:engineering.fb.com/security/service-encryption),共同印证了这种系统的必要性,也凸显了它落地的高难度。

面向所有人的生产级身份框架(SPIFFE)的愿景

要在"通用解"出现之前先把原则立起来。Kubernetes 的创始工程师之一 Joe Beda 反思了自己过往工作中接触到的那些"让工程团队日子更好过"的技术,于 2016 年发出倡议,要做一套专门用于生产环境的身份方案——一个能跨多种异构系统复用的通用解,替代"硬做 PKI(doing PKI the hard way)"这种笨重的中间状态。多家公司合作、围绕 PKI 制定服务身份新标准的这一波浪潮,就是 SPIFFE 的起点。

Beda 的论文于 2016 年在 GlueCon 上发表(参考:engineering.fb.com/security/service-encryption),把一个难啃的硬问题描述成下面这组参数:

  • 解决"零号秘密(secret zero)问题":利用内核层面的内省(introspection),在调用方不提供凭证的情况下获取它的身份信息;
  • 使用 X.509,因为大多数软件本来就兼容 X.509;
  • 把"身份"这个概念从网络定位符中彻底解耦

SPIFFE 的概念被提出之后,一群服务身份领域的专家齐聚 Netflix 总部,探讨原始 SPIFFE 提案的最终形态与可行性。与会者中有不少人都曾各自亲手实现过、持续改进过、也都重新解决过(re-solve)工作负载身份问题——大家都意识到"跨团队、跨厂商的互操作"是个巨大的合作机会。这些专家意识到,大家都在用相似的方法解决同一个问题,完全可以联手共建一个通用标准。

这套工作负载身份方案的最初目标,是建立一份开放规范以及与之配套的生产级实现。这套框架需要满足:

  • 在不同实现之间、开箱即用的软件之间具备互操作性;
  • 在不受信任的环境里确立一个信任根(root of trust),为系统扫除"隐式信任"
  • 把身份从"以网络为中心"转向更灵活、扩展性更好的新形态。

图 1.5 一切开始的地方:2016 年在 Netflix 的那场会议,安全专家们在那里画出了 SPIFFE 的雏形(见 assets/pages/page-022.png)。


下一章:第 2 章 收益

基于 VitePress 构建