Commit Graph
100 Commits
Author SHA1 Message Date
Kristoffer Dalby bb3c0b4cd6 servertest: drive tailscale client oauthkey flow against v2 API
Real feature/oauthkey resolver via tsnet: tskey-client- secret,
?baseURL attributes, CreateKey, register re-advertising key tags.
2026-09-25 17:09:19 +02:00
Kristoffer Dalby c604874dba policy/v2: suggest approved exit nodes by default
Matches SaaS; Apple clients hide the exit-node list without a suggestion.

Fixes #3415
2026-09-23 14:48:45 +02:00
Kristoffer Dalby 06fa3075da hscontrol: replace tailscale line refs with doc links
Line numbers drift on every upstream bump.
2026-09-23 14:48:45 +02:00
Kristoffer Dalby 0e8a3bba54 policy/v2: compare peer CapMap against SaaS route captures
SaaS stamps suggest-exit-node on approved exit peers without nodeAttrs.

Updates #3415
2026-09-23 14:48:45 +02:00
Kristoffer Dalby 5401edb6a8 policy: ignore '#' metadata fields across the whole policy
The filter lived in ACL.UnmarshalJSON, so grants, ssh and nodeAttrs still
hit RejectUnknownMembers. Strip the members in the HuJSON AST instead, at
the single decode entrypoint. Grant "app" payloads are left untouched.

Fixes #3479
2026-09-23 09:30:22 +02:00
Kristoffer Dalby 63123196cc all: inline deprecated tailcfg capability aliases
go 1.27 vet reports the //go:fix inline directives tailscale added to
the tailcfg cap aliases; applied with `go fix -inline ./...`.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 77cd81fe33 flakehashes: refresh for the tailscale.com bump 2026-08-25 21:59:00 +02:00
Kristoffer Dalby 9ceb4511de go.mod: set the lockstep notes off from the sorted requires
go 1.27 tidy folds every require into one direct and one indirect
block; a leading blank line is the only separation it keeps.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 820dc3001b flake: build Go-parsing tools with the project's Go
goimports, gotools' goimports wrapper and golangci-lint-langserver were
on the nixpkgs default Go, whose parser rejects generic methods.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 941fd9ff4e CHANGELOG: minimum client version is v1.82.0 2026-08-25 21:59:00 +02:00
Kristoffer Dalby 7e12e46f63 capver: regenerate for Tailscale v1.102 2026-08-25 21:59:00 +02:00
Kristoffer Dalby fd51ec70f0 capver: sort minor versions numerically
Lexicographic order put v1.102 before v1.24, so the newest release fell
outside the supported window and the minimum never moved.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby d7c1ecce92 mapper: correct the send timer rationale
time.After has been GC-recoverable since Go 1.23, so the leak the comment
described cannot happen and the test asserting it proved nothing.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 635bda9139 hi: compare the stream error with errors.Is 2026-08-25 21:59:00 +02:00
Kristoffer Dalby 0a4d56494f hscontrol: drop the net/http/pprof import
Nothing serves DefaultServeMux; the debug mux already exposes every
profile through tsweb.Debugger.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 1994f50fe8 policy/v2: drop elided struct types in test users
Promoted field keys let the literals skip the embedded gorm.Model.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 93e29ec38d all: use strings.CutLast
Replaces LastIndex plus slice arithmetic when splitting on the last
separator.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 373c60efe3 all: use sync.WaitGroup.Go
Drops the Add/Done bookkeeping, which in batcher.go was spread across
three functions.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 245b81401c integration: make debugJSON and execJSON methods
Go 1.27 allows type parameters on methods, so the container no longer has
to be passed as the first argument.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 60bab7633c golangci-lint: disable canonicalheader
canonicalheader v1.1.2 nil-derefs on a package that has both a generic
method and an http.Header.Set call, which aborts the whole lint run.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 1c6809089f hscontrol: use the stdlib uuid package
Go 1.27 ships RFC 9562 UUIDs and NewV4 cannot fail, dropping gofrs/uuid
and two error paths.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 9f7d829aa5 go.mod: adopt the require layout Go 1.27 tidy produces
Go 1.27 collapses requires into one direct and one indirect block, so the
modernc lockstep note has to live inside a block to survive tidy.
-compat=1.27 is a no-op now that the go directive is 1.27.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby cbbfe613b8 flake: skip comments in go-mod-update-all
Comment lines inside a require block are tab-indented, so the scrape fed
`//` tokens to go get -u.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 0c0b0c68a6 derp: drop deprecated DERPRegion.Avoid
Zero value, superseded by NoMeasureNoHome.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 77caa94400 policy/v2: use value receiver for SSHCheckPeriod.Validate
recvcheck ignores UnmarshalJSON, so the pointer receiver here was the
odd one out against value-receiver MarshalJSON. Validate only reads.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 2c76d5c5b7 all: apply golangci-lint autofixes
Go 1.27 allows setting promoted fields of embedded structs directly in
composite literals, so gorm.Model wrappers go away. Plus strings.Cut,
errors.AsType, reflect.TypeAssert and one gofumpt nit.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 2b64b9fe3a .golangci.yaml: disable exhaustruct_v5
Renamed linter, same 50 findings as the disabled exhaustruct.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby f06ff1cf81 all: build with Go 1.27
Bump every remaining toolchain reference: images, goreleaser, the hi
runner fallback and docs.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby d4d09bc024 all: update dependencies
tailscale.com v1.103.0-pre drops the "format" struct tags that Go 1.27's
finalized encoding/json/v2 rejects; without it MapResponse marshaling
fails for any tailnet with an SSH policy.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 07b5344145 flake: track latest Go in nixpkgs
Use buildGoLatestModule and go_latest so a Go release bump is a
flake.lock update, not a flake.nix edit. Drop the vendored
golangci-lint derivation, now current in nixpkgs.
2026-08-25 21:59:00 +02:00
Kristoffer Dalby 565fd254d0 mkdocs: bump version
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-07-29 11:03:23 +02:00
Kristoffer Dalby 8bb26c5967 CHANGELOG: add 0.29.3
Updates #3385
2026-07-29 11:03:23 +02:00
Kristoffer Dalby d28a6b111a hscontrol: prefer completed auth over expired ctx in followup wait
waitForFollowup selected on ctx.Done() and the verdict channel with equal
priority; when both were ready, select picked at random and discarded a
successful registration as a spurious 401 timeout. Check for a completed
verdict first, race the deadline only if none is ready.

