Tailscale: OAuth Client Detail
Level 3 (Detail) — OAuth client flow, key minting, and lifecycle.
Concept
TazLab uses two Tailscale OAuth clients to generate short-lived auth keys dynamically, avoiding persistent keys in Terraform state. Both are defined in ephemeral-castle/tailscale/main.tf.
Bootstrap OAuth Client
Used for device authentication (joining TazPod and operator machines to the tailnet).
resource "tailscale_oauth_client" "bootstrap" {
description = "tazlab-bootstrap"
scopes = ["auth_keys", "devices"]
tags = ["tag:tazpod"]
}
- Tags
tag:tazpodare automatically assigned to devices joining with keys from this client - Scopes
auth_keys+devicesallow key creation and device management
Kubernetes Operator OAuth Client
Used by the Tailscale Kubernetes Operator to create proxy devices for Ingress and LoadBalancer exposure.
resource "tailscale_oauth_client" "k8s_operator" {
description = "tazlab-k8s-operator"
scopes = ["devices", "auth_keys", "services"]
tags = ["tag:k8s-operator"]
}
- Scope
servicesis required to avoid bug #19471 (operator misclassifies 404 on/vip-servicesas InvalidOAuth) - Without
servicesscope, the operator cannot create Ingress or LoadBalancer proxy devices
Credential Storage
Client credentials are stored as plain files in ~/secrets/:
| File | Content |
|---|---|
~/secrets/tailscale-oauth-client-id | Bootstrap OAuth client ID |
~/secrets/tailscale-oauth-client-secret | Bootstrap OAuth client secret |
~/secrets/tailscale-operator-client-id | Operator OAuth client ID |
~/secrets/tailscale-operator-client-secret | Operator OAuth client secret |
These are provisioned by the ephemeral-castle creation flow and persist in the operator’s encrypted vault. Operator credentials are also pushed to Vault at secret/tazlab-k8s/static/infra/tailscale/.
Key Minting Flow
File: ~/.local/bin/tazpod-tailscale-up — mint_authkey() function
OAuth credentials → POST /oauth/token → access_token (1h)
│
▼
POST /tailnet/-/keys → auth_key (1h)
│
▼
tailscale up --authkey=<key>
- Reads OAuth client ID + secret from
~/secrets/ - POST to
https://api.tailscale.com/api/v2/oauth/tokenwithgrant_type=client_credentials+scope=auth_keys - Extract
access_tokenfrom response - POST to
https://api.tailscale.com/api/v2/tailnet/-/keyswith:reusable: true,ephemeral: true,preauthorized: truetags: ["tag:tazpod"]expirySeconds: 3600(1 hour)
- Extract
keyfrom response - Export as
TS_AUTHKEYenvironment variable
Fallback
If OAuth client credentials are missing, the script falls back to TAILSCALE_API_KEY + TAILSCALE_TAILNET for direct API key minting.
Key Properties
| Property | Value |
|---|---|
| Reusable | true (one key can join multiple devices) |
| Ephemeral | true (devices are removed on disconnect) |
| Preauthorized | true (no manual approval needed) |
| Expiry | 3600 seconds (1 hour) |
| Tags | tag:tazpod (bootstrap), tag:k8s-operator (operator) |
See Also
- Topic: Operator Connectivity
- Topic: Service Exposure
- Reference: start.sh
- Hub: Tailscale