Commit Graph
209 Commits
Author SHA1 Message Date
Kristoffer Dalby 9b46f8f1fc db: keep only the current policy
Every write inserted a row and nothing deleted one, while every reader
takes only the newest. Writers store and install under one lock, so an
uncommitted insert cannot outlive the newer row that pruned the rest.
2026-10-28 19:06:04 +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 837ede3fbc policy/v2: refuse an empty policy
SetPolicy returned a nil error for empty input it ignored, so the v1
API stored it as valid. Reload skips only the nil "no policy" case.
2026-10-28 19:06:04 +00:00
Kristoffer Dalby 1b724a5bb9 state: serialise policy reloads
Load and install the stored policy as one step, so an older
reload cannot install its document over a newer one.
2026-10-28 19:06:04 +00:00
Kristoffer Dalby 18cbaf6f2a servertest, state: add node name golden data
Labels under taken hostnames, case-only renames, hostname changes, long
hostnames and a Service's name; API, peer and self per step, and State
labels as a restart would load them.
2026-10-28 19:06:03 +00:00
Kristoffer Dalby 483489942c servertest, state: add map delivery golden data
A retag through the API, a running peer's DERP, disco, hostname and
port, down and up gaps around policy pushes; peers, filters and frames
per node.
2026-10-28 19:06:02 +00:00
Kristoffer Dalby 38125d1043 db, state: let only the delete that removed a node free its addresses
Fixes ANT-2026-NWTIBB02
2026-10-28 14:25:41 +00:00
Kristoffer Dalby 8126f8727a state: drop registration locks once unused
The lock is taken before any credential check, so client-chosen machine
keys grew the map forever. Ref-count entries; delete at zero.

Fixes ANT-2026-ZC18AF82
2026-10-28 14:25:41 +00:00
Kristoffer Dalby 3c277949c7 auth, state: keep announced routes out of pending registrations
Routes are the one long list; the first MapRequest reports them.
A relogin keeps the node's own routes until then.

Fixes ANT-2026-RQS7JAPP
2026-10-28 14:25:41 +00:00
Kristoffer Dalby c87672bcf6 state: bound client reports of nodes loaded at startup
Rows stored before the bounds would still fan out unbounded endpoints.

Fixes ANT-2026-RQS7JAPP
Fixes ANT-2026-5B6KE87K
Fixes ANT-2026-KFCFFM34
2026-10-28 14:25:41 +00:00
Kristoffer Dalby 7259ef6edd hscontrol: refuse a key expiry that cannot be encoded
handleRegister, the OIDC auth path and SetNodeExpiry refuse it. Expiries
are stored in UTC, which SQLite can read back, and loaded in UTC, as
PostgreSQL returns the server's zone. The v1 API answers 400.

Fixes ANT-2026-QMCAFJ1Q
2026-10-28 14:25:41 +00:00
Kristoffer Dalby 159deeec41 mapper: skip peers whose key expiry cannot be encoded
json.Marshal refuses a year outside [0,9999], so one stored expiry
failed every map with that node. TailNode and key patches send UTC, the
builder drops what fails and the boot scan names the node.

Fixes ANT-2026-QMCAFJ1Q
2026-10-28 14:25:41 +00:00
Kristoffer Dalby eeb0ef0901 state: give another user's key on a tagged machine a new device
Only a key that speaks for a tagged node re-authenticates it: a tagged
key, or one of the user it was tagged from. Another user's key or login
adds that user's own device, or only wakes the node under its node key.

Fixes ANT-2026-AWGRB7O1
2026-10-28 14:25:32 +00:00
Kristoffer Dalby c262792660 state: need a valid pre-auth key to re-register an expired tagged node
Fixes ANT-2026-LYVGXVSW
2026-10-28 14:22:20 +00:00
Kristoffer Dalby abbec204a2 state: never serve a single address inside the node ranges
Such a route names a node, not a subnet: it stays approved and listed
but no viewer gets it. Via inclusions skip the election, so the rule
sits on everything RoutesForPeer returns.

