2011 年,互联网技术先驱 Marc Andreessen 宣称,软件正在吞噬世界(Software is eating the world)。由软件驱动的行业创新正在颠覆着传统业务模式,推动着全球经济实现数字化连接。随着互联网的快速发展,数字化转型已经成为每一个企业的关键战略。但是现代软件开发涉及到多方协作,大量应用依赖开源代码或者三方组件。在上游开源软件的安全问题会传递到下游应用方并被放大,有可能给企业造成重大的安全风险和业务损失。
软件生产的过程与传统制造业在很多方面是相似的。软件制造商将自研业务代码和第三方组件组合成完整的软件,构建流程会将这些组件打包成为可部署的软件制品,然后被企业客户部署到生产环境中。这个过程被形象地称为“软件供应链”。而软件供应链安全的目标是保软件从开发到部署的整个生命周期的安全、可信赖。
在 Sonatypes's 2021 State of the Software Supply Chain 调查报告中,2021 年软件供应链攻击事件增长了 650%。
目前,软件供应链安全已经得到行业的高度重视。已经有很多国家出台了相关的政策法规来指导本国的供应链安全管理,以提高供应链安全韧性。2021 年 4 月,美国网络安全与基础设施安全局(CISA)和美国国家标准与技术院(NIST)联合发布《防御软件供应链攻击》报告,首次对软件供应链进行界定,并给出与软件供应链攻击相关的信息、关联风险以及缓解措施。
谷歌也提出了 Supply chain Levels for Software Artifacts - 软件制品的供应链等级,简称为 SLSA。SLSA 是 Google 内部实施的多年的安全流程的开源版本,提供了一个安全框架和一组最佳实践,来预防因为源代码篡改、三方组件漏洞、制品仓库入侵产生的安全威胁。
软件供应链安全的基础就是透明性。只有理解了应用是如何从代码和依赖组件构建而成,我们才可以对应用的安全风险进行有效的治理。在美国 2021 年颁布的《关于改善国家网络安全的行政命令》行政令中,特别要求政府软件应包含机器可读的软件物料清单(Software Bill Of Materials, SBOM)。SBOM 是包含构建软件使用的各种组件的详细信息和供应链关系的正式记录。
美国国家电信和信息管理局(NITA)在 14028 号政令的要求下,在 2021 年 7 月 12 日发布了《SBOM 的最低要素》,该文档为各开发工具的组织和厂商提供了 SBOM 数据格式的参考。在新一代软件开发工具中,越来越多的软件提供了对 SBOM 的支持。
下面我们以一个 Golang 的 HTTP Server 示例应用为例,了解一下 SBOM 的的概念和使用方式。
- $ git clone https://github.com/denverdino/secure-supply-chain-sample
- $ cd secure-supply-chain-sample
- $ go build .
熟悉 Go 语言的开发者一定对 go 模块概念非常熟悉。应用所依赖的模块,都通过 go.mod 文件声明。go mod tidy 命令可以用来更新 go.mod 文件,并”锁定“所依赖的模块和版本信息,这大大提升了构建的确定性、可重现性和可验证性。为了确保第三方无法篡改所依赖的模块版本, 在 go.sum 文件记录了每个模块依赖的加密哈希值。当利用go命令将模块代码下载到本地时,就会计算其哈希值,如果计算结果与 go.sum 记录的数据不符就意味着存在篡改的风险。更近一步,Google 运营着一个 Go 模块校验数据库服务,它记录了 go 模块版本的加密哈希值,进一步提升了 Go 基础设施的安全性。
更多内容可以阅读:https://go.dev/blog/supply-chain
对于已编译的应用二进制文件,我们可以通过 go version -m 命令查看应用的构建信息,包括其软件来源,依赖模块,构建参数等等。
- $ go version -m secure-supply-chain-sample
- secure-supply-chain-sample: go1.18.3
- path github.com/denverdino/secure-supply-chain-sample
- mod github.com/denverdino/secure-supply-chain-sample (devel)
- dep github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
- dep golang.org/x/sys v0.0.0-20220702020025-31831981b65f h1:xdsejrW/0Wf2diT5CPp3XmKUNbr7Xvw8kYilQ+6qjRY=
- build -compiler=gc
- build CGO_ENABLED=1
- build CGO_CFLAGS=
- build CGO_CPPFLAGS=
- build CGO_CXXFLAGS=
- build CGO_LDFLAGS=
- build GOARCH=amd64
- build GOOS=darwin
- build GOAMD64=v1
- build vcs=git
- build vcs.revision=c630057157df402b658ab97139895337633b5bbc
- build vcs.time=2022-07-04T01:33:01Z
- build vcs.modified=false
容器技术重塑了整个软件供应链。容器镜像将应用及其所有依赖项打包,从而使应用可以在不同的计算环境之间快速、可靠地运行。容器镜像已经成为了应用分发的标准,而 Kubernetes 成为了分布式资源调度和编排的事实标准。那么如何保障容器镜像在构建、分发和运行时的完整性、安全性呢?
我们继续以上面的 Golang 应用为例,介绍一下现容器化软件供应链安全的最新实践和工具链。
Ko 是 Google 开源的一个简单、快速的 Go 应用程序容器镜像构建器。今天我们只聚焦于 Ko 在软件供应链的一些设计,大家可以举一反三,应用在自己的语言和项目中。
无需编写 Dockerfile,利用 Ko build 我们可以将一个 Golang 项目构建成为一个 Docker 镜像。
- $ go version -m secure-supply-chain-sample
- secure-supply-chain-sample: go1.18.3
- path github.com/denverdino/secure-supply-chain-sample
- mod github.com/denverdino/secure-supply-chain-sample (devel)
- dep github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
- dep golang.org/x/sys v0.0.0-20220702020025-31831981b65f h1:xdsejrW/0Wf2diT5CPp3XmKUNbr7Xvw8kYilQ+6qjRY=
- build -compiler=gc
- build CGO_ENABLED=1
- build CGO_CFLAGS=
- build CGO_CPPFLAGS=
- build CGO_CXXFLAGS=
- build CGO_LDFLAGS=
- build GOARCH=amd64
- build GOOS=darwin
- build GOAMD64=v1
- build vcs=git
- build vcs.revision=c630057157df402b658ab97139895337633b5bbc
- build vcs.time=2022-07-04T01:33:01Z
- build vcs.modified=false
为什么镜像的创建时间变成了 ”1970/1/1“ ? 这是因为 Ko 的一个设计目标就是支持 Reproducible Builds - 可重现的构建。Reproducible Builds 是一组开发实践,当源码与构建环境配置一模一样时,在不同机器上编译出来的目标二进制文件会在比特层面上做到完全相同。这样的好处是允许独立的第三方可以对从源代码到二进制文件编译过程中进行验证,确保没有引入漏洞或后门。常见的 docker build 命令构建出的容器镜像会在镜像的 manifest 文件中包含构建时间戳,而镜像的 HASH 计算会包含 manifest 文件。这样导致即使是使用相同的代码和 Dockerfile,也无法保障构建出的镜像 HASH 是相同的。
Ko 通过固定应用代码和镜像构建的时间戳,确保了在任何环境构建出的镜像 HASH 保持不变。所以我们可以通过比较镜像的 HASH 值来快速判断两个镜像中的内容是否一致。一个开放性的问题,Reproducible Build 是否是一个最佳实践?是否适合于您的业务场景?
此外,为了更好支持软件供应链安全。Ko 自动为应用容器镜像生成了相应的 SBOM 信息,并且作为一个应用制品推送到镜像仓库中。可以通过 URL 路径进行访问:{KO_DOCKER_REPO}/{IMAGE_NAME}:sha256-{HASH}.sbom
如何获得容器镜像的 SBOM 信息呢?我们有两个方法:
第一,如果在镜像仓库中没有保存容器镜像的 SBOM 信息,我们可以利用 docker sbom 命令分析生成应用镜像的 SBOM 信息。
- $ docker sbom knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3
- Syft v0.43.0
- ✔ Loaded image
- ✔ Parsed image
- ✔ Cataloged packages [6 packages]
- NAME VERSION TYPE
- base-files 11.1+deb11u3 deb
- github.com/denverdino/secure-supply-chain-sample go-module
- github.com/sirupsen/logrus v1.8.1 go-module
- golang.org/x/sys v0.0.0-20220702020025-31831981b65f go-module
- netbase 6.3 deb
- tzdata 2021a-1+deb11u4 deb
第二,如果在镜像仓库中已经存储了容器镜像的 SBOM 信息,比如 Ko 所发布的 SBOM 信息。我们可以利用 Sigstore 项目中的 cosign 工具直接获得镜像的 SBOM 内容。
- $ cosign download sbom knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3
- Found SBOM of media type: text/spdx
- SPDXVersion: SPDX-2.2
- DataLicense: CC0-1.0
- SPDXID: SPDXRef-DOCUMENT
- DocumentName: github.com/denverdino/secure-supply-chain-sample
- DocumentNamespace: http://spdx.org/spdxpackages/github.com/denverdino/secure-supply-chain-sample
- Creator: Tool: ko 0.11.2
- Created: 1970-01-01T00:00:00Z
-
- ##### Package representing github.com/denverdino/secure-supply-chain-sample
-
- PackageName: github.com/denverdino/secure-supply-chain-sample
- SPDXID: SPDXRef-Package-github.com.denverdino.secure-supply-chain-sample
- PackageSupplier: Organization: github.com/denverdino/secure-supply-chain-sample
- PackageDownloadLocation: https://github.com/denverdino/secure-supply-chain-sample
- FilesAnalyzed: false
- PackageHomePage: https://github.com/denverdino/secure-supply-chain-sample
- PackageLicenseConcluded: NOASSERTION
- PackageLicenseDeclared: NOASSERTION
- PackageCopyrightText: NOASSERTION
- PackageLicenseComments: NOASSERTION
- PackageComment: NOASSERTION
-
- Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-github.com.denverdino.secure-supply-chain-sample
-
-
- Relationship: SPDXRef-Package-github.com.denverdino.secure-supply-chain-sample DEPENDS_ON SPDXRef-Package-github.com.sirupsen.logrus-v1.8.1
-
- ##### Package representing github.com/sirupsen/logrus
-
- PackageName: github.com/sirupsen/logrus
- SPDXID: SPDXRef-Package-github.com.sirupsen.logrus-v1.8.1
- PackageVersion: v1.8.1
- PackageSupplier: Organization: github.com/sirupsen/logrus
- PackageDownloadLocation: https://proxy.golang.org/github.com/sirupsen/logrus/@v/v1.8.1.zip
- FilesAnalyzed: false
- PackageChecksum: SHA256: 7492ae1e0aa4d4d35096aa00e814e533559ff43387dcd063432bb487df806591
- PackageLicenseConcluded: NOASSERTION
- PackageLicenseDeclared: NOASSERTION
- PackageCopyrightText: NOASSERTION
- PackageLicenseComments: NOASSERTION
- PackageComment: NOASSERTION
-
-
- Relationship: SPDXRef-Package-github.com.denverdino.secure-supply-chain-sample DEPENDS_ON SPDXRef-Package-golang.org.x.sys-v0.0.0-20220702020025-31831981b65f
-
- ##### Package representing golang.org/x/sys
-
- PackageName: golang.org/x/sys
- SPDXID: SPDXRef-Package-golang.org.x.sys-v0.0.0-20220702020025-31831981b65f
- PackageVersion: v0.0.0-20220702020025-31831981b65f
- PackageSupplier: Organization: golang.org/x/sys
- PackageDownloadLocation: https://proxy.golang.org/golang.org/x/sys/@v/v0.0.0-20220702020025-31831981b65f.zip
- FilesAnalyzed: false
- PackageChecksum: SHA256: c5db1e8eb5bfd167f67624f908fa775e629435bafb5efc3c9188a543eeaa8d16
- PackageLicenseConcluded: NOASSERTION
- PackageLicenseDeclared: NOASSERTION
- PackageCopyrightText: NOASSERTION
- PackageLicenseComments: NOASSERTION
- PackageComment: NOASSERTION新一代镜像签名技术-Keyless
众所周知,我们可以用镜像签名的方式来保障镜像的可追溯性。一般而言,开发者利用私钥对镜像加签,在部署应用时,可以通过 OPA/Gatekeeper 拦截请求,对镜像进行验签。开发者可以定义安全策略来自动化安全流程。比如我们可以定义一个策略,不允许存在高风险漏洞的镜像部署到生产系统。在镜像构建阶段,我们可以对通过镜像扫描且不包含高风险漏洞的镜像进行签名,而在 K8s 生产集群中,通过安全策略来来校验镜像签名。
由于过去镜像加签/验签的工具易用性不好,并没有得到企业广泛的应用。阿里云镜像服务 ACR,ACK 在提供了集成、简化的镜像安全体验,极大降低了镜像安全的技术门槛。
我们今天给大家介绍一个新的技术思路。Sigstore 是由红帽、谷歌联合几所美国高校主导开发维护的供应链安全开源项目,旨在提供一个新的软件加签、验签和防护的标准。通过 Sigstore,我们可以自动对云原生下的各类开源制品进行数字签名并校验,帮助构建一个更安全、可追溯的供应监控链。
其中 Sigstore 的无密钥签名 Keyless Signatures 模式是这个项目最大的亮点之一。无密钥签名,支持自动化的密钥管理能力,使用者无需管理维护私钥,极大简化的镜像加签验签的体验。
我们首先通过 cosign sign 命令对镜像进行加签:
- $ COSIGN_EXPERIMENTAL=true cosign sign knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3
- Generating ephemeral keys...
- Retrieving signed certificate...
-
- Note that there may be personally identifiable information associated with this signed artifact.
- This may include the email address associated with the account with which you authenticate.
- This information will be used for signing this artifact and will be stored in public transparency logs and cannot be removed later.
- By typing 'y', you attest that you grant (or have permission to grant) and agree to have this information stored permanently in transparency logs.
-
- Are you sure you want to continue? (y/[N]): y
- Your browser will now be opened to:
- https://oauth2.sigstore.dev/auth/auth?access_type=online&client_id=sigstore&code_challenge=Zj3B13VeqlwO1OEs_cc_gVly21ZWdotsB_ipAG1KUD0&code_challenge_method=S256&nonce=2BPd2p6uHg1e916zBz3MoEoH8FR&redirect_uri=http%3A%2F%2Flocalhost%3A55455%2Fauth%2Fcallback&response_type=code&scope=openid+email&state=2BPd2uo32VlgXKC9NY0rDOJlfmA
- Successfully verified SCT...
- tlog entry created with index: 2824482
- Pushing signature to: knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample
在 Keyless 模式中,终端会自动打开浏览器,由用户选择 OAuth 身份服务商进行认证,流程完成后。cosign 会对镜像进行签名,并将镜像签名保存在镜像仓库中,其 URL 路径为: {IMAGE_REPO}/{IMAGE_NAME}:sha256-{HASH}.sig
在这个过程中 cosign 利用 fulcio 的根证书,创建了一个临时秘钥和证书,对镜像进行加签。签名也会记录在 Rekor 的透明化日志中,为镜像签名提供远程证明。
我们可以利用 cosign verify 命令对镜像签名进行校验。
- COSIGN_EXPERIMENTAL=true cosign verify knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3
-
- Verification for knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3 --
- The following checks were performed on each of these signatures:
- - The cosign claims were validated
- - Existence of the claims in the transparency log was verified offline
- - Any certificates were verified against the Fulcio roots.
-
- [{"critical":{"identity":{"docker-reference":"knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample"},"image":{"docker-manifest-digest":"sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3"},"type":"cosign container image signature"},"optional":{"Bundle":{"SignedEntryTimestamp":"MEQCIB1YeNP8suFn6xm7hn0qkAJBiID/gcOP//tsm7u2Z/FIAiBqiw0Zl1CvgVfcGhOWKUEkZKB2XdFm3FPaxq9E+Jsmyg==","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MDRiZWI5M2Q0OGVhYzA4ZmE3OTQzNWNhNTVlZmQyNTE4ODhlN2NmZmMzODJlZTE0MmRjMzZkZDExMzhkYzFjIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUNhNXhYd25pR1J6ckRlamtHRHFCSFNTdlMvdUVCeUJkVUV0cFBiRVRxZUVBSWdGY0hWOW9RWkYxYk4xYmtxMWY3UFdWOUpCY00vZlhyZXZTa0orT0tWSDJrPSIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU52ZWtORFFXbHBaMEYzU1VKQlowbFZTSHByZFVSeVprODJWamxqVUhsSlkyZ3JOVXBFUlc4NVRtcHpkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZDA1NlFYbE5WRTB3VFdwSk1sZG9ZMDVOYWtsM1RucEJlVTFVVFRGTmFra3lWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWQ016UkNhRGRPZWtRMFRqaEVWVWxyWW5WWVkxWTVUMEZEV1d4MlpGcFNiMkkzVmt3S1RIaE9SMnhOSzNrMmIycGhUR05EZHpaNVlsaHNaak5RVFU0clduaERUVmROTUVreVkyWjFPRUpZZFhNelUzaFVka3RQUTBGVlkzZG5aMFpFVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWUFExUm5DakZMV1dKbFZFRlpWVmxvZUZwVU1HeDJja0YzU0RaWmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwbG5XVVJXVWpCU1FWRklMMEpDWjNkR2IwVlZXa2RXZFdSdFZubGFSMngxWWpCQ2JtSlhSbkJpUXpWcVlqSXdkMHhCV1V0TGQxbENRa0ZIUkFwMmVrRkNRVkZSWldGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5ZUhaYU1teDFUREk1YUdSWVVtOU5TVWRLUW1kdmNrSm5SVVZCWkZvMUNrRm5VVU5DU0hORlpWRkNNMEZJVlVGRFIwTlRPRU5vVXk4eWFFWXdaRVp5U2pSVFkxSlhZMWx5UWxrNWQzcHFVMkpsWVRoSloxa3lZak5KUVVGQlIwSUtkbmxZT0d0blFVRkNRVTFCVW1wQ1JVRnBRazVFYjBkNE16STNaMGRDZWxWeWRIaEZVVFZ6ZEZSRFF6Z3haRWMwY1dsU2VrMHZTMHR2Y3pGMU5IZEpad3BMVDIxTVNXdENXVzF6V21ZeFZIQTNNRXhaWkRCbGIwaFVVV3AyV1dFdldrbFdaVFEzWTBwMUwwaFpkME5uV1VsTGIxcEplbW93UlVGM1RVUmhVVUYzQ2xwblNYaEJTemREVFRkQ1NFMW1UWE5GY0hwa1lXbHlVRFIzUkZFeUwxUnZibEUxYTBack5FbEZWamRvY1dScFJWbDJVMEpEVVhoSU0xQjRjV2RGU1dJS2RVdEhiWEozU1hoQlR6VmlRamMyUVRSaE5qWXpUSGRFYVVkalYyUlpkVEZrY0Rabk1ubDRkREpyY0ZObFprcGlVMHBQT1dOWFJEVldOM1JUY1d0WmVRcGFSbkJSUmsxUlowdDNQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0=","integratedTime":1656769347,"logIndex":2822273,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}},"Issuer":"https://github.com/login/oauth","Subject":"denverdino@gmail.com"}},{"critical":{"identity":{"docker-reference":"knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample"},"image":{"docker-manifest-digest":"sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3"},"type":"cosign container image signature"},"optional":{"Bundle":{"SignedEntryTimestamp":"MEYCIQDdn2/IzVRCAuS+8s5Zz1F2+Gp3/MXzFP2whryasTa3GQIhAJKWFtb7jGnL8krnR4CwVYr/jbtZuHtlX7TpNPtEP67R","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MDRiZWI5M2Q0OGVhYzA4ZmE3OTQzNWNhNTVlZmQyNTE4ODhlN2NmZmMzODJlZTE0MmRjMzZkZDExMzhkYzFjIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUURVVUdHOEJOZnRoeDBrZGxzWGpvZzZQaFRENmFsNUo4Y3F0WnFFOFJQY3BBSWdMYlFRd2pLT2I0VU9OS05DZ2hvNnlENDNSNHl4TVU2QldtYllBQmVTQXhnPSIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU53UkVORFFXbHRaMEYzU1VKQlowbFZTVmhtYW5nME1sTnNhQzl5TW5CTlNYUjFRMVJHVjNWVGNUTnJkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZDA1NlFYcE5SRVY1VFVSVk5GZG9ZMDVOYWtsM1RucEJlazFFUlhwTlJGVTBWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVVMk9HWkZabkJqWVZSaE1XUjRlVXQxU0RsbGVIcFJSM2MyWWtaelRDOTJVelkxZENzS01WWnNaSEJFZFRWaVMwcFJjbVozVjA1SFEzZ3Zia3RzYUVaS09VZDZhV1JpTUZkYWJtZFJTVUpsU25GcVRtMTVjVFpQUTBGVlozZG5aMFpGVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWTlRTdG9Da1ZsVDBGUkszaEpTUzlxVVZvMWJrbE5ObEpaWWxadmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwbG5XVVJXVWpCU1FWRklMMEpDWjNkR2IwVlZXa2RXZFdSdFZubGFSMngxWWpCQ2JtSlhSbkJpUXpWcVlqSXdkMHhCV1V0TGQxbENRa0ZIUkFwMmVrRkNRVkZSWldGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5ZUhaYU1teDFUREk1YUdSWVVtOU5TVWRMUW1kdmNrSm5SVVZCWkZvMUNrRm5VVU5DU0hkRlpXZENORUZJV1VGRFIwTlRPRU5vVXk4eWFFWXdaRVp5U2pSVFkxSlhZMWx5UWxrNWQzcHFVMkpsWVRoSloxa3lZak5KUVVGQlIwSUtkMkZYUlVGM1FVRkNRVTFCVW5wQ1JrRnBRazVLWVVKMVJqUk1OVFJsUnpGRlFYVlJTMHh3WTNFNGNtdFlSbXB5TlRoeE1EZHpNazE1ZVVOcFJXZEphQXBCU1hCc1luZHpNVFZLV21KbmVGRTNXRGRTYlRocU5uSjBPV1pWVGxSWlJtMXdhVFJJYzBGcVNIQnRkVTFCYjBkRFEzRkhVMDAwT1VKQlRVUkJNbXRCQ2sxSFdVTk5VVU4zWWtSUVFqQXJWVUZHWkhkcWJVeHJUVEZ4VEhsbGFucGFNbEpWYURKRVEyMHdZbE5rZURSQmQwaDNkV2Q2UmsxaFlsZHpOaTlqVURFS2MxWjZOWHBTTkVOTlVVUTJaR2RVYm0wNWNIUnhVa3BTVlVkWFRGTm1lVzlqZFZoeVRqaFllR1JhU1VsMWJuQjBTek01WW1ZNVYweFdZVFJRYTFObFR3cGhUeXRzSzJvMGRHUmtWVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0=","integratedTime":1656811260,"logIndex":2824453,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}},"Issuer":"https://github.com/login/oauth","Subject":"denverdino@gmail.com"}},{"critical":{"identity":{"docker-reference":"knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample"},"image":{"docker-manifest-digest":"sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3"},"type":"cosign container image signature"},"optional":{"Bundle":{"SignedEntryTimestamp":"MEUCIBe/RXXYBuyDXJVvCwFV/vcjfiUXnNqjS7HR2TpyB6WeAiEA+4MdLCkXW1zFqo4M0cdkNcPpEQe5ZfLw5mv266ib1oE=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MDRiZWI5M2Q0OGVhYzA4ZmE3OTQzNWNhNTVlZmQyNTE4ODhlN2NmZmMzODJlZTE0MmRjMzZkZDExMzhkYzFjIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUNwN3BUckFUMk04M3hYaFJGc1IyNERnc2dEL3dJOHFCdW1TYUdaSXhiTHVBSWdETUpCb3hNWmU0M2ZtMjJBaHR2SE5BVEFwazhteWN0Y3huaC9oUG13QmxBPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnZla05EUVdseFowRjNTVUpCWjBsVlZHWkJRM0ZXTWtKWmJ6UjNUVEpDY2pCVU4yMW5OVWcxVjFrd2QwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEpkMDU2UVhwTlJFVjVUa1JWTTFkb1kwNU5ha2wzVG5wQmVrMUVSWHBPUkZVelYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZxVTNSR1dtcFljbXh2U0dwaGRTOXZRMWxwWVZSVmNUbFRWMk5wWVVzd2RrSnlaMGNLVmt4WlNYbzVVbmR3UnpnM1RuTjNhV3BIZFhac1QySnlXbUkzUTFad2JtRXlSMGwyVUhSNVRFdzNaWFp5VWxOQmJtRlBRMEZWYTNkblowWkdUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlV3TUc5ekNtcFRlRnAzYmtwRWVFeDVVREl2WXl0SEwyZFNUMmR2ZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsbldVUldVakJTUVZGSUwwSkNaM2RHYjBWVldrZFdkV1J0Vm5sYVIyeDFZakJDYm1KWFJuQmlRelZxWWpJd2QweEJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FWRlJaV0ZJVWpCalNFMDJUSGs1Ym1GWVVtOWtWMGwxV1RJNWRFd3llSFphTW14MVRESTVhR1JZVW05TlNVZE1RbWR2Y2tKblJVVkJaRm8xQ2tGblVVTkNTREJGWlhkQ05VRklZMEZEUjBOVE9FTm9VeTh5YUVZd1pFWnlTalJUWTFKWFkxbHlRbGs1ZDNwcVUySmxZVGhKWjFreVlqTkpRVUZCUjBJS2QyRnJiakZuUVVGQ1FVMUJVMFJDUjBGcFJVRm9TR3RCV21abGRWWnVNREpMUlhoU1FsSnNNVGt2Y0VsWUx5OXNMelptZFd3ekwxazNSRE5yYjNGblF3cEpVVVJHSzBWSGRFaEViMUZNT1VseWVWQnNVbk5oUlVOa1FrbGpibVphWldKWE9HMW1RMmgxVFV4dVdtRlVRVXRDWjJkeGFHdHFUMUJSVVVSQmQwNXVDa0ZFUW10QmFrRXJaV1pJTlhjMlUyMVNObnBvVFM5TmQwSkJXVUY1Um10bWRVdEVOMWQwVldadlFUUTVabUUyY205MFZ6WnNjV1UwWm04eVFtbFJTVTRLUjBwYWFVcHJPRU5OU0ZGNkwwdE9aa3d5VjA4eFNFVlNVSFJWTUdoRVlYcFBORkYxUkhkSlJtdDZabFJRYlZaMFJub3JRakIxYjI1TlR5OVhVWHBuVWdwd016Vm1RVXhyWVVKQlBUMEtMUzB0TFMxRlRrUWdRMFZTVkVsR1NVTkJWRVV0TFMwdExRbz0ifX19fQ==","integratedTime":1656811517,"logIndex":2824482,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}},"Issuer":"https://github.com/login/oauth","Subject":"denverdino@gmail.com"}}]
Sigstore 的 cosign-gatekeeper-provider 项目提供了 Cosign 与 OPA/Gatekeeper 的集成,可以在 Kubernetes 集群中 对镜像进行验签。但是目前这个版本还不支持 Keyless 签名方式。我 fork 了一个版本,支持了 Keyless 签名。
安装配置 Gatekeeper 支持 external data 特性:
- $ helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
- $ helm install gatekeeper/gatekeeper \
- --name-template=gatekeeper \
- --namespace gatekeeper-system --create-namespace \
- --set enableExternalData=true \
- --set controllerManager.dnsPolicy=ClusterFirst,audit.dnsPolicy=ClusterFirst
安装 cosign-gatekeeper-provider,配置 OPA 策略,拒绝不包含合法签名的镜像。
- $ git clone https://github.com/denverdino/cosign-gatekeeper-provider
- $ cd cosign-gatekeeper-provider
- $ kubectl apply -f manifest
- $ kubectl apply -f policy/template.yaml
- $ kubectl apply -f policy/constraint.yaml
我们可以验证一下,如果 Deployment 包含正确签名的镜像可以被成功部署。
- $ cat deploy.yaml
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: sample-deployment
- labels:
- app: sample
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: sample
- template:
- metadata:
- labels:
- app: sample
- spec:
- containers:
- - name: sample
- image: knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3
- ports:
- - containerPort: 8080
-
- $ kubectl apply -f deploy.yaml
- deployment.apps/sample-deployment created
如果镜像没有签名,Deployment 的部署将被阻断:
- $ cat deploy-unsigned.yaml
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: sample-deployment-unsigned
- labels:
- app: sample-unsigned
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: sample-unsigned
- template:
- metadata:
- labels:
- app: sample-unsigned
- spec:
- containers:
- - name: sample
- image: knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample:unsigned
- ports:
- - containerPort: 8080
-
- $ kubectl apply -f deploy-unsigned.yaml
- Error from server (Forbidden): error when creating "deploy-unsigned.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [cosign-gatekeeper-provider] invalid response: {"errors": [], "responses": [], "status_code": 200, "system_error": "VerifyImageSignatures: no matching signatures:\n"}
实现真正的端到端的软件供应链安全需要整个行业的努力,近期众多开源社区加大了相关技术的投入和合作。
随着容器技术的普及,越来越多的软件制品通过 OCI 仓库进行管理,比如 AI 模型,Helm Charts。社区的 OCI Registry As Storage ORAS 项目也应运而生,为多样化的应用制品提供统一的管理、分发能力。Cosign 等软件供应链安全能力,也可以无缝应用于这些软件制品。Kubernetes 社区也在大力推动自身项目的安全基础能力建设。从 1.21 版本开始,所有发布的组件将包含相关 SBOM 信息。KEP-3031 也在推动软件制品的签名机制,防止中间人攻击,确保组件的完整性与可信度。
除了容器镜像,Sonatype 也宣布在其 Maven Central 与 Sigstore 展开合作,来提升 Java 开发流程的供应链安全。
过去阻碍软件供应链应用的最大阻力在于其用户体验,无论是秘钥管理还是镜像 SBOM、签名等元数据管理都非常复杂,需要多个系统相互协同。Sigstore/Cosign 在简化用户体验方面有了长足的进展。然而 Cosign 目前基于约定方式,如将 sha256-{IMAGE_HASH}.sig 作为镜像签名的 tag。这样的设计非常简单,但是缺乏灵活性和扩展能力。OCI - Open Container Initiative 成立了 Reference Types 工作组,目标为 OCI 仓库中的软件制品间的关系提供标准化的描述和查询机制。我们拭目以待,期待可以和 Sigstore 社区共同合作在用户体验的简单性,系统的可扩展性方面达成共识,进一步推动软件供应链安全技术的普及。
软件供应链安全还是一个比较新的领域,很多最佳实践和工具链还不完善。目前 Cosign 等项目还在快速发展中,Keyless 签名机制还未达到生产可用,本文仅供大家学习参考相应的技术架构。
作者:易立
本文为阿里云原创内容,未经允许不得转载