- 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.
11 KiB
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
# 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
# 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
# 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
-
Always Constrain to Repository
Never accept wildcards that could match any repository:
# BAD - accepts any repository --certificate-identity "repo:.*" # GOOD - specific repository --certificate-identity "repo:stellaops/scanner:.*" -
Prefer Branch/Tag Constraints for Production
# 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.*" -
Use Environment Constraints When Available
# Most specific - production environment only --certificate-identity "repo:stellaops/scanner:environment:production" -
Always Require Rekor Proofs
# Always include --require-rekor for production stella attest verify \ --artifact sha256:... \ --certificate-identity "..." \ --certificate-oidc-issuer "..." \ --require-rekor -
Pin Trusted Issuers
Only trust expected OIDC issuers. Never accept
.*for issuer.
Common Patterns
Multi-Environment Trust
# 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
# 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
# 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
# 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
# Test pattern matching locally
echo "repo:myorg/myrepo:ref:refs/heads/main" | \
grep -E "repo:myorg/myrepo:ref:refs/heads/(main|develop)"