6.2 KiB
6.2 KiB
CI SBOM Attestation Pipeline Guide
Overview
This guide explains how to integrate Stella Ops' SBOM canonicalization, DSSE attestation, and VEX mapping checks into your CI/CD pipeline using Gitea Actions workflow templates.
The pipeline consists of three stages:
- SBOM Canonicalization - Validate schema and compute deterministic canonical_id
- DSSE Attest + Verify - Sign attestation and verify transparency log inclusion
- VEX Mapping - Validate VEX documents and verify artifact targeting
Prerequisites
- Gitea Actions runner with the Stella Ops CI image (
devops/docker/Dockerfile.ci) - Tools:
sbom-utility,cosign,rekor-cli,python3,ajv-cli(all included in CI image) - For keyless signing: OIDC token provider (Gitea Actions built-in)
- For keyed signing:
COSIGN_PRIVATE_KEY_B64secret configured
Quick Start
Stage 1: SBOM Canonicalization Check
Add to your workflow:
jobs:
sbom-check:
uses: ./.gitea/workflows/templates/sbom-canonicalization-check.yml
with:
bom_path: sbom.json
# Optional: pin to a known canonical_id for regression testing
# expected_canonical_id: 'sha256:abc123...'
What it does:
- Validates the SBOM against the CycloneDX 1.7 JSON schema
- Computes
canonical_id := sha256(JCS(sbom.json))per RFC 8785 - Verifies canonicalization is deterministic (computes twice, asserts match)
- Optionally checks against a known
expected_canonical_idfor regression
Outputs:
canonical_id- The computed canonical identifier (e.g.,sha256:abc123...)validation_result- Schema validation result (passorfail)
Stage 2: DSSE Attestation + Verification
jobs:
attest:
needs: sbom-check
uses: ./.gitea/workflows/templates/dsse-attest-verify-check.yml
with:
subject_ref: 'ghcr.io/org/repo@sha256:...'
predicate_path: sbom.json
signing_mode: keyless # or 'key'
predicate_type: 'https://cyclonedx.org/bom'
# skip_rekor: true # For air-gapped environments
What it does:
- Signs the SBOM as a DSSE/in-toto attestation using cosign
- Verifies the attestation signature
- Validates Rekor transparency log inclusion proof
Signing Modes:
| Mode | Description | When to Use |
|---|---|---|
keyless |
Fulcio/OIDC ephemeral certificate | CI runners with OIDC (default) |
key |
Cosign key pair (PEM) | Air-gapped, self-managed keys |
Stage 3: VEX Mapping Check
jobs:
vex-check:
needs: sbom-check
uses: ./.gitea/workflows/templates/vex-mapping-check.yml
with:
vex_path: vex.json
vex_format: openvex # or 'cyclonedx'
canonical_id: ${{ needs.sbom-check.outputs.canonical_id }}
What it does:
- Validates the VEX document against its schema (OpenVEX or CycloneDX VEX)
- Asserts required fields:
status,vulnerability,product - Verifies the VEX targets match the expected
canonical_id
Complete Pipeline Example
name: SBOM Evidence Pipeline
on:
push:
branches: [main]
pull_request:
jobs:
build-and-scan:
runs-on: ubuntu-latest
outputs:
sbom_path: sbom.json
image_ref: ${{ steps.build.outputs.image_ref }}
steps:
- uses: actions/checkout@v4
- name: Build image
id: build
run: |
# Build your container image
docker build -t myapp .
echo "image_ref=ghcr.io/org/myapp@sha256:..." >> $GITHUB_OUTPUT
- name: Generate SBOM
run: stella scan --image myapp --output-sbom sbom.json --format cyclonedx-1.7
- uses: actions/upload-artifact@v4
with:
name: evidence
path: sbom.json
canonicalize:
needs: build-and-scan
uses: ./.gitea/workflows/templates/sbom-canonicalization-check.yml
with:
bom_path: sbom.json
attest:
needs: [build-and-scan, canonicalize]
if: github.event_name != 'pull_request'
uses: ./.gitea/workflows/templates/dsse-attest-verify-check.yml
with:
subject_ref: ${{ needs.build-and-scan.outputs.image_ref }}
predicate_path: sbom.json
signing_mode: keyless
vex-check:
needs: canonicalize
if: hashFiles('vex.json') != ''
uses: ./.gitea/workflows/templates/vex-mapping-check.yml
with:
vex_path: vex.json
vex_format: openvex
canonical_id: ${{ needs.canonicalize.outputs.canonical_id }}
Air-Gap Mode
For environments without internet access:
- Skip Rekor: Set
skip_rekor: truein the attestation workflow - Use keyed signing: Pre-distribute cosign keys, use
signing_mode: key - Bundle schemas locally: Schemas are already in
docs/schemas/(no network fetch) - Verification: Use bundled checkpoints for offline Rekor verification:
stella attest verify --offline --checkpoint-bundle /path/to/checkpoints
Failure Modes
| Assertion | Failure | What to Do |
|---|---|---|
| Schema validation | Invalid CycloneDX | Check SBOM generator version; validate against docs/schemas/cyclonedx-bom-1.7.schema.json |
| Determinism check | Hash differs between runs | Non-deterministic SBOM generation; check for timestamps, random values, unstable ordering |
| Regression check | canonical_id changed | SBOM content changed; update expected_canonical_id or investigate drift |
| Attestation signing | Cosign error | Check signing key/OIDC token; verify registry access |
| Attestation verification | Signature invalid | Key mismatch or tampered attestation; re-sign |
| Rekor proof | Entry not found | May be pending; retry after 30s; check Rekor connectivity |
| VEX schema | Invalid document | Check VEX format matches vex_format input; validate manually |
| VEX field assertions | Missing status | VEX statements must have status field; check VEX generator |
| Target mismatch | canonical_id not in VEX | VEX document targets different artifact; verify PURL/product matching |
Related Documentation
- Contract:
docs/contracts/canonical-sbom-id-v1.md- Canonical ID computation rules - Contract:
docs/contracts/artifact-canonical-record-v1.md- Unified evidence record - Module:
docs/modules/attestor/architecture.md- Attestor DSSE pipeline - Module:
docs/modules/signer/architecture.md- Signing modes and configuration