# 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:/:.*` | `repo:stellaops/scanner:.*` | | Main branch | `repo:/:ref:refs/heads/main` | `repo:stellaops/scanner:ref:refs/heads/main` | | Any branch | `repo:/:ref:refs/heads/.*` | `repo:stellaops/scanner:ref:refs/heads/.*` | | Version tags | `repo:/:ref:refs/tags/v.*` | `repo:stellaops/scanner:ref:refs/tags/v.*` | | Environment | `repo:/:environment:` | `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:/:.*` | `project_path:stellaops/scanner:.*` | | Main branch | `project_path:/:ref_type:branch:ref:main` | Full pattern | | Protected refs | `project_path:/:ref_protected:true` | Full pattern | | Tags | `project_path:/: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 | `/:.*` | `stellaops/scanner:.*` | | Main branch | `/:ref:refs/heads/main` | `stellaops/scanner:ref:refs/heads/main` | | Tags | `/: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/)