GeoaTailnet Guard

The findings · Tailscale 1.104.2 · headscale 0.29.5

Eleven findings, what they mean for you, and what to do.

From the Security Assessment Report: Tailscale v1.104.2 and headscale v0.29.5 (Tailworm, Cyberdione Labs, published 24 to 25 Oct 2026). Only the conditions under which each finding applies and the mitigations are kept here, each with the line or official page it comes from.

0 of 11 have a vendor fix. Patch sources last checked 2026-10-29. RSS

TW-01Finding 1 · T3HighTailscale SSH

Tailscale SSH: local Unix-socket forwarding (ssh -L) dials as root

No fix published · checked 2026-10-29

What this means for you

If Tailscale SSH is on for a server and your policy lets another device SSH into it, anyone who takes over that other device can reach root-only services on the server through the SSH session. If Tailscale SSH is off everywhere, this does not apply to you.

Component
Tailscale SSH server in tailscaled 1.104.2 (gliderssh streamlocal local forwarding)
Needs
A foothold on one device plus an SSH policy rule that accepts that device to the target, with local forwarding permitted. No sudo or docker-group membership needed.
Impact
The root tailscaled process connects to root-only Unix sockets on the attacker's behalf (for example a container runtime socket), which can mean root on the target.

When it applies

  1. Not affected Every node we can see runs the version that fixes this finding. version_fixed
  2. Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
  3. Not affected Your policy has no Tailscale SSH rule, so no peer can open an SSH session. policy_provided and not ssh_policy_grants_access
  4. Not affected Forwarding is switched off on the node (TS_SSH_DISABLE_FORWARDING). ssh_forwarding_disabled
  5. Affected Tailscale SSH is on, your policy lets peers in, and forwarding is not switched off. ssh_server_on and ssh_policy_grants_access and not ssh_forwarding_disabled
  6. Conditional Tailscale SSH is on. Affected if any SSH rule lets a peer reach these nodes and forwarding is not switched off on them. ssh_server_on

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Earlier bulletins

  • TS-2026-004 (2026-06-03): SSH Unix socket forwarding did not respect symlink permissions. Fixed in 1.98.10. An earlier fix in the same area as findings 1 to 3. It does not reference Tailworm.

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 229 (section lines 229-251): Finding 1 (T3) — ssh -L unix-socket forwarding dials as root (writes to root-only sockets) — Severity: High

TW-02Finding 2 · T2HighTailscale SSH

Tailscale SSH: remote Unix-socket forwarding (ssh -R) replaces root-owned sockets

No fix published · checked 2026-10-29

What this means for you

Same precondition as finding 1: Tailscale SSH on, plus a policy rule that lets a peer in. An attacker on that peer can impersonate root services on the server. Off everywhere means not affected.

Component
Tailscale SSH server in tailscaled 1.104.2 (gliderssh streamlocal remote forwarding, BindUnlink)
Needs
As finding 1, with remote forwarding permitted.
Impact
The root tailscaled removes a root-owned socket in a protected directory and puts the attacker's listener in its place, impersonating that service (root code execution for command-channel daemons; credential capture for others).

When it applies

  1. Not affected Every node we can see runs the version that fixes this finding. version_fixed
  2. Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
  3. Not affected Your policy has no Tailscale SSH rule, so no peer can open an SSH session. policy_provided and not ssh_policy_grants_access
  4. Not affected Forwarding is switched off on the node (TS_SSH_DISABLE_FORWARDING). ssh_forwarding_disabled
  5. Affected Tailscale SSH is on, your policy lets peers in, and forwarding is not switched off. ssh_server_on and ssh_policy_grants_access and not ssh_forwarding_disabled
  6. Conditional Tailscale SSH is on. Affected if any SSH rule lets a peer reach these nodes and forwarding is not switched off on them. ssh_server_on

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Earlier bulletins

  • TS-2026-004 (2026-06-03): SSH Unix socket forwarding did not respect symlink permissions. Fixed in 1.98.10. An earlier fix in the same area as findings 1 to 3. It does not reference Tailworm.

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 253 (section lines 253-266): Finding 2 (T2) — ssh -R unix-socket forwarding replaces root-owned sockets (BindUnlink) — Severity: High

