4405 Commits

Author SHA1 Message Date
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
Simon Law
54a2746f55 Makefile: enable CGO for make test in dev shell
Fixes #3401

Signed-off-by: Simon Law <sfllaw@tailscale.com>
2026-07-28 21:01:56 +02:00
Igor Serganov
cfd845cb53 poll: do not cancel ephemeral GC until Connect succeeds
With node.ephemeral.inactivity_timeout set, ephemeral nodes are
usually deleted after they go offline, but under reconnect churn some
departed nodes stayed in the node list as disconnected indefinitely
until removed manually or until Headscale restarted.

Ephemeral cleanup is timer-based via EphemeralGarbageCollector, not a
periodic LastSeen scan. serveLongPoll cancelled any pending GC timer
at the very start of a long-poll attempt and only rescheduled on a
clean disconnect after Connect. If a reconnect cancelled the timer and
then failed before Connect (for example an UpdateNodeFromMapRequest
error), the deferred cleanup saw connectGen == 0 and returned without
Schedule. The node remained offline with no deletion timer and no
reconciler to recover it.

Cancel the ephemeral GC timer only after a successful Connect, so a
failed reconnect leaves an already-armed inactivity timer intact.
Successful reconnects still cancel GC once the node is online, and a
later disconnect reschedules as before.

Add TestFailedReconnectDoesNotCancelEphemeralGC to lock in the
ordering, plus IsScheduled and DeleteNodeFromStoreForTest helpers for
the test.

Fixes #3382

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 12:28:00 +02:00
Fredrik Ekre
dc3c0bc587 cli: remove leftover trace log from #3324 that always renders a JSON marshaling error
Noticed this while testing out #3394.
2026-07-28 12:27:10 +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
Arpit Jain
0ce3356b89 auth: check machine key on the followup registration path
waitForFollowup returns nodeToRegisterResponse for a completed
registration without checking that the Noise session polling for the
result was started with the machine key that opened the registration.
That response carries the registering user's User and Login, so the auth
ID in the followup URL is the only thing protecting it.

handleRegister and handleLogout both call machineKeyMismatch before
handing back a node, so this is the one path of the three that does not.
The key is already available: HandleNodeFromAuthPath resolves the node
from the MachineKey cached in RegistrationData, so on the normal path the
node and the session agree and the check is a no-op.

The auth ID is 96 bits of randomness and is not guessable, so this is not
reachable by brute force. It is logged at info level when a registration
is created, which makes log access the realistic way to obtain one.

The existing followup_registration_success case built its node with
CreateNodeForTest, which picks a random machine key that no real
registration would produce. Set the registering machine key so the
fixture matches the production path.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
2026-07-28 11:51:50 +02:00
Florian Preinstorfer
7a1ee34f71 Explicitly select lunr as search provider
Keep the existing search provider instead (and the separator) option.
2026-07-24 21:28:27 +02:00
Kristoffer Dalby
f20f1f1278 ci: bump Mic92/hestia to v2.0.0
Point the action at the repo root, which is the canonical entry point
in v2; the /action subpath is now a deprecated shim.

Closes #3356
2026-07-21 13:28:35 +02:00
Kristoffer Dalby
8eec2dbdc2 build: bump Go toolchain to 1.26.5
Tailscale HEAD go.mod now requires go >= 1.26.5; build images pinned
1.26.4. Bump go.mod, the four Go Dockerfiles, and the nixpkgs pin
(staging-next-26.05 ships go_1_26 1.26.5; unstable still lags).
2026-07-20 12:27:47 +02:00
alaningtrump
048308511c chore: fix function comment to match actual function name
Signed-off-by: alaningtrump <alaningtrump@outlook.com>
2026-07-03 07:27:08 +02:00
Kristoffer Dalby
a84945f134 CHANGELOG: shorten 0.29.2 invalid-name entry, set date
Updates #3346
2026-07-01 16:23:24 +02:00
Kristoffer Dalby
fef80f3eb0 CHANGELOG: note /ts2021 WebSocket GET fix
Updates #3357
backup/release-v0.29.2-orig
2026-07-01 15:20:01 +02:00
Kristoffer Dalby
01ef350d5f integration: add TS2021 WebSocket tests to CI matrix
Generated by gh-action-integration-generator.

