Files
git.stella-ops.org/docs/modules/signer/guides/keyless-signing.md
StellaOps Bot c8f3120174 Add property-based tests for SBOM/VEX document ordering and Unicode normalization determinism
- Implement `SbomVexOrderingDeterminismProperties` for testing component list and vulnerability metadata hash consistency.
- Create `UnicodeNormalizationDeterminismProperties` to validate NFC normalization and Unicode string handling.
- Add project file for `StellaOps.Testing.Determinism.Properties` with necessary dependencies.
- Introduce CI/CD template validation tests including YAML syntax checks and documentation content verification.
- Create validation script for CI/CD templates ensuring all required files and structures are present.
2025-12-26 15:17:15 +02:00

6.8 KiB

Keyless Signing Guide

This guide explains how to configure and use keyless signing with Sigstore Fulcio for CI/CD pipelines.

Overview

Keyless signing eliminates the need to manage long-lived signing keys by using short-lived X.509 certificates (~10 minute TTL) issued by Fulcio based on OIDC identity tokens. This approach:

  • Zero key management: No secrets to rotate or protect
  • Identity-bound signatures: Signatures are cryptographically tied to the CI/CD identity
  • Non-repudiation: Audit trail via Rekor transparency log
  • Industry standard: Compatible with Sigstore ecosystem (cosign, gitsign, etc.)

How It Works

┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ CI Runner   │────▶│ OIDC Token  │────▶│   Fulcio    │────▶│  Ephemeral  │
│ (GitHub/GL) │     │  Provider   │     │     CA      │     │    Cert     │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘
                                                                    │
                                                                    ▼
                                                           ┌─────────────┐
                                                           │ Sign DSSE   │
                                                           │  Envelope   │
                                                           └─────────────┘
  1. CI runner provides OIDC token - GitHub Actions, GitLab CI, etc. provide ambient identity tokens
  2. Token exchanged for certificate - Fulcio validates the OIDC token and issues a short-lived certificate
  3. Ephemeral key generation - A new ECDSA P-256 or Ed25519 key is generated per signing operation
  4. DSSE signing - The payload is signed using the ephemeral key
  5. Certificate attached - The Fulcio certificate is included in the signed bundle for verification

Configuration

Basic Configuration

# etc/signer.yaml
signer:
  signing:
    mode: "keyless"
    keyless:
      enabled: true
      fulcio:
        url: "https://fulcio.sigstore.dev"
        timeout: 30s
        retries: 3
      oidc:
        useAmbientToken: true

Private Fulcio Instance

For air-gapped or private deployments:

signer:
  signing:
    keyless:
      fulcio:
        url: "https://fulcio.internal.example.com"
      certificate:
        rootBundlePath: "/etc/stellaops/fulcio-roots.pem"
        additionalRoots:
          - |
            -----BEGIN CERTIFICATE-----
            MIIBjzCCATSgAwIBAgIRANZl...
            -----END CERTIFICATE-----

Identity Constraints

Restrict which identities are allowed to sign:

signer:
  signing:
    keyless:
      identity:
        expectedIssuers:
          - "https://token.actions.githubusercontent.com"
          - "https://gitlab.com"
        expectedSubjectPatterns:
          - "^https://github\.com/myorg/.*$"
          - "^project_path:mygroup/myproject:.*$"

CI/CD Integration

GitHub Actions

name: Sign Artifacts
on: [push]

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

    steps:
      - uses: actions/checkout@v4

      - name: Install StellaOps CLI
        run: |
          curl -sSL https://get.stella-ops.io | bash

      - name: Sign with keyless mode
        run: |
          stella sign --mode keyless \
            --image ghcr.io/${{ github.repository }}:${{ github.sha }}

GitLab CI

sign:
  image: registry.stella-ops.io/cli:latest
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  script:
    - stella sign --mode keyless --image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Algorithm Support

Algorithm Status Use Case
ECDSA P-256 Preferred Default, widest compatibility
Ed25519 Supported Better performance, growing adoption

Configure preferred algorithm:

signer:
  signing:
    keyless:
      algorithms:
        preferred: "ECDSA_P256"
        allowed: ["ECDSA_P256", "Ed25519"]

Signed Bundle Format

The keyless signing produces a DSSE envelope with embedded certificate:

{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEi...",
  "signatures": [
    {
      "keyid": "",
      "sig": "MEUCIQD..."
    }
  ],
  "certificateChain": [
    "-----BEGIN CERTIFICATE-----\nMIIC...",
    "-----BEGIN CERTIFICATE-----\nMIIB..."
  ],
  "signingMode": "keyless",
  "signingIdentity": {
    "issuer": "https://token.actions.githubusercontent.com",
    "subject": "https://github.com/org/repo/.github/workflows/ci.yml@refs/heads/main"
  }
}

Verification

Bundles signed with keyless mode can be verified using:

# Verify a signed bundle
stella verify --bundle verdict.json \
  --expected-issuer "https://token.actions.githubusercontent.com" \
  --expected-subject "https://github.com/myorg/myrepo/*"

The verification process:

  1. Validates the certificate chain to Fulcio roots
  2. Verifies the signature using the certificate's public key
  3. Checks identity claims match expectations
  4. Optionally validates SCT (Signed Certificate Timestamp)

Troubleshooting

Common Issues

OIDC token not available

  • Ensure id-token: write permission in GitHub Actions
  • Ensure id_tokens is configured in GitLab CI
  • Check ACTIONS_ID_TOKEN_REQUEST_URL environment variable

Fulcio returns 401

  • OIDC token may have expired (default 5-10 min validity)
  • Audience mismatch - ensure token is for sigstore
  • Issuer not trusted by Fulcio instance

Certificate chain validation failed

  • Root certificate bundle may be outdated
  • Private Fulcio instance roots not configured
  • Certificate expired (Fulcio certs are ~10 min TTL)

Debug Logging

Enable verbose logging:

STELLAOPS_LOG_LEVEL=debug stella sign --mode keyless ...

Security Considerations

  1. Ephemeral keys never persist - Keys exist only in memory during signing
  2. Short-lived certificates - ~10 minute validity limits exposure window
  3. Identity verification - Always configure expectedIssuers and expectedSubjectPatterns in production
  4. SCT validation - Enable requireSct: true for public Fulcio instances