随着云原生的蓬勃发展,越来越多的企业选择将容器技术应用到自己的生产环境(据去年CNCF的报告,这个数字是93%)。在快速发展的同时一些安全问题也随之放大,比如云原生环境下的供应链安全问题,开发者通常只需要一个简单的API密钥就可以随意发布镜像,大部分公开的制品仓库都暴露在供应链攻击的阴影下。去年的SolarWinds供应链攻击波及了包括美国政府部门及多家全球500强企业,造成了无法估计的严重影响;同时期微软Exchange Sever的数据泄露影响了超过25万台服务器,为了Win10推出的包管理工具Winget也在短短几周后被各类恶意提交占领;还有之前发生的GIthub私仓大面积被勒索攻击的事件,这些层出不穷、愈演愈烈的攻击事件让如何体系化构建供应链安全的防护规范以及相关治理工具的实践开发成为了业界关注的核心安全问题。
在整个软件供应链的安全防护流程,软件的完整性和溯源性证明是其中的关键环节,然而很多时候开源软件的开发者和使用者可能会基于社区精神而选择容忍代码安全性上的一些缺陷,很少有项目会选择做release包的加签,也很少有项目使用者会有意识的去验签。当然我们可以找到不少开源工具或一些厂商提供的解决方案在软件开发、构建、部署的适当时机阻止供应链攻击,然而相关工具在技术或费用上相对较高的门槛让绝大多数开发者很难真正去使用它们。另外现有的方案通常都是基于GPG工具使用一个“可信”的非对称密钥对去做签名,但是如何保证密钥自身的安全可信呢?如何证明我们拿到的密钥是一个没有被篡改或吊销的密钥?密钥如何存储?选择云服务商的KMS服务或是还需要上HSM硬件加密?这些安全问题同样是阻碍开发者真正去实践制品加签的障碍。
Sigstore项目的出现在一定程度上解决了这样的问题。本文会介绍sigstore的概要设计、特点以及在加签验签环节的相关实践。
What is Sigstore:
Sigstore是由红帽、谷歌联合几所美国高校主导开发维护的供应链安全开源项目,旨在提供一个新的软件加签、验签和防护的标准。通过Sigstore,我们可以自动对云原生下的各类开源制品进行数字签名并校验,帮助构建一个更安全、可追溯的供应监控链。相较于传统方案,Sigstore支持以下创新特点:
-
无密钥签名( Keyless Signatures)模式,支持自动化的密钥管理能力,使用者无需管理维护私钥;
-
友好易用的加签验签工具,制品入库后所有操作都会有公开透明的审计日志;
-
支持符合OCI规范的所有云原生制品的加签验签,同时支持OIDC协议的认证和审计,便于厂商集成。
其中Sigstore的无密钥签名模式可以说是项目最大的亮点,在使用上,会由一个证书签发组件基于OIDC协议认证成功后返回的token签发一个短时的x509证书,这里短时证书是一个很好地选择,一是有效减少了攻击者的机会窗口,同时也可以变相支持复杂的吊销需求。那么在证书过期后,如何建立信任呢?不断重复的证书续期肯定是繁琐的,其实我们在验签时只需要证明给定的制品是在证书有效的时刻加签的就可以了。如何证明呢?Sigstore给出的答案是使用基于RFC 3161协议的防篡改日志来实现,其中RFC 3161允许将防篡改的时间戳信息写入目标制品加签后的哈希值,然后由可信的时间戳机构(TSA)生成包含验证时间戳必要信息的时间戳响应文件(TSR),通过TSR文件我们可以基于协议格式验证短时证书是否在正确的时间窗口期签发了目标制品,从而不必理会对证书的长期管理需求,因为这个短时证书在后续的验签流程中已经不需要了。
除了无密钥签名模式,Sigstore的透明日志特性以及便捷的CLI工具可以让用户的安全运维管理人员查看所有制品加签入库后的操作审计;同时Sigstore的标准化适配也很友好,比如它支持符合OCI规范的所有云原生制品的加签验签,支持OIDC协议的认证审计,这里支持对接的OIDC身份提供商认证token可以是源自Github,符合SPIFFE协议的token,也可以是K8s集群开启了令牌卷投影特性后的serviceaccount,可以说非常适合集成到云原生应用中。
Kubernetes 1.24版本也宣布应用Sigstore做release包的加签,Github actions、Trivy、OPA/Kyverno等云原生供应链环节上重要的开源项目也都推出了与Sigstore的集成方案,在CNCF的背书加持下,预计Sigstore会在未来成为供应链制品完整性校验领域重要的行业标准工具。Sigstore项目给自身的定位也很明确,希望像Let's Encrypt在TLS领域提供免费证书的自动化签发工具那样,在制品完整性校验领域提供免费的证书和匹配的自动化工具。
Core components:
在介绍Sigstore的核心组件前,首先来看下其加签流程:
-
首先生成用于签名的短时证书对
-
基于OIDC身份提供商(Github,Google账号等)的OAuth认证流程去验证签发者的邮箱等身份信息
-
认证通过,用户可以获得颁发的短时证书
-
短时证书会写入到系统的透明日志中,后续会被用于验签流程
-
用户使用短时证书对基于RFC 3161协议对制品加签
-
包含时间戳的TSR签名数据写入到透明日志中
-
短时证书可被销毁
-
签名完成,制品可发布到指定仓库中
Sigstore的核心架构中主要包含下面几个核心组件:
-
Fulcio: 用于签发免费短时证书的根CA组件,同时会和OIDC认证流程结合,将OIDC身份供应商中的邮箱地址作为用户身份签发到短时证书中,证书的有效时长是20分钟;在上述加签流程中,步骤2,3,4都是由Fulcio主导完成。
-
Rekor :取名源自希腊语的“Record”。它旨在构建软件供应链环节的不可变防篡改账本,制品开发者或构建系统可以通过它产生一个不可变的签名元数据记录,同时它也提供了用于验签或查看 transparency日志中签名实体的restful API 和CLI工具,方便制品的使用者进行可信验证。
-
Cosign :基于Fulcio和Rekor的签名工具,负责加签指定的制品并将加签信息推送到指定的OCI仓库中。在上述加签流程中,步骤1,5,6,7是由Cosign完成的。
Sign:
Cosign支持多种形式的加签,包括使用指定密钥的加签,从KMS密钥读取密钥加签,基于硬件token的加签,从k8s集群secret中读取密钥加签,形式多种多样,这些加签模式在其他的开源解决方案里也都能找到,这里我们看下它的keyless模式加签,也就是基于OIDC连接的加签。基于RFC 3161协议的三方时间戳权威机构(Timestamp Authority)和透明日志(Transparency Logs)是其中两个关键技术,基于时间戳的认证流程在上面有简要介绍,有兴趣的同学可以查看协议原文了解详细的认证计算过程;而关于Rekor的透明日志特性依赖的是谷歌的trillian项目,这里又有两个重要的依赖:
-
Merkle Tree: 默克尔树是trillian项目的基础数据结构,同时也被应用于很多数字签名、P2P和区块链场景,通过append-only的日志模式保证操作的可溯源;
-
Certificate Transparency:Trillian项目除了提供了底层核心的数据存储接入层外,还需要上层的扩展项目实现一套自身的 定制化扩展,而CT项目正式其中应用最为广泛的实现。有兴趣的同学可以去学习CT项目 源码,它在k8s场景中的应用架构如下:
上面我们介绍了无密钥加签中的一些基本概念和模型,下面我们来在本地使用cosign的CLI工具尝试一下,这里选择一个阿里云镜像仓库中的测试镜像(cosign支持阿里云ACR仓库),通过keyless模式下需要设置COSIGN_EXPERIMENTAL为1:
基于上述介绍的无密钥加签流程,可以看到在Fulcio生成短时证书后,会进入OIDC OAuth环节,这里终端由于无法自动打开浏览器需要手动在浏览器输入OAuth链接,选择目标身份服务商后并完成认证流程后输入返回的verification code;此时对于首次签发的证书Fulcio会返回一个签名的证书时间戳(Signed Certificate Timestamp)同时签名验证的服务会将其写入证书透明日志(CT log)中并完成签名流程。
下面简要介绍下cosign和Github的集成,对开源软件的完整性校验一直是被社区忽视的问题,而大多数应用代码都需要引入开源的三方包,Github在去年末实现了cosign和Github Actions的集成,开发者可以在Marketplace中找到cosign的安装脚本:
在Actions secret中可以填入指定的密钥作为job中的入参变量:
相关工作流yaml示例如下:
name: Docker CI
on:
push:
branches: [ master ]
workflow_dispatch:
env:
REGISTRY: xxxxxx
IMAGE_NAME: signedcontainer
jobs:
build:
。。。。
- name: cosign-installer
uses: sigstore/cosign-installer@v2.0.0
- name: Write signing key to disk
run: 'echo "$KEY" > cosign.key'
shell: bash
env:
KEY: ${
{ secrets.COSIGN_PRIVATE_KEY }}
- name: Sign the published Docker image
env:
COSIGN_PASSWORD: ${
{ secrets.COSIGN_PASSWORD }}
run: cosign sign --key cosign.key ${
{ env.REGISTRY }}/${
{ github.actor }}/${
{ env.IMAGE_NAME }}:${
{ github.run_id }}
Verify:
sigstore在验签环节和k8s部署时刻的策略引擎gatekeeper以及kyverno均有集成。这里我们以gatekeeper为例在集群中部署cosign-gatekeeper-provider,它的实现比较简单,只是基于gatekeeper的ExternalData特性调用cosign的镜像签名校验接口,接口中的server地址默认为Rekor的公网服务器地址https://rekor.sigstore.dev,所以该hook在默认配置下只适配于keyless模式。
可以看到当我们试图在指定的ns下部署非签名的镜像时会遭到拒绝,并提示no matching signatures:
Limitation:
当然Sigstore在当下也存在一定的局限性和未知性:
-
无密钥模式尚在beta阶段,社区并没有将其标识为生产可用,主要原因是依赖的Rekor和Fulcio组件还未进入1.0版本,有待继续关注其后续发展。
-
Notary在该领域积累了相当数量的企业客户,Sigstore在对已签名payload的 定义规范上基于的是红帽自身的规范而为采用OCI Descriptors的定义,未来可以预见二者在设计和实现上肯定会基于统一规范做出一定的调整融合。
Reference:
https://martinheinz.dev/blog/55
https://datatracker.ietf.org/doc/html/rfc6962
https://github.com/sigstore/community/blob/main/docs/zero-trust-supply-chains.pdf
https://github.blog/2021-12-06-safeguard-container-signing-capability-actions/
https://blog.aquasec.com/trivy-github-actions-security-cicd-pipeline