TW-03Finding 3 · T2-RACE / T3-RACEHighTailscale SSH

Tailscale SSH: a race (TOCTOU) defeats the socket-path allowlist

No fix published · checked 2026-10-29

What this means for you

This is why "only allow sockets in the user's own folders" does not protect you. Same precondition as findings 1 and 2. If an attacker gets this far on a tailnet without Tailnet Lock, the report shows they can add their own machine to your tailnet.

Component
Tailscale SSH server in tailscaled 1.104.2 (gliderssh socket-path validation)
Needs
As findings 1 and 2: an SSH accept rule with forwarding permitted; the race runs unprivileged inside the attacker's own session.
Impact
The home / /tmp / /run/user allowlist is not a security boundary: root-context bind, dial, chmod and unlink outside it. The report chains this to capturing enrollment material and enrolling a rogue node.

When it applies

  1. Not affected Every node we can see runs the version that fixes this finding. version_fixed
  2. Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
  3. Not affected Your policy has no Tailscale SSH rule, so no peer can open an SSH session. policy_provided and not ssh_policy_grants_access
  4. Not affected Forwarding is switched off on the node (TS_SSH_DISABLE_FORWARDING). ssh_forwarding_disabled
  5. Affected Tailscale SSH is on, your policy lets peers in, and forwarding is not switched off. ssh_server_on and ssh_policy_grants_access and not ssh_forwarding_disabled
  6. Conditional Tailscale SSH is on. Affected if any SSH rule lets a peer reach these nodes and forwarding is not switched off on them. ssh_server_on

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Earlier bulletins

  • TS-2026-004 (2026-06-03): SSH Unix socket forwarding did not respect symlink permissions. Fixed in 1.98.10. An earlier fix in the same area as findings 1 to 3. It does not reference Tailworm.

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 268 (section lines 268-362): Finding 3 (T2-RACE / T3-RACE) — TOCTOU defeats the path allowlist (bind and dial sides) — Severity: High

TW-04Finding 4 · C1HighApp connectors

conn25 app connectors: a user entitlement allows any egress destination

No fix published · checked 2026-10-29

What this means for you

Only matters if you use the experimental conn25 app-connector feature. Classic app connectors are not this finding. If you do use conn25, every user you gave an app to can reach far more than that app.

Component
tailscale 1.104.2, feature/conn25 connector data path (experimental)
Needs
The experimental conn25 feature is configured and the attacker holds an ordinary app entitlement. Nothing more is needed for the pivot itself.
Impact
An entitled user picks arbitrary destinations through the shared connector with no policy check of the real destination: cloud metadata, internal LANs and, under the Kubernetes operator, every cluster service.

When it applies

  1. Not affected Every node we can see runs the version that fixes this finding. version_fixed
  2. Not affected Your policy has no conn25 (experimental app connector) configuration. not conn25_configured
  3. Affected Users (not only tagged devices) are entitled to conn25 apps. conn25_user_entitlements
  4. Conditional conn25 is configured. Affected for every device entitled to one of its apps. conn25_configured

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 364 (section lines 364-463): Finding 4 (C1) — conn25: a user entitlement confers unrestricted egress selection through the connector (privilege-boundary flaw) — Severity: High

TW-05Finding 5 · T1HighTailscale SSH

Tailscale SSH: session churn pins memory in the root daemon (OOM loop)

No fix published · checked 2026-10-29

What this means for you

Anyone your policy lets SSH into a node can knock that node's Tailscale offline. The report found no Tailscale-side workaround: the only levers are SSH off or fewer SSH rules.

Component
Tailscale SSH server in tailscaled 1.104.2; inherited from upstream gliderlabs/ssh v0.3.8
Needs
An authenticated SSH session, as allowed by policy.
Impact
Memory exhaustion of the root tailscaled (about 536 MB in 7 s, persisting after disconnect): the node can drop off the tailnet.

