- 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.
9.6 KiB
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 ───────────┘
- OIDC Token: Pipeline requests identity token from CI platform
- Fulcio Certificate: Token exchanged for short-lived signing certificate (~10 min)
- Ephemeral Key: Private key exists only in memory during signing
- 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
- StellaOps CLI installed
- CI platform with OIDC support (GitHub Actions, GitLab CI, Gitea)
- 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
- Always verify identity: Never accept
.*as the full identity pattern - Require Rekor proofs: Use
--require-rekorfor production verification - Pin OIDC issuers: Only trust expected issuers
- Use environment constraints: More specific than branch names
- 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 |