引言:软件供应链——你最容易忽视的攻击面
作为Linux系统管理员或DevSecOps工程师,你大概已经在SSH加固、防火墙配置、内核防护这些地方花了不少心思。但说实话,有一个同样致命的攻击面很可能一直被你低估了——软件供应链。
2026年2月,Datadog发布的《DevSecOps现状报告》给出了一组让人心里发凉的数据:87%的组织在生产环境中跑着至少一个已知可利用漏洞的软件,中位依赖库版本落后最新主版本278天(去年是215天),42%的服务依赖不再维护的库。更让人担心的是,71%的组织从未将GitHub Actions固定到特定的commit hash——换句话说,CI/CD管道本身就是一个巨大的供应链风险窗口。
从SolarWinds到Log4j,从Codecov到xz-utils后门事件,这些真实案例反复证明了一件事:你软件的安全性,取决于构建它的每一个组件和每一个环节。光靠"扫描+补丁"的老套路,早就不够用了。
现代软件供应链安全需要回答三个根本问题:软件里有什么?(SBOM)、是谁构建的?(Sigstore签名)、是怎么构建的?(SLSA溯源)。这篇文章就从这三个维度出发,给你一套可以直接在Linux环境中落地的完整实战方案。
第一章:SBOM——让软件成分透明化
1.1 什么是SBOM,为什么现在必须要有
SBOM(Software Bill of Materials,软件物料清单)说白了就是软件的"配料表"——一份详细记录了软件中所有组件、库、依赖关系及其版本信息的清单。就像食品包装上的成分表让你知道吃进去的是什么,SBOM让你清楚地知道你的软件里到底有什么。
那为什么SBOM从"最好有"变成了"必须有"?几个关键原因:
- 法规要求:美国行政令14028要求向联邦机构供应软件的厂商必须提供SBOM;欧盟《网络弹性法案》(CRA)同样要求软件产品附带SBOM;PCI DSS 4.0也将SBOM纳入了安全要求
- 漏洞响应加速:还记得Log4j(CVE-2021-44228)爆发时的场景吗?有SBOM的组织能在几分钟内定位所有受影响的服务,而没有SBOM的组织可能花了数周甚至数月才搞清楚
- 第三方风险管理:前面提到42%的服务依赖已停止维护的库——如果你不知道自己的软件里用了什么,根本没法评估这些风险
1.2 SBOM标准格式:SPDX vs CycloneDX
目前业界主要有两种SBOM标准格式,各有各的侧重点。
SPDX(Software Package Data Exchange)由Linux基金会开发维护,是ISO/IEC 5962:2021国际标准。SPDX更侧重许可证合规和知识产权管理,在法律和合规场景下特别有用。支持Tag-Value、JSON、XML、RDF等多种序列化格式。
CycloneDX由OWASP基金会开发,专注安全场景。它在漏洞关联、依赖关系图谱、服务定义等安全相关元数据上更丰富,机器可读性也更强。支持JSON和XML格式。
我个人的选择建议是这样的:如果你的SBOM主要用于安全漏洞管理和DevSecOps流水线,CycloneDX通常更合适;如果需要满足法律合规或许可证审计需求,SPDX更好。好消息是主流工具同时支持两种格式,大不了两种都生成。
1.3 使用Syft生成SBOM
Syft是Anchore开源的SBOM生成工具,支持28+生态系统(包括所有主流Linux发行版的包管理器),算是目前最成熟、使用最广泛的SBOM生成器之一了。
先来安装Syft:
# 方法1:使用官方安装脚本
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# 方法2:在RHEL/Fedora上使用RPM
rpm -ivh https://github.com/anchore/syft/releases/latest/download/syft_*_linux_amd64.rpm
# 方法3:在Debian/Ubuntu上使用DEB
curl -LO https://github.com/anchore/syft/releases/latest/download/syft_*_linux_amd64.deb
sudo dpkg -i syft_*_linux_amd64.deb
# 验证安装
syft version
接下来看看生成SBOM的基本用法:
# 扫描容器镜像并生成CycloneDX JSON格式的SBOM
syft registry:nginx:1.27-alpine -o cyclonedx-json > nginx-sbom.cdx.json
# 扫描容器镜像并生成SPDX JSON格式的SBOM
syft registry:nginx:1.27-alpine -o spdx-json > nginx-sbom.spdx.json
# 同时生成两种格式
syft registry:nginx:1.27-alpine \
-o cyclonedx-json=./nginx-sbom.cdx.json \
-o spdx-json=./nginx-sbom.spdx.json
# 扫描本地目录(项目源码)
syft dir:/opt/myapp -o cyclonedx-json > myapp-sbom.cdx.json
# 扫描本地Docker镜像
syft docker:my-app:latest -o cyclonedx-json > my-app-sbom.cdx.json
# 扫描Linux系统已安装的包
syft dir:/ -o spdx-json > system-sbom.spdx.json
1.4 使用Grype进行SBOM漏洞扫描
生成SBOM之后,下一步自然是检查其中的组件有没有已知漏洞。Grype同样是Anchore开源的漏洞扫描器,跟Syft无缝配合。
# 安装Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# 直接扫描SBOM文件
grype sbom:./nginx-sbom.cdx.json
# 扫描容器镜像(Grype会自动生成SBOM并扫描)
grype registry:nginx:1.27-alpine
# 只显示已修复的漏洞(可操作的结果)
grype sbom:./nginx-sbom.cdx.json --only-fixed
# 以JSON格式输出,便于自动化处理
grype sbom:./nginx-sbom.cdx.json -o json > vulnerability-report.json
# 在CI/CD中设置严重级别阈值——超过Critical则失败
grype sbom:./nginx-sbom.cdx.json --fail-on critical
输出会清楚地列出每个漏洞的CVE编号、影响的包名和版本、严重级别以及是否有修复版本。把这些信息跟SBOM关联起来,你就能精确定位哪些服务受到了影响——这在应急响应时真的能救命。
第二章:Sigstore签名——用密码学证明制品来源
2.1 为什么需要制品签名
知道软件里有什么(SBOM)是第一步,但光这样还不够。你还得回答一个更关键的问题:这个制品确实是你信任的构建系统产出的吗?有没有人在构建和分发过程中动过手脚?
传统的制品签名方案(比如GPG签名)虽然能解决这个问题,但有个大痛点:密钥管理。你需要安全地生成、存储、分发和轮换签名密钥,一旦私钥泄露,所有签名就都不可信了。实际操作中,这个负担大到很多团队干脆就不签名了——坦白说,我完全理解这种无奈。
Sigstore项目就是为了解决这个问题而诞生的。它由OpenSSF(开源安全基金会)管理,提供了一套完全免费的公共基础设施,让签名变得跟HTTPS证书一样简单——你不需要管理密钥,只需要验证身份。
2.2 Sigstore的三大组件
Sigstore由三个核心组件构成,各自解决签名流程中的一个关键环节:
- Fulcio(证书颁发机构):验证签名者的OIDC身份令牌(来自GitHub、Google、Microsoft等身份提供商),签发一个短生命周期的签名证书(有效期仅约10分钟)。这就是"无密钥签名"的核心——私钥是临时的,用完即弃,完全不需要长期管理
- Rekor(透明日志):一个公开的、只追加的透明日志,记录每一次签名事件。任何人都可以查询和审计,确保签名行为不可抵赖、不可篡改。你可以把它类比为TLS生态中的证书透明度日志(CT Log)
- Cosign(客户端工具):Sigstore的命令行工具,负责实际执行签名和验证操作。支持容器镜像、二进制文件、SBOM和任意文件的签名
2.3 安装和使用Cosign
Cosign目前最新稳定版本是v3.x系列(v4正在开发中,会进一步简化CLI):
# 下载Cosign二进制文件(Linux amd64)
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
# 验证下载的Cosign二进制文件的签名
# 先下载签名文件和证书
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64.sig
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64.pem
# 使用Sigstore的公钥验证
cosign verify-blob cosign-linux-amd64 \
--signature cosign-linux-amd64.sig \
--certificate cosign-linux-amd64.pem \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "https://github.com/sigstore/cosign"
# 安装
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
# 验证安装
cosign version
2.4 无密钥签名容器镜像
这是Cosign最核心的使用场景了。从v2.0开始,无密钥签名是默认行为,不再需要那个COSIGN_EXPERIMENTAL环境变量:
# 签名一个容器镜像(无密钥模式)
# 执行后会打开浏览器让你通过OIDC身份提供商认证
cosign sign ghcr.io/myorg/myapp:v1.2.3
# 在CI/CD环境中,使用平台的OIDC令牌(无需交互)
# GitHub Actions会自动提供OIDC令牌
cosign sign ghcr.io/myorg/myapp@sha256:abc123def456...
# 验证镜像签名
cosign verify ghcr.io/myorg/myapp:v1.2.3 \
--certificate-identity "https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
# 签名时附加自定义注解(元数据)
cosign sign --annotations "build-id=12345" \
--annotations "git-sha=$(git rev-parse HEAD)" \
ghcr.io/myorg/myapp:v1.2.3
2.5 签名SBOM并附加到容器镜像
一个特别强大的做法是将SBOM与容器镜像签名关联起来。这样验证者不仅能确认镜像没被篡改,还能拿到经过签名认证的成分清单:
# 第一步:获取镜像的精确摘要(用摘要而非标签引用,避免TOCTOU攻击)
DIGEST=$(crane digest ghcr.io/myorg/myapp:v1.2.3)
IMAGE="ghcr.io/myorg/myapp@${DIGEST}"
# 第二步:生成SBOM
syft ${IMAGE} -o cyclonedx-json > myapp-sbom.cdx.json
# 第三步:使用Cosign将SBOM作为attestation附加到镜像
cosign attest --type cyclonedx \
--predicate myapp-sbom.cdx.json \
${IMAGE}
# 第四步:验证附加的SBOM attestation
cosign verify-attestation ${IMAGE} \
--type cyclonedx \
--certificate-identity "https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
# 提取并查看SBOM内容
cosign verify-attestation ${IMAGE} \
--type cyclonedx \
--certificate-identity "..." \
--certificate-oidc-issuer "..." \
| jq -r '.payload' | base64 -d | jq '.predicate'
第三章:SLSA构建溯源——证明"怎么构建的"
3.1 SLSA框架概述
SLSA(Supply-chain Levels for Software Artifacts,读作"salsa")是Google发起、OpenSSF维护的软件供应链安全框架。它脱胎于Google内部使用了8年以上的"Binary Authorization for Borg"系统,目标是为软件构建过程建立可验证的安全保障。
简单来说,SLSA通过递进式的等级体系,回答了供应链安全中一个核心问题:这个制品真的是按照你声称的方式,从你声称的代码构建出来的吗?
当前稳定版本为SLSA v1.1(v1.2正在公开审查中)。SLSA定义了四个安全等级:
- Level 1(基本构建卫生):构建过程是脚本化的(不是手动操作)、版本控制的,生成基本的溯源记录。这是最低门槛,但已经能排除大量"手动篡改"的攻击场景
- Level 2(可信溯源):在Level 1基础上增加密码学验证——构建来源经过认证,构建日志不可篡改,构建环境经过身份认证
- Level 3(防篡改):要求密封构建(hermetic build,所有依赖显式声明并隔离)、临时构建环境(用完即销毁)、密码学签名的溯源。即使有构建配置访问权限的恶意内部人员也没法伪造溯源——这点很关键
- Level 4(最高保障):在Level 3基础上要求可重现构建——任何人都可以独立重现构建过程,验证产出物完全一致。坦白说,这个级别目前能做到的项目不多,但它代表了供应链安全的终极目标
3.2 SLSA溯源在GitHub Actions中的实现
在实际项目中实现SLSA Level 3溯源,最简单的方式是使用GitHub官方和SLSA框架提供的GitHub Actions。这里介绍两种主要方案。
方案一:使用GitHub原生构建证明(推荐)
GitHub提供了原生的actions/attest-build-provenance Action,通过Sigstore签名生成SLSA溯源:
# .github/workflows/build-and-attest.yml
name: Build, SBOM, and SLSA Attestation
on:
push:
tags: ['v*']
permissions:
contents: read
packages: write
id-token: write # 必需:OIDC令牌用于Sigstore签名
attestations: write # 必需:上传构建证明
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-attest:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
id: build
uses: docker/build-push-action@v6
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
format: cyclonedx-json
output-file: sbom.cdx.json
- name: Attest build provenance
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
- name: Attest SBOM
uses: actions/attest-sbom@v2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
sbom-path: sbom.cdx.json
push-to-registry: true
方案二:使用SLSA GitHub Generator(达到SLSA Build Level 3)
如果你需要严格满足SLSA Build Level 3的要求(隔离的构建环境和签名密钥),可以用SLSA框架官方的Generator:
# .github/workflows/slsa-container.yml
name: SLSA Container Build
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
image: ${{ steps.build.outputs.image }}
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Build and push
id: build
uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
- name: Output image info
id: image-info
run: |
echo "image=ghcr.io/${{ github.repository }}" >> "$GITHUB_OUTPUT"
echo "digest=${{ steps.build.outputs.digest }}" >> "$GITHUB_OUTPUT"
# SLSA溯源生成(在隔离的可信构建环境中运行)
provenance:
needs: build
permissions:
actions: read
id-token: write
packages: write
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
with:
image: ${{ needs.build.outputs.image }}
digest: ${{ needs.build.outputs.digest }}
registry-username: ${{ github.actor }}
secrets:
registry-password: ${{ secrets.GITHUB_TOKEN }}
3.3 验证SLSA溯源
生成溯源只是一半的工作,验证溯源才是让它真正发挥作用的另一半。SLSA提供了专门的验证工具slsa-verifier:
# 安装slsa-verifier
curl -LO https://github.com/slsa-framework/slsa-verifier/releases/latest/download/slsa-verifier-linux-amd64
chmod +x slsa-verifier-linux-amd64
sudo mv slsa-verifier-linux-amd64 /usr/local/bin/slsa-verifier
# 验证容器镜像的SLSA溯源
slsa-verifier verify-image ghcr.io/myorg/myapp:v1.2.3 \
--source-uri github.com/myorg/myapp \
--source-tag v1.2.3
# 验证二进制制品的SLSA溯源
slsa-verifier verify-artifact myapp-linux-amd64 \
--provenance-path myapp-linux-amd64.intoto.jsonl \
--source-uri github.com/myorg/myapp \
--source-tag v1.2.3
# 使用GitHub CLI验证构建证明
gh attestation verify oci://ghcr.io/myorg/myapp:v1.2.3 \
--owner myorg
第四章:在Kubernetes中强制执行供应链策略
4.1 使用Sigstore Policy Controller
生成SBOM、签名制品、生成SLSA溯源——这些都是"供应"端的安全措施。但如果"消费"端不验证呢?那一切就白做了。在Kubernetes环境中,你需要一个准入控制器来强制执行"未签名、未验证的镜像不得部署"的策略。
Sigstore Policy Controller就是干这个的——它是一个Kubernetes准入Webhook,基于Cosign签名和attestation来决定允许还是拒绝Pod创建:
# 安装Sigstore Policy Controller(使用Helm)
helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update
helm install policy-controller sigstore/policy-controller \
--namespace sigstore-system \
--create-namespace \
--set webhook.configurationScope=namespace
# 为目标命名空间启用策略强制执行
kubectl label namespace production \
policy.sigstore.dev/include=true
然后定义一个ClusterImagePolicy,要求所有来自你的registry的镜像必须有有效签名:
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signed-images
spec:
images:
- glob: "ghcr.io/myorg/**"
authorities:
- keyless:
identities:
- issuer: "https://token.actions.githubusercontent.com"
subjectRegExp: "https://github.com/myorg/.*"
ctlog:
url: https://rekor.sigstore.dev
attestations:
- name: must-have-sbom
predicateType: https://cyclonedx.org/bom
policy:
type: cue
data: |
predicateType: "https://cyclonedx.org/bom"
部署这个策略后,任何试图在production命名空间中部署未经Sigstore签名、没有SBOM attestation的镜像的操作都会被直接拒绝。就是这么干脆。
4.2 使用Kyverno进行策略验证
如果你的Kubernetes集群已经在用Kyverno作为策略引擎,也可以拿它来验证Cosign签名和SLSA溯源:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-slsa-provenance
spec:
validationFailureAction: Enforce
background: false
rules:
- name: check-image-signature-and-provenance
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
attestations:
- type: https://slsa.dev/provenance/v1
conditions:
all:
- key: "{{ buildDefinition.buildType }}"
operator: Equals
value: "https://actions.github.io/buildtypes/workflow/v1"
- key: "{{ runDetails.builder.id }}"
operator: Equals
value: "https://github.com/actions/runner"
第五章:完整DevSecOps管道集成
5.1 端到端安全管道架构
好了,让我们把前面所有的拼图拼起来。一个完整的Linux DevSecOps供应链安全管道长这样:
┌─────────────────────────────────────────────────────────┐
│ 开发者提交代码 │
└─────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 阶段1:代码安全扫描 │
│ • Semgrep / CodeQL(SAST静态分析) │
│ • gitleaks(密钥泄露检测) │
│ • 依赖漏洞扫描(Trivy / Grype) │
└─────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 阶段2:安全构建 │
│ • 密封构建(Hermetic Build) │
│ • 固定所有依赖版本和hash │
│ • 使用临时构建环境(GitHub-hosted runner) │
└─────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 阶段3:制品签名与溯源 │
│ • Syft生成SBOM(CycloneDX/SPDX) │
│ • Cosign签名容器镜像 │
│ • Cosign将SBOM作为attestation附加到镜像 │
│ • 生成SLSA Level 3溯源 │
└─────────────┬───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 阶段4:部署时验证 │
│ • Sigstore Policy Controller / Kyverno验证签名 │
│ • 验证SBOM attestation │
│ • 验证SLSA溯源来源和构建者 │
│ • 通过验证后允许部署到Kubernetes │
└─────────────────────────────────────────────────────────┘
5.2 固定GitHub Actions依赖——被忽视的关键步骤
这个问题值得单独拿出来强调一下。前面提到的Datadog报告显示,71%的组织从未将GitHub Actions固定到commit hash,50%的组织在库发布后24小时内就采用新版本。想想这意味着什么——攻击者只需要搞定一个常用的Action仓库,就可能影响数千个下游项目。
正确做法是用commit SHA固定每一个第三方Action:
# 错误做法——用标签引用,可能被上游篡改
- uses: actions/checkout@v4
# 正确做法——用完整的commit SHA固定版本
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# 使用pinact工具自动固定所有Actions
# 安装pinact
go install github.com/suzuki-shunsuke/pinact/cmd/pinact@latest
# 自动为工作流文件中的所有Actions添加SHA固定
pinact run .github/workflows/*.yml
# 使用Dependabot自动更新固定的SHA
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
5.3 完整的GitHub Actions安全工作流示例
下面这个工作流把所有组件整合在了一起,可以作为你的起点模板:
# .github/workflows/secure-supply-chain.yml
name: Secure Supply Chain Pipeline
on:
push:
tags: ['v*']
permissions:
contents: read
packages: write
id-token: write
attestations: write
security-events: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Run Trivy vulnerability scanner on filesystem
uses: aquasecurity/trivy-action@18f2510ee396bbf400402947e7b5ccdaa3cf2e02
with:
scan-type: fs
scan-ref: .
format: sarif
output: trivy-results.sarif
- name: Run Semgrep SAST
uses: semgrep/semgrep-action@713efdd71e4fbabce26168690a59de22aa0c0e8e
with:
config: p/default
build-sign-attest:
needs: security-scan
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Install Cosign
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da
- name: Log in to GHCR
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: build
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
- name: Sign image with Cosign
run: cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
- name: Generate SBOM with Syft
uses: anchore/sbom-action@fc46e51e3555f3f6035f04b12a25160a5815c38c
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
format: cyclonedx-json
output-file: sbom.cdx.json
- name: Scan SBOM for vulnerabilities
uses: anchore/scan-action@2c901d3e0eb3cd4eca87ef30c4ffa78c0a376519
with:
sbom: sbom.cdx.json
fail-build: true
severity-cutoff: critical
- name: Attest SBOM
uses: actions/attest-sbom@115c3be05ff3974bcbd0b9f9ef62b1aaef20e5e2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
sbom-path: sbom.cdx.json
push-to-registry: true
- name: Attest build provenance
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
第六章:本地Linux服务器的供应链安全加固
6.1 验证系统包的签名
供应链安全不只是CI/CD管道的事。你的Linux服务器本身也是软件供应链的消费端,确保系统包管理器的签名验证正常工作,是最基础也最容易被忽略的一环:
# RHEL/CentOS/Fedora:验证RPM包签名
# 查看已导入的GPG密钥
rpm -qa gpg-pubkey*
# 验证特定包的签名
rpm -K nginx-1.27.0-1.el9.x86_64.rpm
# 确保yum/dnf不跳过GPG检查
grep -r "gpgcheck" /etc/yum.repos.d/
# 确保所有仓库配置都有 gpgcheck=1
# Debian/Ubuntu:验证APT包签名
# 查看已信任的密钥
apt-key list 2>/dev/null || gpg --list-keys --keyring /etc/apt/trusted.gpg.d/
# 检查APT源的签名验证状态
apt-get update 2>&1 | grep -i "NO_PUBKEY\|InRelease is not signed"
# 确保APT不允许未签名的仓库
# /etc/apt/apt.conf.d/99security
cat <<EOF | sudo tee /etc/apt/apt.conf.d/99security
APT::Get::AllowUnauthenticated "false";
Acquire::AllowInsecureRepositories "false";
Acquire::AllowDowngradeToInsecureRepositories "false";
EOF
6.2 生成服务器SBOM并持续监控
为生产服务器生成SBOM,再跟漏洞数据库持续比对,是发现供应链风险的一个非常实用的手段:
# 使用Syft生成整个系统的SBOM
syft dir:/ -o cyclonedx-json > /var/lib/sbom/server-$(hostname)-$(date +%Y%m%d).cdx.json
# 使用Grype扫描系统SBOM
grype sbom:/var/lib/sbom/server-$(hostname)-$(date +%Y%m%d).cdx.json \
--only-fixed -o json > /var/lib/sbom/vuln-report-$(date +%Y%m%d).json
# 设置每日自动扫描的cron任务
cat <<'CRON' | sudo tee /etc/cron.daily/sbom-scan
#!/bin/bash
HOSTNAME=$(hostname)
DATE=$(date +%Y%m%d)
SBOM_DIR=/var/lib/sbom
# 生成SBOM
/usr/local/bin/syft dir:/ -o cyclonedx-json > ${SBOM_DIR}/server-${HOSTNAME}-${DATE}.cdx.json
# 扫描漏洞
/usr/local/bin/grype sbom:${SBOM_DIR}/server-${HOSTNAME}-${DATE}.cdx.json \
--only-fixed --fail-on critical -o json > ${SBOM_DIR}/vuln-${HOSTNAME}-${DATE}.json 2>&1
# 如果发现Critical漏洞,发送告警
if [ $? -ne 0 ]; then
echo "Critical vulnerabilities found on ${HOSTNAME}" | \
mail -s "[ALERT] Supply Chain Vulnerability: ${HOSTNAME}" [email protected]
fi
# 清理30天前的旧报告
find ${SBOM_DIR} -name "*.json" -mtime +30 -delete
CRON
chmod +x /etc/cron.daily/sbom-scan
6.3 验证容器镜像后再部署
即使没有Kubernetes准入控制器,在普通Linux服务器上也应该养成一个习惯:拉取镜像后、运行之前,先验证签名。下面这个脚本可以直接拿去用:
# 创建一个部署前验证脚本
cat <<'SCRIPT' | sudo tee /usr/local/bin/secure-docker-run
#!/bin/bash
set -euo pipefail
IMAGE=$1
shift
# 验证Cosign签名
echo "[*] Verifying image signature for ${IMAGE}..."
if ! cosign verify ${IMAGE} \
--certificate-identity-regexp "https://github.com/myorg/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" 2>/dev/null; then
echo "[FAIL] Image signature verification failed! Aborting deployment."
exit 1
fi
echo "[OK] Signature verified."
# 验证SBOM attestation
echo "[*] Verifying SBOM attestation..."
if ! cosign verify-attestation ${IMAGE} \
--type cyclonedx \
--certificate-identity-regexp "https://github.com/myorg/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" 2>/dev/null; then
echo "[WARN] No SBOM attestation found."
fi
# 执行漏洞快速扫描
echo "[*] Scanning for critical vulnerabilities..."
if ! grype ${IMAGE} --fail-on critical 2>/dev/null; then
echo "[FAIL] Critical vulnerabilities detected! Aborting deployment."
exit 1
fi
echo "[OK] No critical vulnerabilities found."
# 一切通过,运行容器
echo "[*] All checks passed. Starting container..."
docker run "$@" ${IMAGE}
SCRIPT
chmod +x /usr/local/bin/secure-docker-run
# 使用方式:
# secure-docker-run ghcr.io/myorg/myapp:v1.2.3 -d -p 8080:8080
常见问题解答(FAQ)
SBOM和SLSA溯源有什么区别?
SBOM回答"软件里有什么"——它是软件组成成分的详细清单,列出了所有依赖库、版本和许可证信息。SLSA溯源回答"软件是怎么构建的"——它证明了构建过程的完整性,包括使用了哪个代码仓库、哪个构建系统、什么时候构建的。两者是互补关系:SBOM提供透明度,SLSA溯源提供构建可信度。一个完整的供应链安全方案应该两者都有。
Sigstore无密钥签名安全吗?私钥立刻销毁,怎么验证?
这个问题问得好。Sigstore无密钥签名的安全性基于一个挺巧妙的设计:签名时,Fulcio CA签发一个绑定你身份和临时公钥的短生命周期证书(约10分钟有效),签名动作和证书都被记录到Rekor透明日志中。验证时,验证者只需要检查三点:(1)签名时间戳是否在证书有效期内;(2)证书是否由Fulcio签发;(3)签名是否在Rekor中有记录。不需要长期保存私钥,因为验证依赖的是证书链和透明日志,而不是原始签名密钥。
小型团队也需要实施SLSA吗?成本如何?
SLSA的设计本身就是递进式的,小型团队完全可以从Level 1开始——只要确保构建过程是脚本化的并生成基本溯源记录就行。如果你用的是GitHub Actions,达到Level 1几乎零成本。想要Level 3也不复杂:用GitHub原生的actions/attest-build-provenance或SLSA GitHub Generator就可以搞定。所有工具和公共基础设施(Sigstore、Rekor、Fulcio)都完全免费。说实话,真正的投入主要是学习和调整工作流的时间,技术门槛并不高。
如何在不使用Kubernetes的传统Linux服务器上实施供应链安全?
不用Kubernetes也完全可以做供应链安全。关键步骤包括:(1)确保系统包管理器的GPG签名验证已启用且正常工作;(2)用Syft为服务器生成SBOM,配合Grype定期扫描漏洞;(3)对于容器化应用,在docker run之前用Cosign验证镜像签名;(4)把这些检查编成自动化脚本集成到部署流程里。上面第六章给出了完整的脚本示例,拿过去改改就能直接用。
SBOM应该多久更新一次?需要为每次构建都生成吗?
最佳实践是每次构建时自动生成SBOM——把它集成到CI/CD管道中,作为构建产出物的一部分。这样SBOM始终和实际部署的软件保持同步。对于生产服务器,建议每天跑一次SBOM生成和漏洞扫描(cron任务就行),因为新CVE天天都在冒出来,即使软件没变,今天安全的组件明天就可能有新的已知漏洞了。