Linux软件供应链安全实战:Sigstore签名、SBOM生成与SLSA构建溯源完整指南

从SBOM生成到Sigstore无密钥签名,再到SLSA构建溯源,一套可以直接在Linux环境中落地的软件供应链安全实战方案。涵盖GitHub Actions集成、Kubernetes策略强制执行和传统服务器加固。

引言:软件供应链——你最容易忽视的攻击面

作为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天天都在冒出来,即使软件没变,今天安全的组件明天就可能有新的已知漏洞了。

关于作者 Editorial Team

Our team of expert writers and editors.