When it applies

  1. Not affected Every node we can see runs the version that fixes this finding. version_fixed
  2. Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
  3. Not affected Your policy has no Tailscale SSH rule, so no peer can open an SSH session. policy_provided and not ssh_policy_grants_access
  4. Affected Tailscale SSH is on and your policy lets peers open sessions (forwarding settings do not matter here). ssh_server_on and ssh_policy_grants_access
  5. Conditional Tailscale SSH is on. Affected if any SSH rule lets a peer open a session. ssh_server_on

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 465 (section lines 465-473): Finding 5 (T1) — gliderssh session pin: root-tailscaled OOM loop — Severity: High

TW-06Finding 6 · C2 / C3 / C4Medium-Highheadscale

headscale: unauthenticated memory exhaustion and onboarding lockout; DERP client response bomb

No fix published · checked 2026-10-29

What this means for you

Only matters if you run your own headscale control server. On Tailscale's hosted control plane the headscale parts do not apply. The relay part (C3) needs your clients to be pointed at a relay an attacker controls.

Component
headscale 0.29.5 (embedded DERP, noise auth cache); tailscale client derphttp/magicsock for C3
Needs
Internet reachability of the headscale server, no account (C4); an authenticated insider (C2); a client pointed at a hostile relay (C3).
Impact
Control-server crash loop and blocked onboarding of new devices; client agent memory exhaustion from a hostile relay.

When it applies

  1. Not affected You use Tailscale's hosted control plane, so the headscale parts do not apply. (C3 needs a client pointed at a hostile relay.) control_plane_saas
  2. Conditional You use a self-hosted control server. Affected if it is reachable from the internet (the report tested headscale 0.29.5; 0.29.6 makes no claim about this finding). control_plane_headscale

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

  • headscale: set a systemd MemoryMax sized for the documented growth, enable restart supervision (Restart=always), put a rate-limiting reverse proxy in front of the registration endpoints, and expose the control plane only to an admin network or VPN.

    Tailworm report, lines 741-745; Tailworm report, lines 123-124 (summary mitigation (7))

  • Monitor headscale resident memory (it keeps peak RSS after an attack), registration-cache churn and registration latency.

    Tailworm report, lines 771-772

  • The fixes are vendor-side (vendor the upstream embedded-DERP fix, bound the noise auth cache, bound derphttp reads). headscale 0.29.6 (24 Oct 2026) does not say it addresses this.

    Tailworm report, lines 484-486; headscale project (official release page; headscale is not a Tailscale product) https://github.com/juanfont/headscale/releases (v0.29.6 release notes)

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 475 (section lines 475-486): Finding 6 — headscale v0.29.5: C2/C4 (memory exhaustion) + C3 upstream client bomb — Severity: Medium-High

TW-07Finding 7 · SERVE-F-1HighServe and Funnel

tailscale serve: a TCP forward to the tailscaled socket exposes the admin API

No fix published · checked 2026-10-29

What this means for you

Check every node that uses tailscale serve: a TCP forward to a Unix socket, and above all to tailscaled's own socket, must not exist. If you never forward to a Unix socket, this does not apply.

Component
tailscale 1.104.2, ipn/ipnlocal serve TCP forwarding
Needs
Someone with serve-config access (a local operator) sets up a TCP forward to the tailscaled socket; any ACL-authorized peer can then use it.
Impact
A remote peer gets read-write access to the node's local admin API: node takeover. The HTTP-proxy path has a guard; the raw TCP path does not.

When it applies

  1. Affected A serve TCP forward on this node points at tailscaled's own socket. serve_tailscaled_socket_forward
  2. Conditional A serve TCP forward on this node points at a Unix socket. Affected if that socket is privileged or its path can be swapped (finding 8). serve_unix_socket_forward
  3. Conditional A non-root operator on this node can change the serve config, so they could create such a forward. serve_operator_nonroot
  4. Not affected No serve forward to a Unix socket on this node. not serve_unix_socket_forward and not serve_operator_nonroot

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Earlier bulletins

  • TS-2026-005 (2026-06-03): Serve Unix socket proxy targets not restricted to root. Fixed in 1.98.9. An earlier fix in the serve Unix-socket area (findings 7 and 8). It does not reference Tailworm.

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 496 (section lines 496-516): Finding 7 (SERVE-F-1) — serve --tcp to the localapi socket: remote permit-write localapi — Severity: High

TW-08Finding 8 · SERVE-F-2MediumServe and Funnel

