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.
This commit is contained in:
310
docs/guides/identity-constraints.md
Normal file
310
docs/guides/identity-constraints.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# Identity Constraints for Keyless Verification
|
||||
|
||||
## Overview
|
||||
|
||||
Keyless signing binds cryptographic signatures to OIDC identities. When verifying signatures, you must specify which identities are trusted. This document covers identity constraint patterns for all supported CI/CD platforms.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Certificate Identity
|
||||
|
||||
The certificate identity is the subject claim from the OIDC token, embedded in the Fulcio certificate. It identifies:
|
||||
|
||||
- **Who** created the signature (repository, branch, workflow)
|
||||
- **When** the signature was created (within the certificate validity window)
|
||||
- **Where** the signing happened (CI platform, environment)
|
||||
|
||||
### OIDC Issuer
|
||||
|
||||
The OIDC issuer is the URL of the identity provider that issued the token. Each CI platform has its own issuer:
|
||||
|
||||
| Platform | Issuer URL |
|
||||
|----------|------------|
|
||||
| GitHub Actions | `https://token.actions.githubusercontent.com` |
|
||||
| GitLab CI (SaaS) | `https://gitlab.com` |
|
||||
| GitLab CI (Self-hosted) | `https://your-gitlab-instance.com` |
|
||||
| Gitea | `https://your-gitea-instance.com` |
|
||||
|
||||
### Verification Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Verification Process │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. Extract certificate from attestation │
|
||||
│ └─▶ Contains: subject, issuer, SAN, validity period │
|
||||
│ │
|
||||
│ 2. Validate certificate chain │
|
||||
│ └─▶ Chains to trusted Fulcio root │
|
||||
│ │
|
||||
│ 3. Check OIDC issuer │
|
||||
│ └─▶ Must match --certificate-oidc-issuer │
|
||||
│ │
|
||||
│ 4. Check certificate identity │
|
||||
│ └─▶ Subject must match --certificate-identity pattern │
|
||||
│ │
|
||||
│ 5. Verify Rekor inclusion (if required) │
|
||||
│ └─▶ Signature logged during certificate validity │
|
||||
│ │
|
||||
│ 6. Verify signature │
|
||||
│ └─▶ Signature valid for artifact digest │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Platform-Specific Patterns
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
GitHub Actions OIDC tokens include rich context about the workflow execution.
|
||||
|
||||
#### Token Claims
|
||||
|
||||
| Claim | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `sub` | Subject (identity) | `repo:org/repo:ref:refs/heads/main` |
|
||||
| `repository` | Full repository name | `org/repo` |
|
||||
| `repository_owner` | Organization/user | `org` |
|
||||
| `ref` | Git ref | `refs/heads/main` |
|
||||
| `ref_type` | Ref type | `branch` or `tag` |
|
||||
| `job_workflow_ref` | Workflow file | `.github/workflows/release.yml@refs/heads/main` |
|
||||
| `environment` | Deployment environment | `production` |
|
||||
|
||||
#### Identity Patterns
|
||||
|
||||
| Constraint | Pattern | Example |
|
||||
|------------|---------|---------|
|
||||
| Any ref | `repo:<owner>/<repo>:.*` | `repo:stellaops/scanner:.*` |
|
||||
| Main branch | `repo:<owner>/<repo>:ref:refs/heads/main` | `repo:stellaops/scanner:ref:refs/heads/main` |
|
||||
| Any branch | `repo:<owner>/<repo>:ref:refs/heads/.*` | `repo:stellaops/scanner:ref:refs/heads/.*` |
|
||||
| Version tags | `repo:<owner>/<repo>:ref:refs/tags/v.*` | `repo:stellaops/scanner:ref:refs/tags/v.*` |
|
||||
| Environment | `repo:<owner>/<repo>:environment:<env>` | `repo:stellaops/scanner:environment:production` |
|
||||
| Workflow | (use SAN) | N/A |
|
||||
|
||||
#### Examples
|
||||
|
||||
```bash
|
||||
# Accept only main branch
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "repo:stellaops/scanner:ref:refs/heads/main" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
# Accept main or release branches
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "repo:stellaops/scanner:ref:refs/heads/(main|release/.*)" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
# Accept any version tag
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "repo:stellaops/scanner:ref:refs/tags/v[0-9]+\.[0-9]+\.[0-9]+.*" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
# Accept production environment only
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "repo:stellaops/scanner:environment:production" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
```
|
||||
|
||||
### GitLab CI
|
||||
|
||||
GitLab CI provides OIDC tokens with project and pipeline context.
|
||||
|
||||
#### Token Claims
|
||||
|
||||
| Claim | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `sub` | Subject | `project_path:group/project:ref_type:branch:ref:main` |
|
||||
| `project_path` | Full project path | `stellaops/scanner` |
|
||||
| `namespace_path` | Namespace | `stellaops` |
|
||||
| `ref` | Git ref | `main` |
|
||||
| `ref_type` | Ref type | `branch` or `tag` |
|
||||
| `ref_protected` | Protected ref | `true` or `false` |
|
||||
| `environment` | Environment name | `production` |
|
||||
| `pipeline_source` | Trigger source | `push`, `web`, `schedule` |
|
||||
|
||||
#### Identity Patterns
|
||||
|
||||
| Constraint | Pattern | Example |
|
||||
|------------|---------|---------|
|
||||
| Any ref | `project_path:<group>/<project>:.*` | `project_path:stellaops/scanner:.*` |
|
||||
| Main branch | `project_path:<group>/<project>:ref_type:branch:ref:main` | Full pattern |
|
||||
| Protected refs | `project_path:<group>/<project>:ref_protected:true` | Full pattern |
|
||||
| Tags | `project_path:<group>/<project>:ref_type:tag:ref:.*` | Full pattern |
|
||||
|
||||
#### Examples
|
||||
|
||||
```bash
|
||||
# Accept main branch only
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "project_path:stellaops/scanner:ref_type:branch:ref:main" \
|
||||
--certificate-oidc-issuer "https://gitlab.com"
|
||||
|
||||
# Accept any protected ref
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "project_path:stellaops/scanner:ref_protected:true.*" \
|
||||
--certificate-oidc-issuer "https://gitlab.com"
|
||||
|
||||
# Self-hosted GitLab
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "project_path:mygroup/myproject:.*" \
|
||||
--certificate-oidc-issuer "https://gitlab.internal.example.com"
|
||||
```
|
||||
|
||||
### Gitea
|
||||
|
||||
Gitea OIDC tokens follow a similar pattern to GitHub Actions.
|
||||
|
||||
#### Token Claims
|
||||
|
||||
| Claim | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `sub` | Subject | `org/repo:ref:refs/heads/main` |
|
||||
| `repository` | Repository path | `org/repo` |
|
||||
| `ref` | Git ref | `refs/heads/main` |
|
||||
|
||||
#### Identity Patterns
|
||||
|
||||
| Constraint | Pattern | Example |
|
||||
|------------|---------|---------|
|
||||
| Any ref | `<org>/<repo>:.*` | `stellaops/scanner:.*` |
|
||||
| Main branch | `<org>/<repo>:ref:refs/heads/main` | `stellaops/scanner:ref:refs/heads/main` |
|
||||
| Tags | `<org>/<repo>:ref:refs/tags/.*` | `stellaops/scanner:ref:refs/tags/.*` |
|
||||
|
||||
#### Examples
|
||||
|
||||
```bash
|
||||
# Accept main branch
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "stella-ops.org/git.stella-ops.org:ref:refs/heads/main" \
|
||||
--certificate-oidc-issuer "https://git.stella-ops.org"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Security Recommendations
|
||||
|
||||
1. **Always Constrain to Repository**
|
||||
|
||||
Never accept wildcards that could match any repository:
|
||||
|
||||
```bash
|
||||
# BAD - accepts any repository
|
||||
--certificate-identity "repo:.*"
|
||||
|
||||
# GOOD - specific repository
|
||||
--certificate-identity "repo:stellaops/scanner:.*"
|
||||
```
|
||||
|
||||
2. **Prefer Branch/Tag Constraints for Production**
|
||||
|
||||
```bash
|
||||
# Better - only main branch
|
||||
--certificate-identity "repo:stellaops/scanner:ref:refs/heads/main"
|
||||
|
||||
# Even better - only signed tags
|
||||
--certificate-identity "repo:stellaops/scanner:ref:refs/tags/v.*"
|
||||
```
|
||||
|
||||
3. **Use Environment Constraints When Available**
|
||||
|
||||
```bash
|
||||
# Most specific - production environment only
|
||||
--certificate-identity "repo:stellaops/scanner:environment:production"
|
||||
```
|
||||
|
||||
4. **Always Require Rekor Proofs**
|
||||
|
||||
```bash
|
||||
# Always include --require-rekor for production
|
||||
stella attest verify \
|
||||
--artifact sha256:... \
|
||||
--certificate-identity "..." \
|
||||
--certificate-oidc-issuer "..." \
|
||||
--require-rekor
|
||||
```
|
||||
|
||||
5. **Pin Trusted Issuers**
|
||||
|
||||
Only trust expected OIDC issuers. Never accept `.*` for issuer.
|
||||
|
||||
### Common Patterns
|
||||
|
||||
#### Multi-Environment Trust
|
||||
|
||||
```yaml
|
||||
# GitHub Actions - Different constraints per environment
|
||||
staging:
|
||||
identity: "repo:myorg/myrepo:ref:refs/heads/.*"
|
||||
|
||||
production:
|
||||
identity: "repo:myorg/myrepo:ref:refs/(heads/main|tags/v.*)"
|
||||
```
|
||||
|
||||
#### Cross-Repository Trust
|
||||
|
||||
```bash
|
||||
# Trust signatures from multiple repositories
|
||||
stella attest verify \
|
||||
--artifact sha256:... \
|
||||
--certificate-identity "repo:myorg/(repo1|repo2|repo3):ref:refs/heads/main" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
```
|
||||
|
||||
#### Organization-Wide Trust
|
||||
|
||||
```bash
|
||||
# Trust any repository in organization (use with caution)
|
||||
stella attest verify \
|
||||
--artifact sha256:... \
|
||||
--certificate-identity "repo:myorg/.*:ref:refs/heads/main" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| `identity mismatch` | Pattern doesn't match certificate subject | Check ref format (refs/heads/ vs branch name) |
|
||||
| `issuer mismatch` | Wrong OIDC issuer URL | Use correct issuer for platform |
|
||||
| `certificate expired` | Signing cert expired, no Rekor proof | Ensure `--require-rekor` and Rekor was used at signing |
|
||||
| `no attestations found` | Attestation not attached to artifact | Verify attestation was pushed to registry |
|
||||
|
||||
### Debugging Identity Patterns
|
||||
|
||||
```bash
|
||||
# Inspect certificate to see actual identity
|
||||
stella attest inspect \
|
||||
--artifact sha256:... \
|
||||
--show-cert
|
||||
|
||||
# Expected output:
|
||||
# Certificate Subject: repo:stellaops/scanner:ref:refs/heads/main
|
||||
# Certificate Issuer: https://token.actions.githubusercontent.com
|
||||
# Certificate SAN: https://github.com/stellaops/scanner/.github/workflows/release.yml@refs/heads/main
|
||||
```
|
||||
|
||||
### Testing Patterns
|
||||
|
||||
```bash
|
||||
# Test pattern matching locally
|
||||
echo "repo:myorg/myrepo:ref:refs/heads/main" | \
|
||||
grep -E "repo:myorg/myrepo:ref:refs/heads/(main|develop)"
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Keyless Signing Guide](../modules/signer/guides/keyless-signing.md)
|
||||
- [GitHub Actions Templates](../../.github/workflows/examples/)
|
||||
- [GitLab CI Templates](../../deploy/gitlab/examples/)
|
||||
- [Sigstore Documentation](https://docs.sigstore.dev/)
|
||||
247
docs/guides/keyless-signing-quickstart.md
Normal file
247
docs/guides/keyless-signing-quickstart.md
Normal 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/
|
||||
399
docs/guides/keyless-signing-troubleshooting.md
Normal file
399
docs/guides/keyless-signing-troubleshooting.md
Normal 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
|
||||
Reference in New Issue
Block a user