Fixes ANT-2026-MLP8ZR5T
2026-10-28 14:21:03 +00:00
Kristoffer Dalby cd62031b98 state: stop the auth cache TTL sweeper on Close
The expirable LRU ran its sweeper forever, one goroutine per State. Purge
first so pending waiters get ErrRegistrationExpired instead of hanging.
The OIDC state cache is closed at server shutdown for the same reason.
2026-10-23 18:23:53 +02:00
Kristoffer Dalby 51b420865e state: own request memory before storing nodes
A reused RegisterRequest.Hostinfo was written and stored while NodeStore readers cloned it.
2026-10-23 18:23:53 +02:00
Kristoffer Dalby 552356a2ee state: skip the caller's policy refresh once the writer applied it
The writer refreshes the policy manager before publishing its batch, so
the caller's SetNodes with the published snapshot recompiled it back.
2026-10-23 18:23:53 +02:00
Kristoffer Dalby 5d21186f22 state: release persistMu before the NodeStore delete
Holding it serialised concurrent deletes into one NodeStore batch and
peer map build each, capping deletion at one batch tick per node.
2026-10-23 18:23:53 +02:00
Kristoffer Dalby 7414c276f6 mapper: limit capability copies to requested peers
Small peer updates should not copy capabilities for the entire tailnet.
2026-10-22 22:17:36 +02:00
Kristoffer Dalby a928125afa state: lowercase renames and refresh self DNS config on rename
"Foo" and "foo" could share one DNS name; collisions now ignore case.
A rename never resent the node its own DNS config, so CertDomains lagged.

Updates #2137
2026-10-18 20:53:18 +02:00
Serhii Matsyshyn 8f3ba2d8cd state: rebuild peer maps in ReloadPolicy only when the policy changed
The SetPolicy API handler calls State.SetPolicy, which already rebuilds the peer map when the policy changed, and then ReloadPolicy, which rebuilt it again even when nothing changed. So every policy write built the peer map twice.

ReloadPolicy now rebuilds only when the policy really changed. A test checks one build per changed write and none for an unchanged reload.

Part of #3549
2026-10-18 13:18:48 +02:00
Martin -nexus- Mlynář a8d6f5be81 state: send a relogin as a whole node unless only its keys changed
A PeerChange patch cannot clear a peer's Expired flag or carry the
Hostinfo the relogin stored.

Fixes #3531
2026-10-09 11:44:37 +02:00
Kristoffer Dalby 49b848a83d state: compare peer-visible Hostinfo through views
Field by field: no allocation, no reflection.

Updates #3531
2026-10-09 11:44:37 +02:00
Kristoffer Dalby 9c764e5a99 state: return the replaced node from NodeStore updates
UpdateNodeDiff hands back the pre-update clone the writer already makes.

Updates #3531
2026-10-09 11:44:37 +02:00
Kristoffer Dalby 957a332d5d policy/v2: match the login user in SSHCheckParams
The client picks the check rule by login user; the server took the
first rule for the node pair, so a root login could get a 12h
localpart period instead of "always", or be approved after its rule
was removed while another user's rule remained. Hold URLs now carry
the concrete user: tailssh never expanded the encoded $LOCAL_USER.

Updates #3508
2026-10-08 10:29:23 +02:00
Kristoffer Dalby dceb584c89 noise: reject SSH checks the policy no longer requires
A stale check rule still held and accepted after login. Deny with a 200
Reject (tailssh retries errors); re-check after the verdict.

Updates #3508
2026-10-08 10:29:23 +02:00
Kristoffer Dalby d7b333d2aa mapper: send an empty SSH policy when compiling it fails
Fail closed; nil kept the client's previous rules.

Updates #3508
2026-10-08 10:29:23 +02:00
Kristoffer Dalby ba413fca3d state: return a node's primary routes in stable order
They came from map iteration, so an unchanged router rendered its self
node differently on every call.

