Commit Graph
34 Commits
Author SHA1 Message Date
Kristoffer Dalby 4317c5a35d api/v2: add the acl/validate endpoint
It checks a policy as a write would, without storing it. As on
Tailscale, a refused policy answers 200 with its reason; only an empty
body is a 400. Terraform provider 0.29.3 calls it at plan time.
2026-10-28 19:06:43 +00:00
Kristoffer Dalby 4aac6ea8f0 api/v2: require a token to hold a device's current tags to retag it
SetNodeTags replaces the tag set, so the tags it removes need the grant
too. A tag outside the grant answers 400, as on Tailscale SaaS.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby 04774b16fa api/v2: name the token's tags in the token answer
Only a token that can assign tags names them, as Tailscale's answer
does.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby eb7a125601 api/v2: send empty route lists under fields=all 2026-10-28 19:06:06 +00:00
Kristoffer Dalby ea96cd2978 api/v2: name the join key in a device's oauthClientId
For a key a token made, Tailscale names the token's client, which is
not recorded, so the field is left out.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby 664b2dd0cf api/v2: show a tagged device's creator as its user
A tagged device shows the login that made the key it joined with.
Its owner stays its tags.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby a5fbaed702 api/v2: list token-made auth keys to a token without all=true
Keys an OAuth token makes are marked so. No listing holds an invalid
key.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby b26cc46b5b api/v2: show userId on admin-made tagged keys
An admin key's user is recorded as the creator of the tagged keys it
makes. A tagged key re-registers a machine by its tags, whoever made it.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby a4eb791b68 api/v2: return a device name as its FQDN 2026-10-28 19:06:06 +00:00
Kristoffer Dalby f69d2ff2d7 api/v2: refuse an unknown keyType 2026-10-28 19:06:06 +00:00
Kristoffer Dalby 373b1494a9 api/v2: ignore unknown properties in request bodies
Tailscale's API ignores them.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby 6a589a76f5 db: keep at most 1000 access tokens per OAuth client
Minting needs only the client secret, whatever its scopes, and
added one row per mint. Each mint now ends tokens past the newest 1000.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby 69060138c4 api/v2: check the createKey scope before reading the body
createKey has no static scope, so any token got its body parsed
and validated before the handler's scope check.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby 80dc7d388c api/v2: stop echoing request values in error responses
huma sets an unexpected property's value to the whole request object.
A refused policy, or another 400, now carries its reason in message,
and a failed test run one data entry per failing test naming its src.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby fbce433541 api/v2: bound every request body
huma gives RawBody no limit, and validates a whole body before
the handler runs. The token form is parsed before the secret check.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby 7b236fcde0 api/v2: keep a tagged all-scope client's tokens inside its tags
Only an untagged all-scope client may mint tokens for any tag, so a
tagged token may not create one.
2026-10-28 19:06:06 +00:00
Kristoffer Dalby b9d35f5397 api, policy/v2: refuse route prefixes with host bits
Routes are matched as written, so such a prefix never equals the one a
client announces. The approval APIs answer 400 and autoApprovers refuses
it at load and write, with Tailscale's text.
2026-10-28 19:06:05 +00:00
Kristoffer Dalby a9024e564d state: install a written policy only after it is stored
Writers validated by installing the policy live, so a store failure
left a refused or unstored policy enforced. Validate on a throwaway
manager, store, then reload from the stored row.
2026-10-28 19:06:04 +00:00
Kristoffer Dalby 3f6e54cbd5 mapper: send CertDomains and https cap when certificates are enabled
Both gate tailscale cert and serve HTTPS on the client; httpsEnabled
follows, as in Tailscale. No certificate when an operator extra record
sits at or below the name: a wildcard would cover it.

Updates #2137
Updates #1921
2026-10-18 20:53:18 +02:00
Kristoffer Dalby 60d42808b8 state: keep node removal apart from policy refresh in DeleteNode
Peers get the deletion as an explicit PeersRemoved change, independent of
their sent-peers tracking.

