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 多年安全流程的开源版本提供了一个安全框架和一组最佳实践,以防止源代码篡改、三方组件漏洞和产品仓库入侵造成的安全威胁。
Aliware
软件供应链安全的基础是透明度。只有了解应用程序是如何构建代码和依赖组件的,我们才能有效地控制应用程序的安全风险。在美国 2021 在年度发布的《关于改善国家网络安全的行政命令》中,特别要求政府软件包括机器可读软件材料清单(Software Bill Of Materials, SBOM)。SBOM 正式记录了构建软件使用的各种组件的详细信息和供应链关系。
美国国家电信和信息管理局(NITA)在 14028 在号政令的要求下,在 2021 年 7 月 12 日发布了《SBOM 该文为各开发工具的组织和制造商提供了最低要素 SBOM 数据格式的参考。在新一代软件开发工具中,越来越多的软件提供了对 SBOM 的支持。
让我们用一个 Golang 的 HTTP Server 以示例应用为例,了解一下 SBOM 概念和使用方法。
$gitclonehttps://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 命令查看应用程序的构建信息,包括软件源、依赖模块、构建参数等。
$goversion-msecure-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 镜像。
$cat.ko.yaml defaultBaseImage: knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/distroless/static:nonroot $ export KO_DOCKER_REPO=knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino $ ko build -B . 2022/07/02 21:05:33 Using base knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/distroless/static:nonroot@sha256:66cd130e90992bebb68b8735a72f8ad154d0cd4a6f3a8b76f1e372467818d1b4 for github.com/denverdino/secure-supply-chain-sample 2022/07/02 21:05:33 Building github.com/denverdino/secure-supply-chain-sample for linux/amd64 2022/07/02 21:05:34 Publishing knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample:latest 2022/07/02 21:05:35 existing blob: sha256:64ef75a9b8ab92a78ea225f1e707dfee0434dcf7cb0e0b5a8b5020e6b737b791 2022/07/02 21:05:35 existing blob: sha256:3f99978937439fa8ef418c3f36e755f10d175ba218dda5f42846a52b8dca002d 2022/07/02 21:05:35 knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample:sha256-cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3.sbom: digest: sha256:000ec91074135a2774c87fe86833c0267713be9e78cc4ea86b86cc42701fd0a size: 368
2022/07/02 21:05:35 Published SBOM knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample:sha256-cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3.sbom
2022/07/02 21:05:36 existing blob: sha256:7d883a3eb1c556752c73dedf88da2c0d3943b9f5c3f763cdd7a813ca766f5486
2022/07/02 21:05:36 existing blob: sha256:250c06f7c38e52dc77e5c7586c3e40280dc7ff9bb9007c396e06d96736cf8542
2022/07/02 21:05:36 existing blob: sha256:1759dab52bf113b8a23d818934e0fb48d6f4528df4d614b6edc1d7dba05a01b3
2022/07/02 21:05:36 existing blob: sha256:70e4c71b62aef82e9650deb85d30f3402b435e07665d775a3f3b700e9e7300cb
2022/07/02 21:05:36 knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample:latest: digest: sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3 size: 751
2022/07/02 21:05:36 Published knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3
knative-dev-registry.cn-hangzhou.cr.aliyuncs.com/denverdino/secure-supply-chain-sample@sha256:cd1ac13d65aa8983f6c10317a09b994cd66fa46aceb1ba1eaf2cab4ee2038ec3
为什么镜像的创建时间变成了 ”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
Aliware
众所周知,我们可以用镜像签名的方式来保障镜像的可追溯性。一般而言,开发者利用私钥对镜像加签,在部署应用时,可以通过 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"}
Aliware
实现真正的端到端的软件供应链安全需要整个行业的努力,近期众多开源社区加大了相关技术的投入和合作。
随着容器技术的普及,越来越多的软件制品通过 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 签名机制还未达到生产可用,本文仅供大家学习参考相应的技术架构。
阿里云容器服务 ACK、容器镜像服务 ACR 在容器安全领域有着深厚的投入。在信通院首次 “云原生安全成熟度”评估中,阿里云取得了国内唯一全域最高等级认证。我们也在和 OCI, Sigstore 等社区合作,持续为企业客户提供更加可信赖、更加易用的软件供应链安全能力。
点击阅读原文,访问容器镜像服务 ACR,了解阿里云云原生交付链管理和镜像安全更多详情。