|
| 1 | +# Authorization |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Fulcio supports optional claims-based authorization that can be configured to restrict certificate issuance based on OIDC token claims. Authorization runs after successful OIDC authentication and before certificate creation, allowing fine-grained access control based on token metadata. |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +The authorization system evaluates configurable rules against OIDC token claims to determine if a certificate request should be approved. This enables scenarios such as: |
| 10 | + |
| 11 | +- Restricting certificate issuance to specific repositories or organizations |
| 12 | +- Allowing only certain CI/CD pipelines to obtain certificates |
| 13 | +- Implementing custom access control policies based on token claims |
| 14 | +- Supporting multi-tenant environments with isolated access |
| 15 | + |
| 16 | +Authorization is **optional** - Fulcio continues to work when no authorization rules are configured. |
| 17 | + |
| 18 | +## How authorization works |
| 19 | + |
| 20 | +``` |
| 21 | +OIDC Token → Authentication → Authorization → Certificate Issuance |
| 22 | +``` |
| 23 | + |
| 24 | +1. **Authentication**: OIDC token is validated (configured OIDC Issuers + valid signature) |
| 25 | +2. **Authorization**: Token claims are evaluated against configured rules |
| 26 | +3. **Certificate Issuance**: Certificate is created if authorization passes or if no authorization rules are defined for the OIDC Issuer |
| 27 | + |
| 28 | +If authorization fails, the request is rejected with HTTP 403 Forbidden. |
| 29 | + |
| 30 | +## Configuration |
| 31 | + |
| 32 | +Authorization rules are configured per OIDC issuer in the Fulcio configuration file: |
| 33 | + |
| 34 | +```yaml |
| 35 | +oidc-issuers: |
| 36 | + https://token.actions.githubusercontent.com: |
| 37 | + issuer-url: https://token.actions.githubusercontent.com |
| 38 | + client-id: sigstore |
| 39 | + type: github-workflow |
| 40 | + authorization-rules: |
| 41 | + - name: "Allow specific organization repositories" |
| 42 | + logic: "AND" |
| 43 | + conditions: |
| 44 | + - field: "repository_owner" |
| 45 | + pattern: "^myorg$" |
| 46 | + - field: "repository" |
| 47 | + pattern: "^myorg/(prod-app|staging-app)$" |
| 48 | + - name: "Allow admin user for any repository" |
| 49 | + logic: "AND" |
| 50 | + conditions: |
| 51 | + - field: "actor" |
| 52 | + pattern: "^admin@myorg\\.com$" |
| 53 | +``` |
| 54 | +
|
| 55 | +### Rule structure |
| 56 | +
|
| 57 | +- **name**: Descriptive name for the rule (used in logging) |
| 58 | +- **logic**: Either "AND" or "OR" to combine conditions |
| 59 | +- **conditions**: Array of field/pattern pairs to evaluate |
| 60 | +
|
| 61 | +### Condition structure |
| 62 | +
|
| 63 | +- **field**: OIDC token claim to evaluate (e.g., "repository", "sub", "email") |
| 64 | +- **pattern**: Regular expression pattern to match against the claim value |
| 65 | +
|
| 66 | +### Evaluation logic |
| 67 | +
|
| 68 | +- **AND logic**: ALL conditions must match for the rule to pass |
| 69 | +- **OR logic**: ANY condition can match for the rule to pass |
| 70 | +- **Rule evaluation**: If ANY rule passes, authorization succeeds |
| 71 | +- **No rules**: If no rules are configured, authorization is skipped |
| 72 | +
|
| 73 | +## Common use cases |
| 74 | +
|
| 75 | +### 1. Repository-based access control |
| 76 | +
|
| 77 | +Restrict certificate issuance to specific GitHub repositories: |
| 78 | +
|
| 79 | +```yaml |
| 80 | +authorization-rules: |
| 81 | + - name: "Production repositories only" |
| 82 | + logic: "AND" |
| 83 | + conditions: |
| 84 | + - field: "repository_owner" |
| 85 | + pattern: "^myorg$" |
| 86 | + - field: "repository" |
| 87 | + pattern: "^myorg/(api|web|mobile)$" |
| 88 | +``` |
| 89 | +
|
| 90 | +### 2. Organization-wide access |
| 91 | +
|
| 92 | +Allow any repository within an organization: |
| 93 | +
|
| 94 | +```yaml |
| 95 | +authorization-rules: |
| 96 | + - name: "Organization members" |
| 97 | + logic: "AND" |
| 98 | + conditions: |
| 99 | + - field: "repository_owner" |
| 100 | + pattern: "^myorg$" |
| 101 | +``` |
| 102 | +
|
| 103 | +### 3. User-based access control |
| 104 | +
|
| 105 | +Allow specific users regardless of repository: |
| 106 | +
|
| 107 | +```yaml |
| 108 | +authorization-rules: |
| 109 | + - name: "Authorized maintainers" |
| 110 | + logic: "OR" |
| 111 | + conditions: |
| 112 | + - field: "actor" |
| 113 | + pattern: "^(alice|bob|charlie)$" |
| 114 | +``` |
| 115 | +
|
| 116 | +### 4. Environment-based access |
| 117 | +
|
| 118 | +Restrict based on deployment environment: |
| 119 | +
|
| 120 | +```yaml |
| 121 | +authorization-rules: |
| 122 | + - name: "Production deployments" |
| 123 | + logic: "AND" |
| 124 | + conditions: |
| 125 | + - field: "job_workflow_ref" |
| 126 | + pattern: "^myorg/[a-zA-Z0-9._-]{1,100}/.github/workflows/production.yaml@refs/heads/main" |
| 127 | + - field: "repository_owner" |
| 128 | + pattern: "^myorg$" |
| 129 | +``` |
| 130 | +
|
| 131 | +### 5. Multiple rule example |
| 132 | +
|
| 133 | +Combine different access patterns: |
| 134 | +
|
| 135 | +```yaml |
| 136 | +authorization-rules: |
| 137 | + - name: "Production repositories" |
| 138 | + logic: "AND" |
| 139 | + conditions: |
| 140 | + - field: "repository_owner" |
| 141 | + pattern: "^myorg$" |
| 142 | + - field: "repository" |
| 143 | + pattern: "^myorg/(api|web)$" |
| 144 | + - name: "Admin override" |
| 145 | + logic: "AND" |
| 146 | + conditions: |
| 147 | + - field: "actor" |
| 148 | + pattern: "^myorg-admin-bot$" |
| 149 | +``` |
| 150 | +
|
| 151 | +## Security considerations |
| 152 | +
|
| 153 | +### Defense in depth |
| 154 | +
|
| 155 | +Authorization provides an additional security layer after OIDC authentication: |
| 156 | +
|
| 157 | +- **Authentication**: Verifies the token is valid and from a trusted issuer |
| 158 | +- **Authorization**: Verifies the authenticated identity should have access |
| 159 | +- **Transparency**: All decisions are logged to the certificate transparency log |
| 160 | +
|
| 161 | +### Regular expression safety |
| 162 | +
|
| 163 | +- Patterns use Go's `regexp` package, which is safe from ReDoS attacks |
| 164 | +- Patterns are compiled once at startup for performance |
| 165 | +- It is recommended to use anchors (`^` and `$`) to prevent partial matches |
| 166 | +- Patterns should be tested thoroughly before deployment |
| 167 | + |
| 168 | +### Token claim validation |
| 169 | + |
| 170 | +- Claims are extracted from authenticated OIDC tokens only |
| 171 | +- Authorization cannot be bypassed by manipulating unauthenticated tokens |
| 172 | +- All claim values are treated as strings for pattern matching |
| 173 | + |
| 174 | +### Configuration validation and server startup |
| 175 | + |
| 176 | +Fulcio prioritizes security over availability when it comes to authorization configuration: |
| 177 | + |
| 178 | +- **Configuration validation**: All regex patterns and rule structures are validated at startup |
| 179 | +- **Fail-secure by design**: Any malformed authorization rules will prevent server startup |
| 180 | + |
| 181 | +This ensures that authorization policies are always correctly applied and prevents accidental security misconfigurations. |
| 182 | + |
| 183 | +### Logging and monitoring |
| 184 | + |
| 185 | +Authorization decisions are logged with structured logging: |
| 186 | + |
| 187 | +``` |
| 188 | +DEBUG authorization/authorizer.go:130 Authorization passed: rule matched |
| 189 | +DEBUG authorization/authorizer.go:145 Authorization denied: no rules matched |
| 190 | +``` |
| 191 | + |
| 192 | +Monitor these logs to detect: |
| 193 | +- Unexpected authorization failures |
| 194 | +- Potential security policy violations |
| 195 | +- Need for rule adjustments |
| 196 | + |
| 197 | +## Troubleshooting |
| 198 | + |
| 199 | +### Server fails to start with authorization configuration errors |
| 200 | + |
| 201 | +Fulcio uses a fail-secure approach. Any malformed authorization configuration will prevent server startup: |
| 202 | + |
| 203 | +1. **Invalid regex patterns**: Check server startup logs for regex compilation errors |
| 204 | +2. **Empty rule names**: Ensure all rules have descriptive names |
| 205 | +3. **Invalid logic operators**: Use only "AND" or "OR" (case-insensitive) |
| 206 | +4. **Missing conditions**: Each rule must have at least one condition |
| 207 | + |
| 208 | +### Authorization always fails |
| 209 | + |
| 210 | +1. Verify OIDC token contains expected claims |
| 211 | +2. Test regex patterns against actual claim values |
| 212 | +3. Ensure at least one rule matches your use case |
| 213 | +4. Check that field names match actual token claims |
| 214 | +4. Start with broad patterns and narrow down |
| 215 | +5. Test regex patterns separately before adding to configuration |
| 216 | + |
| 217 | +### Authorization always passes |
| 218 | + |
| 219 | +1. Verify rules are configured in the correct issuer section |
| 220 | +2. Check that field names match actual token claims |
| 221 | +3. Ensure regex patterns have proper anchors when relevant (`^` and `$`) |
| 222 | +5. Test regex patterns separately before adding to configuration |
| 223 | + |
| 224 | +## Integration with Helm charts |
| 225 | + |
| 226 | +When deploying Fulcio with Helm (see [sigstore/helm-charts](https://github.com/sigstore/helm-charts/tree/main/charts/fulcio)), authorization rules can be configured via values: |
| 227 | + |
| 228 | +```yaml |
| 229 | +fulcio: |
| 230 | + config: |
| 231 | + contents: |
| 232 | + OIDCIssuers: |
| 233 | + "https://token.actions.githubusercontent.com": |
| 234 | + IssuerURL: "https://token.actions.githubusercontent.com" |
| 235 | + ClientID: "sigstore" |
| 236 | + Type: "github-workflow" |
| 237 | + AuthorizationRules: |
| 238 | + - Name: "Allow specific repositories" |
| 239 | + Logic: "AND" |
| 240 | + Conditions: |
| 241 | + - Field: "repository_owner" |
| 242 | + Pattern: "^myorg$" |
| 243 | + - Field: "repository" |
| 244 | + Pattern: "^myorg/allowed-repo$" |
| 245 | +``` |
0 commit comments