- 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.
6.8 KiB
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 │
└─────────────┘
- CI runner provides OIDC token - GitHub Actions, GitLab CI, etc. provide ambient identity tokens
- Token exchanged for certificate - Fulcio validates the OIDC token and issues a short-lived certificate
- Ephemeral key generation - A new ECDSA P-256 or Ed25519 key is generated per signing operation
- DSSE signing - The payload is signed using the ephemeral key
- 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:
- Validates the certificate chain to Fulcio roots
- Verifies the signature using the certificate's public key
- Checks identity claims match expectations
- 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
- Ephemeral keys never persist - Keys exist only in memory during signing
- Short-lived certificates - ~10 minute validity limits exposure window
- Identity verification - Always configure expectedIssuers and expectedSubjectPatterns in production
- SCT validation - Enable requireSct: true for public Fulcio instances