Files
git.stella-ops.org/docs/modules/signer/guides/keyless-signing.md
StellaOps Bot 7792749bb4 feat: Add archived advisories and implement smart-diff as a core evidence primitive
- Introduced new advisory documents for archived superseded advisories, including detailed descriptions of features already implemented or covered by existing sprints.
- Added "Smart-Diff as a Core Evidence Primitive" advisory outlining the treatment of SBOM diffs as first-class evidence objects, enhancing vulnerability verdicts with deterministic replayability.
- Created "Visual Diffs for Explainable Triage" advisory to improve user experience in understanding policy decisions and reachability changes through visual diffs.
- Implemented "Weighted Confidence for VEX Sources" advisory to rank conflicting vulnerability evidence based on freshness and confidence, facilitating better decision-making.
- Established a signer module charter detailing the mission, expectations, key components, and signing modes for cryptographic signing services in StellaOps.
- Consolidated overlapping concepts from triage UI, visual diffs, and risk budget visualization advisories into a unified specification for better clarity and implementation tracking.
2025-12-26 13:01:43 +02:00

9.6 KiB

Keyless Signing Guide

Overview

Keyless signing uses ephemeral X.509 certificates from Sigstore Fulcio, eliminating the need for persistent signing keys. This approach is ideal for CI/CD pipelines where key management is complex and error-prone.

How It Works

┌──────────────┐     ┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ CI Pipeline  │────▶│ OIDC Provider│────▶│ Fulcio       │────▶│ Rekor        │
│              │     │ (GitHub/GL)  │     │ (Sigstore)   │     │ (Sigstore)   │
│ 1. Get token │     │ 2. Issue JWT │     │ 3. Issue cert│     │ 4. Log entry │
│              │     │    (5 min)   │     │    (10 min)  │     │ (permanent)  │
└──────────────┘     └──────────────┘     └──────────────┘     └──────────────┘
        │                                                              │
        │                                                              │
        └───────────── Attestation with cert + Rekor proof ───────────┘
  1. OIDC Token: Pipeline requests identity token from CI platform
  2. Fulcio Certificate: Token exchanged for short-lived signing certificate (~10 min)
  3. Ephemeral Key: Private key exists only in memory during signing
  4. Rekor Logging: Signature logged to transparency log for verification after cert expiry

Key Benefits

Benefit Description
Zero Key Management No secrets to rotate, store, or protect
Identity Binding Signatures tied to OIDC identity (repo, branch, workflow)
Audit Trail All signatures logged to Rekor transparency log
Short-lived Certs Minimizes exposure window (~10 minutes)
Industry Standard Adopted by Kubernetes, npm, PyPI, and major ecosystems

Quick Start

Prerequisites

  1. StellaOps CLI installed
  2. CI platform with OIDC support (GitHub Actions, GitLab CI, Gitea)
  3. Network access to Fulcio and Rekor (or private instances)

GitHub Actions Example

name: Sign Container Image

on:
  push:
    branches: [main]

jobs:
  build-and-sign:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # Required for OIDC
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Build and Push Image
        id: build
        run: |
          docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
          docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
          echo "digest=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/${{ github.repository }}:${{ github.sha }} | cut -d@ -f2)" >> $GITHUB_OUTPUT

      - name: Keyless Sign
        uses: stella-ops/sign-action@v1
        with:
          artifact-digest: ${{ steps.build.outputs.digest }}
          artifact-type: image

CLI Usage

# Sign with ambient OIDC token (in CI environment)
stella attest sign --keyless --artifact sha256:abc123...

# Sign with explicit token
STELLAOPS_OIDC_TOKEN="..." stella attest sign --keyless --artifact sha256:abc123...

# Verify signature (checks Rekor proof)
stella attest verify \
  --artifact sha256:abc123... \
  --certificate-identity "repo:myorg/myrepo:ref:refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com"

Configuration

Signer Configuration