Fixes #3385
2026-07-29 11:03:23 +02:00
Kristoffer Dalby 6275e3a356 policy,state: authorize reauth tags against the authenticating user
Re-authenticating a tagged node with --advertise-tags checked the tag-owned
node, not the authenticating user, so every tag was rejected.

Fixes #3374
2026-07-28 11:55:17 +02:00
Kristoffer Dalby fc16cc6905 state: apply a new pre-auth key's tags on re-registration
Re-registering a tagged node with a different key discarded the new key's
tags and left a stale auth-key reference; retag on key change and persist it.

Fixes #3370
2026-07-28 11:55:17 +02:00
Kristoffer Dalby 1ed5693fa4 state: do not expire tagged nodes on logout
Tagged nodes never expire, but handleLogout stamped a past expiry on them,
leaving them stuck expired and unable to re-authenticate.

Fixes #3371
2026-07-28 11:55:17 +02:00
Kristoffer Dalby 5b6e1e17be hscontrol: gate /key on supported capability version
/key handed out the Noise public key for any v>=39, a floor unrelated
to the handshake's capver.MinSupportedCapabilityVersion. Reject below
the supported floor, matching /ts2021, and drop the stale constant.

Fixes #3380
2026-07-28 11:54:46 +02:00
Kristoffer Dalby b0c221f3a1 changelog: set 0.29 date
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-06-17 11:09:20 +02:00
Kristoffer Dalby 68a6d3cf17 db: drop ambiguous machine-key getter, match precisely in test helper
The First()-by-machine-key getter returned an undefined node when a machine
key mapped to several nodes. It was used only by RegisterNodeForTest; match on
(machine_key, user_id) there instead and remove the getter.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby 1689478485 state: return all nodes for a machine key, reject ambiguous ownership
Collapse the single-pick machine-key lookups onto GetNodesByMachineKeyAllUsers
so callers see every node sharing a machine key and reject the ambiguous or
impossible cases (tagged and user-owned coexistence; a tagged key with several
user-owned candidates) instead of mutating an arbitrarily-picked node.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby a1d3e98255 state: allow key expiry to be set on tagged nodes
Tagged nodes disable key expiry by default but can still have one set
explicitly, and changing tags leaves expiry unchanged, matching Tailscale.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby b83bf3f993 state: serialise registration per machine key
Concurrent registrations of one machine key each saw no existing node and
created their own, duplicating nodes and IPs. Hold a per-machine lock across
the find-then-create section.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby 96d2e6ed60 state: roll back node store when re-registration write fails
Re-registration mutated the node store before the database write and did not
revert on failure, so a restart could drop the client's current node key.
Snapshot the node and restore it if the write fails.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby 9b8949727d db: treat unknown pre-auth key as not found
An unknown or deleted key returned a bare error matching neither the
not-found nor pre-auth-key checks, so registration returned a server error
instead of 401. Wrap gorm.ErrRecordNotFound.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby bff216a184 state: update node in place on pre-auth-key re-registration
A reusable key on a converted node, or a tagged key on a user-owned node,
fell through to new-node creation, leaving two nodes per machine. Match by
machine key and update or convert in place.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby fd08b8fa8c state: make any-user machine-key lookup deterministic
The lookup returned the first map match, so re-auth branch choice varied
with map order once a machine key had more than one node. Prefer the tagged
node, else the lowest node ID.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby a73d38bb3f state: reject re-registration claiming another node's key
The pre-auth-key path wrote the client node key without the collision check
the auth path applies, so a re-registration could claim another node's key
and poison the node-key index. Reject keys bound to a different machine.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby e759d9fc90 auth: re-validate key when an expired node re-registers
The re-registration fast-path skipped validation for a matching node key
without checking expiry, so an expired node could re-auth with a spent key.
Gate it on the node not being expired.

