- 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.
311 lines
11 KiB
Markdown
311 lines
11 KiB
Markdown
# 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/)
|