guildhouse-spire-plugins/docs/oidc-attestation.md

110 lines
4.2 KiB
Markdown

# OIDC Workload Attestation
How the `oidc-attestor` plugin identifies workloads via OIDC tokens.
## Flow
```
Workload SPIRE Agent oidc-attestor OIDC Provider
| | | |
| FetchX509SVID | | |
|----------------->| | |
| | Attest(pid=1234) | |
| |------------------->| |
| | | GET /.well-known/ |
| | | openid-configuration|
| | |-------------------->|
| | | JWKS endpoint |
| | |<--------------------|
| | | |
| | | Read token from |
| | | /var/run/secrets/ |
| | | oidc/token |
| | | |
| | | Verify signature |
| | | via JWKS |
| | | |
| | Selectors | |
| |<-------------------| |
| X509-SVID | | |
|<-----------------| | |
```
## Token Discovery
The plugin discovers the workload's OIDC token via a configurable path. In Kubernetes, the token is typically projected into the pod filesystem:
```yaml
# Pod spec
volumes:
- name: oidc-token
projected:
sources:
- serviceAccountToken:
audience: spire
expirationSeconds: 3600
path: token
containers:
- name: app
volumeMounts:
- name: oidc-token
mountPath: /var/run/secrets/oidc
readOnly: true
```
Plugin configuration:
```hcl
WorkloadAttestor "guildhouse_oidc" {
plugin_cmd = "/opt/spire/plugins/oidc-attestor"
plugin_data {
issuer = "https://keycloak.guildhouse.example.org/realms/platform"
audience = "spire"
token_path = "/var/run/secrets/oidc/token"
}
}
```
## Token Verification
1. Plugin reads the JWT from the configured path.
2. Fetches the OIDC discovery document from `{issuer}/.well-known/openid-configuration`.
3. Retrieves the JWKS from the `jwks_uri` in the discovery document.
4. Verifies the JWT signature using the matching key from the JWKS.
5. Validates standard claims: `iss` matches configured issuer, `aud` includes configured audience, `exp` is in the future.
## Selector Output
The plugin returns selectors that SPIRE uses to match workloads against registration entries:
| Selector | Source Claim | Example |
|----------|-------------|---------|
| `oidc_attestor:iss:<value>` | `iss` | `oidc_attestor:iss:https://keycloak.example.org/realms/platform` |
| `oidc_attestor:sub:<value>` | `sub` | `oidc_attestor:sub:f47ac10b-58cc-4372-a567-0e02b2c3d479` |
| `oidc_attestor:email:<value>` | `email` | `oidc_attestor:email:operator@guildhouse.coop` |
| `oidc_attestor:group:<value>` | `groups[]` | `oidc_attestor:group:platform-engineers` |
## Registration Entry Matching
To grant an SVID to workloads authenticated via OIDC:
```bash
spire-server entry create \
-spiffeID spiffe://guildhouse.io/ns/prod/sa/web-server \
-parentID spiffe://guildhouse.io/spire/agent/k8s_psat/guildhouse/... \
-selector oidc_attestor:sub:f47ac10b-58cc-4372-a567-0e02b2c3d479
```
Multiple selectors can be combined (AND logic):
```bash
spire-server entry create \
-spiffeID spiffe://guildhouse.io/ns/prod/sa/admin-tool \
-parentID spiffe://guildhouse.io/spire/agent/k8s_psat/guildhouse/... \
-selector oidc_attestor:iss:https://keycloak.example.org/realms/platform \
-selector oidc_attestor:group:platform-engineers
```
## JWKS Caching
The plugin caches JWKS responses for the duration specified by the `Cache-Control` header (or 5 minutes if not present). This avoids hitting the OIDC provider on every attestation.