110 lines
4.2 KiB
Markdown
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.
|