tailscale serve: the tailscaled-socket guard is checked only at config time

No fix published · checked 2026-10-29

What this means for you

If a serve backend on a node is a Unix socket in a folder other users can write to, that user can redirect it. Root-owned folders are safe; no Unix backends means not affected.

Component
tailscale 1.104.2, ipn/ipnlocal serve unix-proxy guard
Needs
Local write access to the directory of a configured Unix backend path, to swap it for a symlink after configuration.
Impact
Contained for the admin API itself; the residual risk is a root-context dial of any root-only Unix socket through serve or funnel.

When it applies

  1. Conditional This node serves a Unix-socket backend. Affected if a non-root user can write to that socket's directory. serve_unix_backend
  2. Not affected No Unix-socket serve backend on this node. not serve_unix_backend

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

  • Keep Unix-socket serve backends in root-owned directories that other users cannot write to (non-sticky shared directories are not protected), or use TCP backends.

    Tailworm report, lines 531-533

  • The real fix is vendor-side: re-validate the resolved target on every connection.

    Tailworm report, lines 533-534

Earlier bulletins

  • TS-2026-005 (2026-06-03): Serve Unix socket proxy targets not restricted to root. Fixed in 1.98.9. An earlier fix in the serve Unix-socket area (findings 7 and 8). It does not reference Tailworm.

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 518 (section lines 518-535): Finding 8 (SERVE-F-2) — isTailscaledSocket guard is config-time-only (TOCTOU) — Severity: Medium (contained)

TW-09Finding 9 · SERVE-F-3MediumServe and Funnel

Funnel: the funnel node attribute is enforced only by the CLI

No fix published · checked 2026-10-29

What this means for you

A threat-model note: the "funnel" permission in your policy is checked by the tailscale command, not by the daemon. Treat any node with funnel on as internet-facing and know why it is on.

Component
tailscale 1.104.2, funnel ingress gates; headscale 0.29.5 (cannot mint the ingress capability)
Needs
Write access to the node's local serve config (local admin API), without going through the CLI.
Impact
Funnel can be armed on a node without the control-minted funnel attribute; funnel X-Forwarded-For is only as trustworthy as the relay.

When it applies

  1. Conditional Funnel is on for this node: it is reachable from the internet, and the funnel permission is enforced only by the CLI. funnel_on
  2. Not affected Funnel is off on this node. not funnel_on

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 537 (section lines 537-559): Finding 9 (SERVE-F-3) — funnel entitlement enforcement is peer-cap + local config only (the node attribute is CLI-only) — Severity: Medium (threat-model finding)

TW-10Finding 10 · SERVE-F-4NoteServe and Funnel

TS_SERVE_ALLOW_ALL_INTERFACES exposes serve outside the tunnel

No fix published · checked 2026-10-29

What this means for you

Off by default and set mainly by the Kubernetes operator's experimental ingress. If you never set it, this does not apply.

Component
tailscale 1.104.2; non-default environment knob (upstream-acknowledged)
Needs
A network path (for example the LAN) to the node's Tailscale IP while the knob is set.
Impact
All serve backends on the node, funnel ones included, are reachable without WireGuard, ACLs or identity headers.

When it applies

  1. Affected TS_SERVE_ALLOW_ALL_INTERFACES is set and this node serves something. serve_allow_all_interfaces and serve_configured
  2. Conditional TS_SERVE_ALLOW_ALL_INTERFACES is set; any serve config added later is exposed beyond the tunnel. serve_allow_all_interfaces
  3. Not affected TS_SERVE_ALLOW_ALL_INTERFACES is not set for tailscaled on this node. not serve_allow_all_interfaces

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

  • Unset TS_SERVE_ALLOW_ALL_INTERFACES in the tailscaled environment unless you rely on the Kubernetes operator's cluster-traffic ingress.

    Tailworm report, lines 565-571

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 561 (section lines 561-572): Finding 10 (SERVE-F-4) — TS_SERVE_ALLOW_ALL_INTERFACES exposes serve outside the tunnel bind — Configuration note

TW-11Finding 11 · SERVE-F-5MediumServe and Funnel

tailscale serve over HTTP/2: app-capability headers can bleed between mounts

No fix published · checked 2026-10-29