# etc/signer.yaml
signer:
  signing:
    mode: "keyless"
    keyless:
      enabled: true
      fulcio:
        url: "https://fulcio.sigstore.dev"
        timeout: 30s
        retries: 3
      oidc:
        issuer: "https://authority.internal"
        clientId: "signer-keyless"
        useAmbientToken: true
      algorithms:
        preferred: "ECDSA_P256"
        allowed: ["ECDSA_P256", "Ed25519"]
      certificate:
        rootBundlePath: "/etc/stellaops/fulcio-roots.pem"
        validateChain: true
        requireSCT: true

Private Fulcio Instance

For air-gapped or high-security environments, deploy a private Fulcio instance:

signer:
  signing:
    keyless:
      fulcio:
        url: "https://fulcio.internal.example.com"
      oidc:
        issuer: "https://keycloak.internal.example.com/realms/stellaops"
      certificate:
        rootBundlePath: "/etc/stellaops/private-fulcio-roots.pem"

Identity Verification

Identity Constraints

When verifying signatures, specify which identities are trusted:

stella attest verify \
  --artifact sha256:abc123... \
  --certificate-identity "repo:myorg/myrepo:ref:refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com"

Platform Identity Patterns

GitHub Actions

Pattern Matches
repo:org/repo:.* Any ref in repository
repo:org/repo:ref:refs/heads/main Main branch only
repo:org/repo:ref:refs/tags/v.* Version tags
repo:org/repo:environment:production Production environment

Issuer: https://token.actions.githubusercontent.com

GitLab CI

Pattern Matches
project_path:group/project:.* Any ref in project
project_path:group/project:ref_type:branch:ref:main Main branch
project_path:group/project:ref_protected:true Protected refs only

Issuer: https://gitlab.com (or self-hosted URL)

Long-Term Verification

The Problem

Fulcio certificates expire in ~10 minutes. How do you verify signatures months later?

The Solution: Rekor Proofs

At signing time:
┌──────────────────────────────────────────────────────────────┐
│ Signature + Certificate + Signed-Certificate-Timestamp (SCT) │
│                            ↓                                  │
│                 Logged to Rekor                               │
│                            ↓                                  │
│         Merkle Inclusion Proof returned                       │
└──────────────────────────────────────────────────────────────┘

At verification time (even years later):
┌──────────────────────────────────────────────────────────────┐
│ 1. Check signature is valid (using cert public key)          │
│ 2. Check SCT proves cert was logged when valid               │
│ 3. Check Rekor inclusion proof (entry was logged)            │
│ 4. Check signing time was within cert validity window        │
│                            ↓                                  │
│              Signature is valid! ✓                            │
└──────────────────────────────────────────────────────────────┘

Attestation Bundles

For air-gapped verification, StellaOps bundles attestations with proofs:

# Export bundle with Rekor proofs
stella attest export-bundle \
  --image sha256:abc123... \
  --include-proofs \
  --output attestation-bundle.json

# Verify offline
stella attest verify --offline \
  --bundle attestation-bundle.json \
  --artifact sha256:abc123...

Troubleshooting

Common Errors

Error Cause Solution
OIDC token expired Token older than 5 minutes Re-acquire token before signing
Fulcio unavailable Network issues Check connectivity, increase timeout
Certificate chain invalid Wrong Fulcio roots Update root bundle
Identity mismatch Wrong verify constraints Check issuer and identity patterns
Rekor proof missing Logging failed Retry signing, check Rekor status

Debug Mode

# Enable verbose logging
STELLAOPS_LOG_LEVEL=debug stella attest sign --keyless --artifact sha256:...

# Inspect certificate details
stella attest inspect --artifact sha256:... --show-cert

Security Considerations

Best Practices

  1. Always verify identity: Never accept .* as the full identity pattern
  2. Require Rekor proofs: Use --require-rekor for production verification
  3. Pin OIDC issuers: Only trust expected issuers
  4. Use environment constraints: More specific than branch names
  5. Monitor signing activity: Alert on unexpected identities

Threat Model

Threat Mitigation
Stolen OIDC token Short lifetime (~5 min), audience binding
Fulcio compromise Certificate Transparency (SCT), multiple roots
Rekor compromise Multiple witnesses, checkpoints, consistency proofs
Private key theft Ephemeral keys, never persisted

External Resources