Updates #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby 0961e79e16 state: re-register converted tagged nodes with reused key
A node converted to tagged is re-indexed under no user, so re-registration
keyed on the key's owner missed it and rejected the spent one-shot key.
Match the existing tagged node by machine key.

Fixes #3312
2026-06-15 20:29:15 +02:00
Kristoffer Dalby a5ef3aff15 state: patch relogins and gate endpoint broadcasts
Relogin sent as a peer patch with endpoints preserved; endpoint-only
deltas broadcast only on useful (non-STUN) changes.
2026-06-15 12:02:39 +02:00
Kristoffer Dalby 4da06925d0 types/change: add NodeKeyRotated for relogin peer patch
Sends a relogin as an incremental PeerChange, not a whole-node add.
2026-06-15 12:02:39 +02:00
Kristoffer Dalby 21058d1142 flake, go.mod: refresh dependencies
tailscale.com v1.100.0 -> v1.101.0-pre, golang.org/x/*, genproto,
docker-credential-helpers, client/tailscale/v2; vendor SRI and
nixpkgs lock refreshed. modernc.org/libc stays at v1.72.3 per
modernc.org/sqlite v1.52.0's go.mod. Go 1.26.4 staging pin kept:
nixpkgs-unstable still ships 1.26.3.
2026-06-11 16:28:25 +02:00
Kristoffer Dalby c5f3d5c28d auth: clamp logout expiry to now
Tailscale sends the sentinel time.Unix(123, 0) on logout; storing it
verbatim propagates a 1970 KeyExpiry to every peer's netmap. Same
semantics (expired as of now), saner logs and debug dumps.
2026-06-11 16:28:25 +02:00
Kristoffer Dalby 40ed210521 policy: read pm.pol under the mutex
NodeCanHaveTag, TagExists, ViaRoutesForPeer checked pm.pol before
taking pm.mu, racing SetPolicy's write (caught by -race in
TestRaceConcurrentServerMutations). DebugString read pol, filter, and
the derived maps with no lock at all.
2026-06-11 16:28:25 +02:00
Kristoffer Dalby f4eeb94b1c mapper: gate broadcast sends until a connection's initial map is delivered
AddNode registers the connection before sending the initial map, so a
batched delta could land first and become the stream's first frame;
Tailscale clients then kill the poll with "initial MapResponse lacked
Node" and a retry loop under steady change traffic leaves the netmap
empty. Skip connections awaiting their initial map and retry the
change next tick — the in-flight map may predate it, so it cannot be
dropped. Retries are prepended to keep patch order.
2026-06-11 16:28:25 +02:00
Kristoffer Dalby 7918187e7a servertest: add logout/relogin storm repro with poll churn
Mirrors the flaky integration relogin tests with production batcher
tuning. The churn variant restarts map polls around login like newer
tailscaled does; pre-fix it stranded nodes online after logout 2/2.
2026-06-11 16:28:25 +02:00
Kristoffer Dalby f497b4efd7 state, poll: refcount poll sessions, mark offline only on last release
A cancelled map request whose handler ran late could Connect after the
live session, steal the newest SessionEpoch, then exit without
disconnecting (stillConnected path); the live session's final
Disconnect was rejected as stale and the node stayed online forever
(relogin flake). Counted releases are order-independent, so overlapping
sessions cannot strand a node in either direction.
2026-06-11 16:28:25 +02:00
Kristoffer Dalby 759381ad78 types: add ActiveSessions poll session refcount to Node
In-memory only, like SessionEpoch. Generated clone/view regenerated.
2026-06-11 16:28:25 +02:00
Kristoffer Dalby f585f8a94d flake, go.mod: move to Go 1.26.4 and refresh dependencies
Track nixpkgs staging-next-26.05 for prebuilt Go 1.26.4 (security GO-2026-5037/5039;
unstable still ships 1.26.3, lagging the staging pipeline). Bump go.mod, refresh deps
(tailscale.com v1.100.0, modernc sqlite v1.52.0 with libc lockstep), test Dockerfiles, vendor hash.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 88044f43ff hscontrol: satisfy golangci-lint on changed lines
Sentinel ErrNodeKeyInUse (err113); key the visible-peer set by tailcfg.NodeID
to drop an int64->uint64 cast (gosec G115); NewRequestWithContext (noctx); wsl.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 5e05652a78 mapper: derive incremental visibility from one shared filter
filterVisiblePeerPatches and filterVisibleNodes now share one visiblePeerIDs
helper using the live MatchersForNode/ReduceNodes set, so paths cannot drift.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 020560fc5f policy: remove unused top-level BuildPeerMap
Zero non-test callers; production uses policy/v2 PolicyManager.BuildPeerMap.
A stray second copy of the peer-visibility predicate invites drift.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby fad8f2a729 mapper: test incremental visibility matches full map
Pin that NodeOnline patches, NodeAdded peers, and cross-user profiles expose
exactly the full-map ACL-visible set across the four policy shapes.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 0121083b53 test: assert NodeKey hijack is rejected on re-auth
TestReAuthWithDifferentMachineKey asserted that a second machine claiming an existing NodeKey succeeds, but that hijack (now rejected by f8f08cf7) poisoned the NodeKey index and DoS'd the original node — the test only checked the hijacker's node, never the original's survival. Assert the registration is rejected and the original node survives intact.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 9fc88e308f noise: drop write-only nodeKey field to fix data race
noiseServer.nodeKey was assigned on every PollNetMap and Register request but never read anywhere. The inner HTTP/2 mux multiplexes concurrent requests over one Noise session, so those per-request writes to the shared field raced (caught by -race in servertest TestConnectDisconnectRace, reachable by a client issuing concurrent map requests). Remove the dead field and its two writes.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby c483bebba8 mapper: guard nil Hostinfo in addNextDNSMetadata
addNextDNSMetadata dereferenced node.Hostinfo().OS() without the .Valid() guard its siblings (RequestTags, TailNode) apply, so building the DNS config for a node with nil Hostinfo (a legacy NULL host_info row) panicked whenever a NextDNS resolver was configured. Guard the OS() dereference.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 4914f9f2fd state: reject re-auth claiming another machine's NodeKey
applyAuthNodeUpdate rotated the node's NodeKey to the client-supplied value without the 1:1 NodeKey/MachineKey check createAndSaveNewNode (f8f08cf7) and getAndValidateNode enforce. A re-authenticating node could thus rotate its key to a victim's and poison the NodeStore NodeKey index, denying the victim service. Apply the same uniqueness check on the re-auth path.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 8237ac662a mapper: filter incremental UserProfiles by ACL visibility
buildFromChange's PeersChanged path passed an unfiltered changed-node slice to WithUserProfiles, while the full-map path uses the BuildPeerMap-filtered ListPeers. A node thus received the identities (login name, display name, avatar) of users owning peers its ACL forbids accessing. Filter the UserProfiles peer set via the same ReduceNodes visibility check buildTailPeers applies.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby cd1c208980 mapper: filter peer-change patches by ACL visibility
buildFromChange added PeersChangedPatch (online/offline, endpoint, key-expiry) directly, skipping the policy.ReduceNodes visibility filter that buildTailPeers applies to full peers. A node thus received the existence, presence, and addresses of peers its ACL forbids accessing. Restrict patches to the recipient's visible peer set.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 0fdff0c79b oidc: set SameSite=Lax on state/nonce CSRF cookies
setCSRFCookie set no SameSite attribute despite a nolint comment claiming it did, so the OIDC state/nonce cookies relied on the browser default. Set Lax explicitly (Strict would drop the cookie on the cross-site IdP->callback navigation and break login), hardening browsers that do not default to Lax against OIDC login CSRF.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby eb57a3a62b state: reject registration claiming another machine's NodeKey
createAndSaveNewNode trusted the client NodeKey without checking it was already bound to a different machine, so an authenticated party could register a node carrying a victim's public NodeKey, poison the NodeStore NodeKey index, and make the victim's MapRequest resolve to the wrong node (rejected by getAndValidateNode = DoS). Enforce the 1:1 NodeKey/MachineKey binding at registration, as poll time already does.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 56cd3eb24d policy: guard SSHCheckParams autogroup:self against nil User
The autogroup:self SSH-check branch dereferenced node.User().ID() guarded only by !IsTagged(); a non-tagged node with an unhydrated User (UserID set, association nil) crashed the server via the Noise SSH-check path. Gate on User().Valid() like filter.go, same shape as 171fd7a3.
2026-06-09 15:21:18 +02:00
Kristoffer Dalby 8f75ee5647 docker: head tailscale latest go
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-06-08 10:04:49 +02:00
Kristoffer Dalby efdd9463e9 mapper: keep one batched bundle per node in flight to preserve order
A saturated worker pool could deliver a later tick before an earlier one.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby a518a5076a state: batch route auto-approval into one policy rebuild
Per-node SetApprovedRoutes made each policy reload O(m*n^2).
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 2c9164b1c4 policy: precompute node routes in the peer-map build
CanAccess recomputed each node's routes per pair, making the scan O(n^2)-heavy.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 0e7b154617 mapper: register reconnecting node atomically against cleanup
A reconnect could be deleted from b.nodes mid-AddNode and orphaned.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby ad2693ff13 mapper: record initial-map peers only after delivery
A timed-out initial map left phantom lastSentPeers, skewing later diffs.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 7d845ef65e db: advance allocator cursor under lock during IP backfill
Reading prev4/prev6 unlocked raced Next; not advancing made backfill O(n^2).
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 9f0c74e73a dns: release lock before extra-records channel send
A blocked send held the write lock and leaked the watcher at shutdown.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby e413919810 derp: clone regions when merging DERP maps
In-place node shuffling aliased regions shared with the served map.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 0921972f96 oidc: avoid slice panic in getCookieName for short values
A malformed short nonce no longer panics; it yields a non-matching name.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 99ad555d64 db: handle degenerate prefixes in random IP allocation
Single-address prefixes panicked; zero-high-byte addresses failed to encode.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 10696fa634 db: look up API keys by explicit primary key, not struct condition
GetAPIKeyByID(0) returned the first key instead of not-found.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 84c99023e5 util: check RNG error before slicing url-safe random string
A crypto/rand failure sliced empty output and panicked.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby ba54349176 util: handle single-address IPv4 prefix in reverse DNS generation
A /32 indexed past the 4-byte address and panicked at startup.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 08f186f22a state: skip database persist for keepalive-only map requests
A lone LastSeen bump no longer triggers a full-row write and policy rescan.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 017162dac1 state: signal NodeStore shutdown without closing writeQueue
Writes racing Stop now drop cleanly instead of panicking on send.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby bb06b90543 types: skip malformed derp.urls entries instead of panicking
A failed url.Parse left a nil pointer that was dereferenced at startup.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 5a70a72988 types: lock tailcfg DNS config access for extra-records updates
The watcher write raced per-client map builds cloning ExtraRecords.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby ec94573258 db: drop superseded ephemeral deletions in the GC drain loop
A node reconnecting after its deletion queued is no longer removed.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 4c165ae5e7 db: reap ephemeral GC watcher goroutine on cancel and reschedule
A stopped timer never fires, so the per-node watcher leaked until close.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 06d6816dc9 state: keep nil expiry for nodes that stay tagged on reauth
The convert-from-tag arm wrongly set an expiry on still-tagged nodes.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby f61753e737 db: bound IP allocation scan so exhausted prefixes error out
Random strategy previously spun forever under the allocator mutex.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 4f67300005 types: clone Hostinfo before applying DERP change
Stops mutating a NetInfo pointer shared with a published snapshot.
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 2e2401833b state: persist live NodeStore node in persistNodeToDB
Avoids clobbering concurrent admin writes (tags, routes, rename).
2026-06-08 10:04:49 +02:00
Kristoffer Dalby 29f87e5eaa flakehashes: refresh vendor hash after dropping gorilla/mux
Regenerated by `go run ./cmd/vendorhash update` so the Nix build's
vendorHash matches the new go.mod/go.sum fingerprint.

Updates #3296
2026-06-05 15:00:59 +02:00
Kristoffer Dalby f61d21b4a7 go.mod: drop unused gorilla/mux dependency
ApplePlatformConfig was the last consumer; go mod tidy removes the
require and the two checksum lines.

Updates #3296
2026-06-05 15:00:59 +02:00
Kristoffer Dalby b892b8f254 hscontrol: read Apple platform via chi.URLParam, not mux.Vars
ApplePlatformConfig still used gorilla mux.Vars after the chi
migration in 30338441, so every /apple/{platform} request returned
400 "no platform specified". Read the param via chi and add tests.

Fixes #3296
2026-06-05 15:00:59 +02:00
Kristoffer Dalby 3587225a88 mapper: fix phantom updateSentPeers on disconnected nodes
When send() is called on a node with zero active connections
(disconnected but kept for rapid reconnection), it returns nil
(success). handleNodeChange then calls updateSentPeers, recording
peers as delivered when no client received the data.

This corrupts lastSentPeers: future computePeerDiff calculations
produce wrong results because they compare against phantom state.
After reconnection, the node's initial map resets lastSentPeers,
but any changes processed during the disconnect window leave
stale entries that cause asymmetric peer visibility.

Return errNoActiveConnections from send() when there are no
connections. handleNodeChange treats this as a no-op (the change
was generated but not deliverable) and skips updateSentPeers,
keeping lastSentPeers consistent with what clients actually
received.
2026-04-10 13:18:56 +01:00