WIF & workload identity
Short-lived upstream tokens via token_file sidecars, OpenAI WIF, GCP, AWS/Azure brokers, and GitHub Actions OIDC.
Run the gateway without long-lived sk-… keys in the process environment by feeding short-lived access tokens from outside the hot path.
What it is
The gateway is stateless and does not implement cloud STS / IMDS. Something else (sidecar, init container, CI step, Vault agent) obtains a token; the gateway only consumes it.
| Mechanism | Config |
|---|---|
| OAuth2 client / refresh | auth: oauth2 — see OAuth & upstream auth |
| Google SA JSON JWT | auth: service_account + service_account_file |
| Plain access token file | auth: adc + token_file |
| In-process inject | proxy.Server.SetTokenSource |
Pattern A — token_file (universal)
A sidecar refreshes a file the gateway can read:
providers:
openai:
kind: openai
base_url: "https://api.openai.com/v1"
auth: adc
token_file: /var/run/secrets/openai-access-tokenFile format
- Raw access token only (no
Bearerprefix) - Whitespace trimmed
- Cached ~2 minutes, then re-read (rotation-friendly)
- Mount read-only, mode
0600 - Never log file contents
Works for OpenAI WIF, cloud OIDC exchanges, Vault agents — anything that can write a token file.
Kubernetes sketch
volumeMounts:
- name: provider-token
mountPath: /var/run/secrets
readOnly: true
# sidecar writes openai-access-token into the shared volumePattern B — OpenAI workload identity federation
- Configure OpenAI project workload identity (OIDC → OpenAI access token).
- Exchange the cloud OIDC JWT for an OpenAI access token (sidecar or CI).
- Write the access token to
token_file, or useauth: oauth2against your broker’s token URL.
auth: oauth2
oauth:
token_url: "https://your-broker.example/token"
client_id_env: BROKER_CLIENT_ID
client_secret_env: BROKER_CLIENT_SECRETGitHub Actions sketch
permissions:
id-token: write
contents: read
steps:
- name: Exchange OIDC → provider access token
run: |
# use your broker / OpenAI WIF exchange here
echo "$OPENAI_ACCESS_TOKEN" > /tmp/oai.token
chmod 600 /tmp/oai.token
- name: Run gateway with token_file
run: ./llm-gateway -config gateway.yamlDo not commit tokens or upload them as artifacts.
Pattern C — Google Cloud (Vertex)
Service account JSON (simple)
providers:
vertex:
kind: google
base_url: "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT/locations/us-central1/publishers/google"
auth: service_account
service_account_file: /secrets/vertex-sa.jsonBuild the base URL with config.VertexBaseURL(project, location) in Go, or copy from Vertex AI dual-path.
GKE Workload Identity (no JSON key)
- Bind Kubernetes SA → Google SA (
roles/aiplatform.useror tighter). - Sidecar / metadata helper writes access token to
token_file. - Config:
auth: adc
token_file: /var/run/secrets/gcp-access-tokenGOOGLE_APPLICATION_CREDENTIALS pointing at SA JSON also auto-wires for auth: adc.
Pattern D — AWS / Azure → provider token
The gateway does not call STS/IMDS.
- Use IRSA / Managed Identity to get a cloud credential.
- Exchange via your broker for the LLM provider access token.
- Feed the gateway via
token_fileorauth: oauth2.
Keep exchange out of the per-request path (sidecar refresh loop).
Pattern E — GitHub Actions OIDC
GHA OIDC JWT → broker / vendor token URL → access token → token_file or env- Restrict
aud/subon the trust policy. - Short TTL; never upload tokens as workflow artifacts.
Multi-tenant vs operator-held
| Mode | Use when |
|---|---|
oauth2 / SA / token_file |
Operator holds the credential; clients use edge auth only |
client_bearer |
Each client presents their own upstream OAuth token |
api_key_env |
Classic static keys |
Threat model
- Process with SA keys or refresh tokens is high privilege.
- Mis-mounted
token_filecan be world-readable — use secret volumes. - Prefer short-lived access tokens and rotate aggressively.