consolidation of some of the modules, localization fixes, product advisories work, qa work

This commit is contained in:
master
2026-03-05 03:54:22 +02:00
parent 7bafcc3eef
commit 8e1cb9448d
3878 changed files with 72600 additions and 46861 deletions

View File

@@ -0,0 +1,247 @@
# Keyless Signing Quick Start
Get keyless signing working in your CI/CD pipeline in under 5 minutes.
## Overview
Keyless signing uses your CI platform's OIDC identity to sign artifacts without managing private keys. The signature is bound to your repository, branch, and workflow identity.
```
┌─────────────┐ ┌─────────┐ ┌───────────────┐
│ CI Platform │────▶│ Fulcio │────▶│ Signed Artifact│
│ OIDC Token │ │ Sigstore│ │ + Rekor Entry │
└─────────────┘ └─────────┘ └───────────────┘
```
## GitHub Actions (Fastest)
### Step 1: Add the workflow
Create `.github/workflows/sign.yml`:
```yaml
name: Build and Sign
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 container
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Install StellaOps CLI
run: curl -sL https://get.stella-ops.org/cli | sh
- name: Get OIDC Token
id: oidc
run: |
TOKEN=$(curl -sLS "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=sigstore" \
-H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
| jq -r '.value')
echo "::add-mask::${TOKEN}"
echo "token=${TOKEN}" >> $GITHUB_OUTPUT
- name: Sign container
env:
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
run: |
DIGEST=$(docker inspect ghcr.io/${{ github.repository }}:${{ github.sha }} \
--format='{{index .RepoDigests 0}}' | cut -d@ -f2)
stella attest sign --keyless --artifact "$DIGEST"
```
### Step 2: Push and verify
```bash
git add .github/workflows/sign.yml
git commit -m "Add keyless signing"
git push
```
Check Actions tab - your container is now signed!
---
## GitLab CI (5 minutes)
### Step 1: Update `.gitlab-ci.yml`
```yaml
stages:
- build
- sign
build:
stage: build
image: docker:24
services:
- docker:dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- echo "ARTIFACT_DIGEST=$(docker inspect $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --format='{{index .RepoDigests 0}}' | cut -d@ -f2)" >> build.env
artifacts:
reports:
dotenv: build.env
sign:
stage: sign
image: stella-ops/cli:latest
id_tokens:
STELLAOPS_OIDC_TOKEN:
aud: sigstore
needs:
- build
script:
- stella attest sign --keyless --artifact "$ARTIFACT_DIGEST"
only:
- main
```
### Step 2: Push
```bash
git add .gitlab-ci.yml
git commit -m "Add keyless signing"
git push
```
---
## Verification Gate
Add verification before deployment:
### GitHub Actions
```yaml
deploy:
needs: [build-and-sign]
runs-on: ubuntu-latest
environment: production
steps:
- name: Verify before deploy
run: |
stella attest verify \
--artifact "${{ needs.build-and-sign.outputs.digest }}" \
--certificate-identity "repo:${{ github.repository }}:ref:refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--require-rekor
- name: Deploy
run: kubectl set image deployment/app app=$IMAGE
```
### GitLab CI
```yaml
deploy:
stage: deploy
environment: production
needs:
- sign
script:
- |
stella attest verify \
--artifact "$ARTIFACT_DIGEST" \
--certificate-identity "project_path:$CI_PROJECT_PATH:ref_type:branch:ref:main" \
--certificate-oidc-issuer "https://gitlab.com" \
--require-rekor
- kubectl set image deployment/app app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main
```
---
## Identity Patterns Cheat Sheet
### GitHub Actions
| Pattern | Example |
|---------|---------|
| Any branch | `repo:org/repo:.*` |
| Main only | `repo:org/repo:ref:refs/heads/main` |
| Tags only | `repo:org/repo:ref:refs/tags/v.*` |
| Environment | `repo:org/repo:environment:production` |
**OIDC Issuer:** `https://token.actions.githubusercontent.com`
### GitLab CI
| Pattern | Example |
|---------|---------|
| Any ref | `project_path:group/project:.*` |
| Main only | `project_path:group/project:ref_type:branch:ref:main` |
| Tags only | `project_path:group/project:ref_type:tag:.*` |
| Protected | `project_path:group/project:ref_protected:true` |
**OIDC Issuer:** `https://gitlab.com` (or self-hosted URL)
---
## Using Reusable Workflows
For cleaner pipelines, use StellaOps reusable workflows:
### GitHub Actions
```yaml
jobs:
sign:
uses: stella-ops/workflows/.github/workflows/stellaops-sign.yml@v1
with:
artifact-digest: sha256:abc123...
artifact-type: image
permissions:
id-token: write
verify:
needs: [sign]
uses: stella-ops/workflows/.github/workflows/stellaops-verify.yml@v1
with:
artifact-digest: sha256:abc123...
certificate-identity: "repo:${{ github.repository }}:ref:refs/heads/main"
certificate-oidc-issuer: "https://token.actions.githubusercontent.com"
```
### GitLab CI
```yaml
include:
- project: 'stella-ops/templates'
file: '.gitlab-ci-stellaops.yml'
sign-container:
extends: .stellaops-sign
variables:
ARTIFACT_DIGEST: sha256:abc123...
ARTIFACT_TYPE: image
```
---
## What's Next?
- [Identity Constraints Guide](./identity-constraints.md) - Secure verification patterns
- [Troubleshooting Guide](./keyless-signing-troubleshooting.md) - Common issues and fixes
- [Offline Verification](../airgap/offline-verification.md) - Air-gapped environments
## Need Help?
- Documentation: https://docs.stella-ops.org/
- Issues: https://github.com/stella-ops/stellaops/issues
- Slack: https://stellaops.slack.com/