What this means for you

Only matters if one serve host:port has several paths that pass different Tailscale app capabilities to your app, and your app trusts that header for authorization.

Component
tailscale 1.104.2, ipn/ipnlocal serveWebHandler
Needs
Serve over TLS (h2), two or more mounts with different AcceptAppCaps, a peer holding the capability, concurrent streams on one connection.
Impact
A small share of requests to a mount that accepts no capabilities carries another mount's capability header; apps that authorize by that header can be bypassed (same-peer bound).

When it applies

  1. Conditional This node serves mounts with different AcceptAppCaps on one host:port. Affected if your app authorizes by the Tailscale-App-Capabilities header. serve_appcaps_split
  2. Not affected No serve host:port on this node mixes mounts with different AcceptAppCaps. not serve_appcaps_split

The audit checks these in order and the first one that matches decides. A rule that needs an input you did not give makes the verdict Unknown, naming that input.

What to do

Source: Tailworm report, source/tailworm.com-2026-10-25.md line 574 (section lines 574-598): Finding 11 (SERVE-F-5) — h2 shared serve-context race: cross-mount capability bleed → mount-level authorization bypass — Severity: Medium within header-authorization deployments; Low-Medium otherwise

Vendor response

As of 2026-10-25 Tailscale has published no security bulletin, release note or changelog entry that acknowledges or fixes the Tailworm findings. The latest client is 1.104.2, the version the report tested.

A response by Tailscale's VP of Engineering on r/Tailscale ("Recent tailscale vulnerability reports", 25 Oct 2026) is reported by third parties, but Reddit refused every fetch, so we do not describe Tailscale's position. The only Tailscale-side words we could read are an employee comment on Hacker News: "we will have more details to follow."

The researcher says the issues were "reported but not coordinated" (Hacker News). headscale 0.29.6 (2026-10-24) is newer than the 0.29.5 the report tested; no vendor statement ties it to Finding 6.

Earlier bulletins in the same areas

How a finding becomes fixed here

A finding is marked fixed only when a GitHub release (tailscale/tailscale; juanfont/headscale for finding 6) or a Tailscale security bulletin names it (the word Tailworm with the finding number, or a CVE the knowledge base links to it) in a sentence that states a fix with no negation or hedging, and gives a version (a release counts as its own version; a bulletin must state one). Prereleases and drafts never count. Anything less is shown as "mentioned" and never counts as fixed. A fixed finding never goes back.

Where the sources disagree

Shown side by side and labelled. We do not pick a winner where the vendor has not spoken.

Disable Tailscale SSH?

The report says

The SSH findings apply where Tailscale SSH is enabled ("opt-in per node, often fleet-wide"); its first mitigation is per-pair SSH grants.

Tailworm report, lines 96-97; Tailworm report, lines 112-113

Tailscale says

Unknown. Tailscale has not published guidance on these findings that we could read. Its docs describe how to turn SSH off (tailscale set --ssh=false) and warn to keep another way in.

Tailscale documentation https://tailscale.com/docs/features/tailscale-ssh

Geoa reads it as

We show both and present neither as Tailscale guidance on Tailworm: turn Tailscale SSH off on nodes that do not need it (the documented tailscale set --ssh=false, after making sure there is another way in), and where it stays on, narrow the SSH rules to per-pair grants (the report's mitigation 1).

How to switch forwarding off on Tailscale (SaaS)

The report says

Set the per-rule forwarding fields to false in the policy ("on SaaS, set the per-rule forwarding fields to false"; "set forwarding flags to denied per action").

Tailworm report, lines 113-114; Tailworm report, lines 705-706

Tailscale says

The documented policy-file syntax has no per-rule forwarding fields. allowAgentForwarding / allowLocalPortForwarding / allowRemotePortForwarding exist only in the control-to-node wire format (tailcfg.SSHAction). Whether the hosted policy parser accepts them is not documented.

Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file; Verified in the open-source code (not in the public docs) https://github.com/tailscale/tailscale/blob/v1.104.2/tailcfg/tailcfg.go

Geoa reads it as

We do not recommend undocumented policy keys. If they appear in your policy we report them as present but unverified. The node-level env knob is verified in code.