Updates #3357
2026-07-01 15:20:01 +02:00
Kristoffer Dalby
f4fba32dc6 integration: test /ts2021 WebSocket GET with a real WASM client
A raw coder/websocket dial and the real tailscale.com js/wasm control client under Node, both against headscale alongside normal Tailscale clients.

Updates #3357
2026-07-01 15:20:01 +02:00
Kristoffer Dalby
fc6f216b61 hscontrol: register /ts2021 for WebSocket GET
The chi migration dropped GET; JS/WASM control clients open /ts2021 as a WebSocket GET and were rejected with 405 before reaching NoiseUpgradeHandler.

Fixes #3357
2026-07-01 15:20:01 +02:00
Kristoffer Dalby
de9db9c811 CHANGELOG: note 0.29.2 invalid-name map fix
Updates #3346
2026-07-01 15:19:08 +02:00
Kristoffer Dalby
4946d1c88d state: log nodes with map-breaking data at startup
Scan a node-health check registry at boot and log each node whose name
can't form a valid FQDN, with the rename fix. Log-only, no mutation.

Updates #3346
2026-07-01 15:19:08 +02:00
Kristoffer Dalby
c497612c99 state: reject renames whose FQDN exceeds the hostname limit
A valid label can still overflow 255 chars under a long base_domain; gate
RenameNode with the new types.ValidateGivenName.

Updates #3346
2026-07-01 15:19:08 +02:00
Kristoffer Dalby
4e4512c4b7 poll: return an HTTP error on long-poll setup failure
A bare return sent an empty 200 the client read as "unexpected EOF" and
retried forever; emit a real error instead.

Updates #3346
2026-07-01 15:19:08 +02:00
Kristoffer Dalby
08956d51a4 mapper: skip peers with invalid names instead of failing the map
A peer whose GivenName fails GetFQDN aborted the whole map for every node
that could see it. Drop and log it; SSH policy errors degrade too.

Fixes #3346
2026-07-01 15:19:08 +02:00
Kristoffer Dalby
d528686f14 mapper,policy: add reconnect-storm and lock-concurrency regression tests
TestInitialMapNotStarvedByReconnectStorm reproduces the #3346 stall;
TestPolicyManagerConcurrentReads guards the RLock cache access under -race.

Updates #3346
2026-07-01 14:41:03 +02:00
Kristoffer Dalby
6f317c7576 policy: take RLock for reads so map generation runs concurrently
One exclusive mutex serialized every policy read, so a mass reconnect on
autogroup:self/via/relay policies stalled clients into "unexpected EOF"
retries. Per-node caches become xsync.Maps for lazy population under RLock.

Fixes #3346
2026-07-01 14:41:03 +02:00
Kristoffer Dalby
518b497be9 ci: run TestK8sOperator in the shared sqlite matrix
It needs no special runner (every integration container already runs privileged), so it joins the generated matrix; load br_netfilter only for that test.

Updates #1202
2026-06-26 22:06:26 +02:00
Kristoffer Dalby
8d91e42a9f cmd/hi: share the test-container prefix matcher and check the k3s image
Factor the hs-/ts-/derp-/k3s- prefix set into one helper used by cleanup and docker; add a doctor check for the ghcr k3s image.
2026-06-26 22:06:26 +02:00
Kristoffer Dalby
99fd62477d integration: add a k3s and Tailscale Kubernetes operator test
Run the real operator in a single-container k3s cluster against an in-test Headscale over plain HTTP. k3sic exposes reusable building blocks (InstallOperator, DeployConnector, DeployEchoServer, ExposeServiceToTailnet, DeployProxyGroup); the test covers operator registration, an egress connector, ingress connectivity from a tailnet node, and proxy groups. tls-ca-baking.md records the private-CA TLS variant.