View File

@@ -0,0 +1,399 @@
# Keyless Signing Troubleshooting Guide
This guide covers common issues when integrating StellaOps keyless signing into CI/CD pipelines.
## Common Errors
### OIDC Token Acquisition Failures
#### Error: "Unable to get OIDC token"
**Symptoms:**
```
Error: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL
```
**Cause:** The workflow doesn't have `id-token: write` permission.
**Solution:**
```yaml
# GitHub Actions
permissions:
id-token: write
contents: read
# GitLab CI
job:
id_tokens:
STELLAOPS_OIDC_TOKEN:
aud: sigstore
```
#### Error: "Token audience mismatch"
**Symptoms:**
```
Error: Token audience 'api://default' does not match expected 'sigstore'
```
**Cause:** OIDC token was requested with wrong audience.
**Solution:**
```yaml
# GitHub Actions
OIDC_TOKEN=$(curl -sLS "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=sigstore" \
-H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}")
# GitLab CI
id_tokens:
STELLAOPS_OIDC_TOKEN:
aud: sigstore # Must be 'sigstore' for Fulcio
```
---
### Fulcio Certificate Errors
#### Error: "Failed to get certificate from Fulcio"
**Symptoms:**
```
Error: error getting certificate from Fulcio: 401 Unauthorized
```
**Causes:**
1. OIDC token expired (tokens are short-lived, typically 5-10 minutes)
2. Fulcio doesn't recognize the OIDC issuer
3. Network connectivity issues to Fulcio
**Solutions:**
1. **Token expiry:** Request token immediately before signing:
```yaml
- name: Get OIDC Token
id: oidc
run: |
# Get fresh token right before signing
OIDC_TOKEN=$(curl -sLS "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=sigstore" \
-H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
| jq -r '.value')
echo "token=${OIDC_TOKEN}" >> $GITHUB_OUTPUT
- name: Sign (immediately after)
env:
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
run: stella attest sign --keyless --artifact "$DIGEST"
```
2. **Unknown issuer:** Ensure your CI platform is supported:
- GitHub Actions: `https://token.actions.githubusercontent.com`
- GitLab.com: `https://gitlab.com`
- Self-hosted GitLab: Must be configured in Fulcio
3. **Network issues:** Check connectivity:
```bash
curl -v https://fulcio.sigstore.dev/api/v2/signingCert
```
#### Error: "Certificate identity not found in token"
**Symptoms:**
```
Error: no matching subject or SAN found in OIDC token
```
**Cause:** Token claims don't include expected identity fields.
**Solution:** Verify token contents:
```bash
# Decode and inspect token (don't do this in production logs)
echo $OIDC_TOKEN | cut -d. -f2 | base64 -d | jq .
```
Expected claims for GitHub Actions:
```json
{
"sub": "repo:org/repo:ref:refs/heads/main",
"iss": "https://token.actions.githubusercontent.com",
"repository": "org/repo",
"ref": "refs/heads/main"
}
```
---
### Rekor Transparency Log Errors
#### Error: "Failed to upload to Rekor"
**Symptoms:**
```
Error: error uploading entry to Rekor: 500 Internal Server Error
```
**Causes:**
1. Rekor service temporarily unavailable
2. Entry too large
3. Network issues
**Solutions:**
1. **Retry with backoff:**
```yaml
- name: Sign with retry
run: |
for i in 1 2 3; do
stella attest sign --keyless --artifact "$DIGEST" && break
echo "Attempt $i failed, retrying in 30s..."
sleep 30
done
```
2. **Check Rekor status:** https://status.sigstore.dev/
3. **Use offline bundle (air-gapped):**
```bash
stella attest sign --keyless --artifact "$DIGEST" --offline-bundle
```
#### Error: "Rekor entry not found"
**Symptoms:**
```
Error: entry not found in transparency log
```
**Cause:** Verification requiring Rekor but entry wasn't logged (offline signing).
**Solution:** Either:
- Sign with Rekor enabled (default)
- Verify without Rekor requirement:
```bash
stella attest verify --artifact "$DIGEST" --skip-rekor
```
---
### Verification Failures
#### Error: "Certificate identity mismatch"
**Symptoms:**
```
Error: certificate identity 'repo:org/repo:ref:refs/heads/feature'
does not match expected 'repo:org/repo:ref:refs/heads/main'
```
**Cause:** Artifact was signed from a different branch/ref than expected.
**Solutions:**
1. **Use regex for flexibility:**
```bash
stella attest verify \
--artifact "$DIGEST" \
--certificate-identity "repo:org/repo:.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
```
2. **Verify expected signing context:**
```bash
# Check what identity was actually used
stella attest inspect --artifact "$DIGEST" --show-identity
```
#### Error: "Certificate OIDC issuer mismatch"
**Symptoms:**
```
Error: certificate issuer 'https://gitlab.com'
does not match expected 'https://token.actions.githubusercontent.com'
```
**Cause:** Artifact was signed by a different CI platform.
**Solution:** Update verification to accept correct issuer:
```bash
# For GitLab-signed artifacts
stella attest verify \
--artifact "$DIGEST" \
--certificate-identity "project_path:org/repo:.*" \
--certificate-oidc-issuer "https://gitlab.com"
```
#### Error: "Signature expired"
**Symptoms:**
```
Error: certificate validity period has expired
```
**Cause:** Fulcio certificates are short-lived (10 minutes). Verification after expiry requires Rekor proof.
**Solution:** Ensure Rekor verification is enabled:
```bash
stella attest verify \
--artifact "$DIGEST" \
--require-rekor \
--certificate-identity "..." \
--certificate-oidc-issuer "..."
```
---
### Platform-Specific Issues
#### GitHub Actions: "Resource not accessible by integration"
**Symptoms:**
```
Error: Resource not accessible by integration
```
**Cause:** GitHub App or token lacks required permissions.
**Solution:** Ensure workflow has correct permissions:
```yaml
permissions:
id-token: write # For OIDC token
contents: read # For checkout
packages: write # If pushing to GHCR
attestations: write # For GitHub attestations
```
#### GitLab CI: "id_tokens not available"
**Symptoms:**
```
Error: STELLAOPS_OIDC_TOKEN variable not set
```
**Cause:** GitLab version doesn't support `id_tokens` or feature is disabled.
**Solutions:**
1. Check GitLab version (requires 15.7+)
2. Enable CI/CD OIDC in project settings:
- Settings > CI/CD > Token Access
- Enable "Allow CI job tokens from the following projects"
3. Use service account as fallback:
```yaml
sign:
script:
- |
if [ -z "$STELLAOPS_OIDC_TOKEN" ]; then
# Fallback to service account
stella attest sign --key "$SIGNING_KEY" --artifact "$DIGEST"
else
stella attest sign --keyless --artifact "$DIGEST"
fi
```
#### Gitea: OIDC Token Format
**Symptoms:**
```
Error: Invalid OIDC token format
```
**Cause:** Gitea Actions uses different token acquisition method.
**Solution:**
```yaml
- name: Get OIDC Token
run: |
# Gitea provides token directly in environment
if [ -n "$ACTIONS_ID_TOKEN" ]; then
echo "token=$ACTIONS_ID_TOKEN" >> $GITHUB_OUTPUT
else
echo "::error::OIDC token not available"
exit 1
fi
```
---
### Network and Connectivity
#### Error: "Connection refused" to Sigstore services
**Symptoms:**
```
Error: dial tcp: connection refused
```
**Cause:** Firewall blocking outbound connections.
**Required endpoints:**
| Service | URL | Purpose |
|---------|-----|---------|
| Fulcio | `https://fulcio.sigstore.dev` | Certificate issuance |
| Rekor | `https://rekor.sigstore.dev` | Transparency log |
| TUF | `https://tuf-repo-cdn.sigstore.dev` | Trust root |
| OIDC | CI platform URL | Token validation |
**Solution:** Allow outbound HTTPS to these endpoints, or use self-hosted Sigstore.
#### Proxy Configuration
```yaml
- name: Sign with proxy
env:
HTTPS_PROXY: http://proxy.internal:8080
NO_PROXY: internal.corp.com
STELLAOPS_OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
run: stella attest sign --keyless --artifact "$DIGEST"
```
---
## Debugging Commands
### Inspect OIDC Token
```bash
# Decode token payload (never log in production)
echo $OIDC_TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq .
```
### Verify Fulcio Connectivity
```bash
curl -v https://fulcio.sigstore.dev/api/v2/configuration
```
### Check Rekor Entry
```bash
# Search by artifact hash
rekor-cli search --sha "sha256:abc123..."
# Get entry details
rekor-cli get --uuid "24296fb24b8ad77a..."
```
### Inspect Attestation
```bash
stella attest inspect \
--artifact "$DIGEST" \
--show-certificate \
--show-rekor-entry
```
### Verbose Signing
```bash
STELLAOPS_LOG_LEVEL=debug stella attest sign --keyless --artifact "$DIGEST"
```
---
## Getting Help
1. **Check service status:** https://status.sigstore.dev/
2. **StellaOps documentation:** https://docs.stella-ops.org/
3. **Sigstore documentation:** https://docs.sigstore.dev/
4. **File an issue:** https://github.com/stella-ops/stellaops/issues
When reporting issues, include:
- CI platform and version
- StellaOps CLI version (`stella --version`)
- Sanitized error output (remove tokens/secrets)
- Relevant workflow configuration

View File

@@ -0,0 +1,230 @@
# 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
```yaml
# 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:
```yaml
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:
```yaml
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
```yaml
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
```yaml
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:
```yaml
signer:
signing:
keyless:
algorithms:
preferred: "ECDSA_P256"
allowed: ["ECDSA_P256", "Ed25519"]
```
## Signed Bundle Format
The keyless signing produces a DSSE envelope with embedded certificate:
```json
{
"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:
```bash
# 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:
```bash
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
## Related Documentation
- [Signer Architecture](../architecture.md)
- [DSSE Envelope Format](../dsse-format.md)
- [CI/CD Gate Integration](../../policy/guides/cicd-gates.md)
- [Sigstore Documentation](https://docs.sigstore.dev/)