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

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

Linux供应链安全指南2026:Sigstore与SBOM

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

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

关于作者 Adaeze Okonkwo

Adaeze runs platform security at a Series C fintech in Lagos, where she spent the last three years migrating a sprawling Debian estate to immutable Flatcar nodes on bare-metal Kubernetes. Before that she was a senior SRE at Andela for five years and did a two-year stint at Interswitch hardening PCI-DSS Linux hosts the old-fashioned way - Lynis scans, Bastille, and a lot of shell scripts that probably shouldn't have existed. She holds OSCP, CKS, and is one of the few people who has actually read the entire CIS Debian 12 benchmark cover to cover. She maintains a public set of OpenSCAP profiles tuned for African ISP environments where bandwidth assumptions matter. Adaeze writes about the operational side of Linux security: SSH key rotation that actually happens, log shipping that survives a reboot, and incident response runbooks people will read at 3am.