- 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.
274 lines
9.6 KiB
Markdown
274 lines
9.6 KiB
Markdown
# 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
|
|
|
|
```yaml
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```yaml
|
|
# 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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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 |
|
|
|
|
## Related Documentation
|
|
|
|
- [Signer Architecture](../architecture.md)
|
|
- [Attestor Bundle Format](../../attestor/bundle-format.md)
|
|
- [Air-Gap Verification](../../../airgap/attestation-verification.md)
|
|
- [CI/CD Integration](../../../guides/cicd-signing.md)
|
|
|
|
## External Resources
|
|
|
|
- [Sigstore Documentation](https://docs.sigstore.dev/)
|
|
- [Fulcio Overview](https://docs.sigstore.dev/certificate_authority/overview/)
|
|
- [Rekor Transparency Log](https://docs.sigstore.dev/logging/overview/)
|
|
- [cosign Keyless Signing](https://docs.sigstore.dev/signing/quickstart/)
|