Updates #3502
2026-10-07 23:36:16 +02:00
Kristoffer Dalby 541ef40c87 state: report unindexed node key instead of panicking
A missing key yields the zero NodeView; its ID() panicked inside a reader
goroutine and took down the test binary instead of failing with a diagnostic.
2026-10-07 22:55:03 +02:00
Kristoffer Dalby bc7fe6b8eb state: test NodeKey index agrees with node list
Covers put, rotation and payload writes on the reuse path, and delete.
2026-10-07 22:55:03 +02:00
Kristoffer Dalby c4ce3f7d14 state: revalidate pre-auth key reuse when registration applies 2026-10-07 14:03:54 +02:00
Kristoffer Dalby 4776898029 state: roll back only registration fields on failed re-registration
Restoring the whole pre-update snapshot also reverted sessions that
connected during the failed write, stranding a live node offline.
2026-10-07 11:10:38 +02:00
Kristoffer Dalby f7c8b4d93f state: serialise IP backfill with node persistence
A persist between the backfill's database write and its NodeStore reload
wrote the old addresses back, silently undoing the backfill.
2026-10-07 11:10:38 +02:00
Kristoffer Dalby 57358b7430 state: backfill only IPs into NodeStore
PutNode of the DB row dropped session state, stranding connected nodes
offline, and nil-dereferenced nodes without Hostinfo.
2026-10-07 11:10:38 +02:00
Kristoffer Dalby 9c34c0c90d state: stop tag approval sorting the reported RequestTags
nodeToRegister.Tags aliased Hostinfo.RequestTags, so sort/compact rewrote
the stored Hostinfo ([b a a] became [a b ""]).
2026-10-07 11:10:38 +02:00
Kristoffer Dalby 357f34a778 state: send a node's DNS config with its own refresh
Drained at dispatch for nodeAttrs changes from any policy, user or node
update, and for hostname/OS changes feeding NextDNS device metadata.
2026-09-30 19:03:13 +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 9526d74d61 state: send PolicyChange from SetApprovedRoutes only when visibility moved
Otherwise NodeAdded. Any SubnetRoutes/ExitRoutes change still bumps
NodesGeneration, so in practice only unannounced approvals narrow.
2026-09-30 17:21:02 +02:00
Kristoffer Dalby c26f6e5255 state: approve routes inside the map request write
One NodeStore write, one peer build, one row update per
auto-approved map request, instead of a second SetApprovedRoutes write.
2026-09-30 17:21:02 +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 05fc17bc85 state: decide whole-peer updates at each caller, not in persist
persistNodeAndRefreshPolicy no longer fabricates NodeAdded; RenameNode
and SetNodeTags add it since both are peer visible.
2026-09-30 17:21:02 +02:00
Kristoffer Dalby 86b4f7c430 state,policy: add peer map and NodeStore write benchmarks
Cover BuildPeerMap, SetNodes, NodeStore writes and
UpdateNodeFromMapRequest across node counts and policy shapes.
2026-09-30 17:21:02 +02:00
Kristoffer Dalby d813144f3b state: fail when a Node field is not classified for peer map reuse
Every exported types.Node field is pinned to relation/election/payload
in nodeFieldImpact; a new field fails until classified, and each
relation/election field gets a mutation check against updateChanges.
2026-09-30 17:21:02 +02:00
Kristoffer Dalby 1613023be9 state: check NodeStore adjacency against a full peer map build
Rapid test drives random writes through a real NodeStore and policy
manager, checking adjacency before and after syncPolicy against a
BuildPeerMap from a fresh policy manager.
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
Kristoffer Dalby 61a626eab7 state: accept advertise-tags subset of a pre-auth key's tags
tailscale client's OAuth authkey flow re-advertises the key's tags.
Reject only tags the key lacks, for new nodes and re-registrations.
2026-09-25 17:09:19 +02:00
Kristoffer Dalby 80c863b15a state: mark expired nodes offline without ending the session
Online now requires a live session and an unexpired key, derived in one
place by Node.ShouldBeOnline so every writer agrees what online means.

Fixes #3470
2026-09-23 09:23:51 +02:00
Kristoffer Dalby 4c6a2dff52 state: resolve changed peers through adjacency
ListPeers now filters named peers against the recipient's adjacency, so
a node the policy hides is never delivered.

Updates #3417
2026-09-10 13:06:13 +02:00
Kristoffer Dalby e3c4c81b18 state: reuse peer adjacency for payload-only writes
A write that cannot move visibility carries the previous adjacency
forward; policy and user changes rebuild it explicitly.

Updates #3417
2026-09-10 13:06:13 +02:00