Updates tailscale/tailscale#15660
2026-09-30 19:03:13 +02:00
Kristoffer Dalby 21f6e46fb8 state: refresh policy nodes inside the peer map build
One peer build per tag/user/IP/route write; callers detect policy moves
via NodesGeneration. Per-node caches only store results for the node
pm holds, so a mapper reading mid-build cannot pin a stale filter.
2026-09-30 17:21:02 +02:00
Kristoffer Dalby 393dd3e2d9 db: store all credentials in one SHA-256-hashed table
API keys, pre-auth keys and OAuth clients/tokens share one table and verify
path. Secrets carry 256 bits of crypto/rand entropy, so a SHA-256 digest
needs no stretching; bcrypt/argon2id rows rehash on use until 0.32.
2026-09-26 00:33:12 +02:00
MsfPablo d60bac5c79 db: reject unknown OAuth client scopes at creation (#3424)
Unknown scopes grant nothing, so clients were silently under-privileged.
Report every invalid scope at once.

Fixes #3406
2026-09-25 21:24:37 +02:00
Kristoffer Dalby eebeab3c1e docs: document joining nodes with an OAuth client
Prefix swap, baseURL, every attribute; examples for tailscale up,
container, tsnet, GitHub Action.
2026-09-25 17:09:19 +02:00
Kristoffer Dalby fc6a16fdfc state: preserve committed node deletion changes
Updates #3410
2026-09-09 18:18:17 +02:00
Kristoffer Dalby af46fe8867 hscontrol, integration: test OAuth scope enforcement and the oauth-clients CLI
Add scope-enforcement coverage and integration tests for the
oauth-clients CLI across the test matrix.
2026-06-26 09:18:06 +02:00
Kristoffer Dalby 84715e976c api/v2: add OAuth client-credentials auth and scope enforcement 2026-06-26 09:18:06 +02:00
Kristoffer Dalby 83d3828ba1 api/v2: simplify endpoint comments 2026-06-24 18:41:06 +02:00
Kristoffer Dalby 475a5ead0c api/v2: add user get and list (Tailscale compat)
Backs the tailscale_user and tailscale_users data sources. getUser maps
gorm/ErrUserNotFound to 404; unmodelled fields are honest constants,
deviceCount/lastSeen/currentlyConnected aggregate the user's nodes.
2026-06-24 18:41:06 +02:00
Kristoffer Dalby e2e9fe3081 api/v2: add read-only tailnet settings endpoint
Report the settings the k8s operator reads, computed from config;
writes return 501.
2026-06-21 04:17:03 +02:00
Kristoffer Dalby 3cddc07026 api/v2: add Tailscale-compatible ACL endpoint
Serve and replace the policy as HuJSON with content-addressed ETags
and If-Match preconditions; writes are rejected in file mode.
2026-06-21 04:17:03 +02:00
Kristoffer Dalby b3472bb8dd api/v2: add Tailscale-compatible devices API
Map the Tailscale device surface — get, list, delete, set name, tags,
key expiry, routes, and authorization — onto Headscale nodes.
2026-06-21 04:17:03 +02:00
Kristoffer Dalby 6b413fe237 api/v2: soft-revoke auth keys with a configurable collector
Tailscale's keys API has no separate expire verb: DELETE is the revoke.
Map it to a soft revoke so the key stays retrievable as invalid afterwards
instead of vanishing, matching the SDK and Terraform's expectations.

Add a revoked timestamp to pre-auth keys (migration plus schema), mark a
key invalid once revoked, and have the keys DELETE handler stamp it rather
than destroy the row. A background collector reaps revoked keys after a
configurable retention window (preauth_keys.revoked_retention, default
168h), so the table does not grow without bound.
2026-06-21 04:17:03 +02:00
Kristoffer Dalby fe08d43978 api/v2: add Tailscale-compatible keys API
Add the v2 foundation — Basic/Bearer auth, scope scaffolding, the
tailnet check, and the Tailscale error shape — plus the auth-key
endpoints mapped onto Headscale pre-auth keys.
2026-06-21 04:17:03 +02:00