Updates #1202
2026-06-26 22:06:26 +02:00
Kristoffer Dalby
c7b162509c integration/hsic: add CreateOAuthClient via the v2 API client
Mints an admin API key and creates a keyType=client OAuth credential through gen/client/v2, exposed on ControlServer. Reusable by tests needing OAuth client credentials.
2026-06-26 22:06:26 +02:00
Kristoffer Dalby
a4ec76605e integration/tsic: add WithDERPOverHTTP for the non-TLS embedded DERP
Sets TS_DEBUG_DERP_WS_CLIENT + TS_DEBUG_USE_DERP_HTTP so a client can reach hsic's plain-HTTP embedded DERP over websockets; the counterpart to hsic.WithoutTLS.
2026-06-26 22:06:26 +02:00
Kristoffer Dalby
1867213b69 capver: order TailscaleLatestMajorMinor versions with cmpver
Lexical sort placed v1.100 before v1.98; cmpver compares numerically.
2026-06-26 22:06:26 +02:00
Kristoffer Dalby
622e08f5e6 oidc: harden callback CSRF cookies, state reuse, and issuer config
Defence-in-depth fixes to the OIDC login flow.

Set the state/nonce cookie Secure flag from the configured server_url
scheme rather than req.TLS, so the cookies stay Secure behind a
TLS-terminating reverse proxy where the proxy-to-Headscale hop is plain
HTTP. Deriving it from config avoids trusting a spoofable
X-Forwarded-Proto header.

Make the OIDC state single-use: consume it from the cache on the
callback and clear the state/nonce cookies once validated, so a replayed
callback cannot resolve the same session and the cookies do not linger
until expiry.

Bound OIDC discovery to the caller's context so a slow or unreachable
issuer fails startup within the timeout instead of hanging, and validate
the issuer URL and required client_id/client_secret at config load so an
unworkable setup fails fast.
2026-06-26 09:18:06 +02:00
Kristoffer Dalby
79bf201ccb changelog: note OAuth clients and scopes 2026-06-26 09:18:06 +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
b05f8875c3 hscontrol, cli: serve the v2 API on the local socket and add the oauth-clients command
Serve the v2 API over the local unix socket for the CLI and add the
oauth-clients command that drives it.
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
ee95cf58d9 hscontrol: add the OAuth client and access-token model
Add the OAuth client type, its database storage, the scope grant package,
policy tag-ownership exposure, and the state operations backing the v2
OAuth client-credentials flow.
2026-06-26 09:18:06 +02:00
Florian Preinstorfer
7b1776a87e Add changelog entry for systemd service refresh 2026-06-25 11:07:10 +02:00
Florian Preinstorfer
f6865fb40c Filter @resources from the list of allowed syscalls 2026-06-25 11:07:10 +02:00
Florian Preinstorfer
6e0814a845 Add DevicePolicy and MemoryDenyWriteExecute
I've used those for a long time with my setups.
2026-06-25 11:07:10 +02:00
Florian Preinstorfer
6e6ea3758d Remove CAP_CHOWN from systemd service 2026-06-25 11:07:10 +02:00
Florian Preinstorfer
3846c3f148 Remove X-Restart-Triggers from systemd service file
Its not used by systemd.
2026-06-25 11:07:10 +02:00
Florian Preinstorfer
fe536dd799 Slightly restructure the example config comments 2026-06-25 11:07:10 +02:00
Fredrik Ekre
5aeeff98d0 oidc: fix NewProvider ignoring caller context timeout
NewAuthProviderOIDC received a context with a 30-second timeout from
its caller, but immediately discarded it by passing context.Background()
to oidc.NewProvider. This caused both `headscale serve` and
`headscale configtest` to hang indefinitely if the OIDC issuer was
unreachable, rather than timing out and returning an error.

Fix by passing the caller's ctx to oidc.NewProvider and remove the
nolint:contextcheck annotation that was suppressing the linter warning
about this bug. The annotation was introduced in ce580f82 as part of a
bulk lint-suppression pass without addressing the underlying issue.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 19:03:00 +02:00
Kristoffer Dalby
66937040f2 policy: allow the tailscale.com/cap/secrets capability
It's setec's official cap; allowlist it so it can be granted via policy.
2026-06-24 18:41:33 +02:00
Kristoffer Dalby
036760df02 hscontrol: collapse key and settings asserts into cmp.Diff 2026-06-24 18:41:06 +02:00
Kristoffer Dalby
83d3828ba1 api/v2: simplify endpoint comments 2026-06-24 18:41:06 +02:00
Kristoffer Dalby
dd9f0a3f4f servertest: roundtrip v2 users and 4via6 through the clients
Exercises the user data sources via the Go SDK, tscli, and OpenTofu, and
backfills tailscale_4via6 (provider-local compute), each with the
no-drift gate.
2026-06-24 18:41:06 +02:00