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
- Not affected Every node we can see runs the version that fixes this finding. version_fixed
- Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
- 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
- Not affected Forwarding is switched off on the node (TS_SSH_DISABLE_FORWARDING). ssh_forwarding_disabled
- 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
- 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
- Turn Tailscale SSH off on every node that does not need it:
tailscale set --ssh=false (make sure you have another way in first).Tailscale documentation https://tailscale.com/docs/features/tailscale-ssh (documented way to disable Tailscale SSH, with the lock-out warning); Tailworm report, lines 96-97 (the SSH findings apply only where Tailscale SSH is enabled)
- Rewrite SSH rules as per-pair grants: one specific source to one specific destination. Remove identity-wide (autogroup:member, broad groups) and tag-wide accept rules, and never target root or passwordless-sudo accounts.
Tailworm report, lines 112-113 (mitigation (1), the one control that bounded the spread in the lab); Tailworm report, lines 700-704; Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file (ssh rule fields: action, src, dst, users, checkPeriod, acceptEnv, srcPosture)
- Switch SSH forwarding off on the node: set TS_SSH_DISABLE_FORWARDING=true in the tailscaled environment (for systemd: /etc/default/tailscaled) and restart tailscaled. This env knob blocks local, remote and Unix-socket forwarding in tailscaled 1.104.2.
Verified in the open-source code (not in the public docs) https://github.com/tailscale/tailscale/blob/v1.104.2/ssh/tailssh/tailssh.go#L54 (sshDisableForwarding envknob; checked first in all four forwarding handlers (lines 527 to 581))
The sources disagree on this step
- On headscale 0.29.5 forwarding cannot be switched off in policy: it is granted on every accepted session. Until fixed, treat every headscale SSH grant as root-equivalent and cut the rule set down accordingly.
Tailworm report, lines 114-115; Tailworm report, lines 704-705; Verified in the open-source code (not in the public docs) https://github.com/juanfont/headscale/blob/v0.29.5/hscontrol/policy/v2/filter.go (sshAccept sets AllowAgentForwarding / AllowLocalPortForwarding / AllowRemotePortForwarding to true)
- Do not count
check mode as protection here: it gates session start only, and forwarding happens inside the approved session. For privileged rules still prefer a short checkPeriod (minutes or "always").Tailworm report, lines 655-660; Tailworm report, lines 688-691; Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file (checkPeriod 1m to 168h or always; default 12h)
- Restricting SSH to non-root accounts is defense in depth only; these findings work from fully unprivileged accounts.
Tailworm report, lines 116-117
- On SSH-enabled Linux nodes, add nftables rules matching the tailscaled cgroup that reject output to loopback, link-local (metadata) and RFC1918 unless allowlisted. Firewalls cannot see Unix-socket operations, so this limits network reach only.
Tailworm report, lines 727-733; Tailworm report, lines 735-739
- Detection while unpatched: audit unix-socket unlink/bind/chmod in /tmp, home directories and /run/user, and alert on rename storms in those directories.
Tailworm report, lines 753-756; Tailworm report, lines 763-766
- Give identities a clock: enable node-key expiry on server fleets (tagged devices start with expiry disabled) and use TPM / Secure-Enclave-bound state where the hardware allows.
Tailworm report, lines 747-749; Tailworm report, lines 117-118 (summary mitigation (3)); Tailscale documentation https://tailscale.com/docs/features/access-control/key-expiry (default 180 days; tagged devices have expiry disabled on first tag + auth)
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
- Not affected Every node we can see runs the version that fixes this finding. version_fixed
- Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
- 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
- Not affected Forwarding is switched off on the node (TS_SSH_DISABLE_FORWARDING). ssh_forwarding_disabled
- 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
- 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
- Turn Tailscale SSH off on every node that does not need it:
tailscale set --ssh=false (make sure you have another way in first).Tailscale documentation https://tailscale.com/docs/features/tailscale-ssh (documented way to disable Tailscale SSH, with the lock-out warning); Tailworm report, lines 96-97 (the SSH findings apply only where Tailscale SSH is enabled)
- Rewrite SSH rules as per-pair grants: one specific source to one specific destination. Remove identity-wide (autogroup:member, broad groups) and tag-wide accept rules, and never target root or passwordless-sudo accounts.
Tailworm report, lines 112-113 (mitigation (1), the one control that bounded the spread in the lab); Tailworm report, lines 700-704; Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file (ssh rule fields: action, src, dst, users, checkPeriod, acceptEnv, srcPosture)
- Switch SSH forwarding off on the node: set TS_SSH_DISABLE_FORWARDING=true in the tailscaled environment (for systemd: /etc/default/tailscaled) and restart tailscaled. This env knob blocks local, remote and Unix-socket forwarding in tailscaled 1.104.2.
Verified in the open-source code (not in the public docs) https://github.com/tailscale/tailscale/blob/v1.104.2/ssh/tailssh/tailssh.go#L54 (sshDisableForwarding envknob; checked first in all four forwarding handlers (lines 527 to 581))
The sources disagree on this step
- On headscale 0.29.5 forwarding cannot be switched off in policy: it is granted on every accepted session. Until fixed, treat every headscale SSH grant as root-equivalent and cut the rule set down accordingly.
Tailworm report, lines 114-115; Tailworm report, lines 704-705; Verified in the open-source code (not in the public docs) https://github.com/juanfont/headscale/blob/v0.29.5/hscontrol/policy/v2/filter.go (sshAccept sets AllowAgentForwarding / AllowLocalPortForwarding / AllowRemotePortForwarding to true)
- Do not count
check mode as protection here: it gates session start only, and forwarding happens inside the approved session. For privileged rules still prefer a short checkPeriod (minutes or "always").Tailworm report, lines 655-660; Tailworm report, lines 688-691; Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file (checkPeriod 1m to 168h or always; default 12h)
- Detection while unpatched: audit unix-socket unlink/bind/chmod in /tmp, home directories and /run/user, and alert on rename storms in those directories.
Tailworm report, lines 753-756; Tailworm report, lines 763-766
- Give identities a clock: enable node-key expiry on server fleets (tagged devices start with expiry disabled) and use TPM / Secure-Enclave-bound state where the hardware allows.
Tailworm report, lines 747-749; Tailworm report, lines 117-118 (summary mitigation (3)); Tailscale documentation https://tailscale.com/docs/features/access-control/key-expiry (default 180 days; tagged devices have expiry disabled on first tag + auth)
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
- Not affected Every node we can see runs the version that fixes this finding. version_fixed
- Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
- 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
- Not affected Forwarding is switched off on the node (TS_SSH_DISABLE_FORWARDING). ssh_forwarding_disabled
- 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
- 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
- Turn Tailscale SSH off on every node that does not need it:
tailscale set --ssh=false (make sure you have another way in first).Tailscale documentation https://tailscale.com/docs/features/tailscale-ssh (documented way to disable Tailscale SSH, with the lock-out warning); Tailworm report, lines 96-97 (the SSH findings apply only where Tailscale SSH is enabled)
- Rewrite SSH rules as per-pair grants: one specific source to one specific destination. Remove identity-wide (autogroup:member, broad groups) and tag-wide accept rules, and never target root or passwordless-sudo accounts.
Tailworm report, lines 112-113 (mitigation (1), the one control that bounded the spread in the lab); Tailworm report, lines 700-704; Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file (ssh rule fields: action, src, dst, users, checkPeriod, acceptEnv, srcPosture)
- Switch SSH forwarding off on the node: set TS_SSH_DISABLE_FORWARDING=true in the tailscaled environment (for systemd: /etc/default/tailscaled) and restart tailscaled. This env knob blocks local, remote and Unix-socket forwarding in tailscaled 1.104.2.
Verified in the open-source code (not in the public docs) https://github.com/tailscale/tailscale/blob/v1.104.2/ssh/tailssh/tailssh.go#L54 (sshDisableForwarding envknob; checked first in all four forwarding handlers (lines 527 to 581))
The sources disagree on this step
- On headscale 0.29.5 forwarding cannot be switched off in policy: it is granted on every accepted session. Until fixed, treat every headscale SSH grant as root-equivalent and cut the rule set down accordingly.
Tailworm report, lines 114-115; Tailworm report, lines 704-705; Verified in the open-source code (not in the public docs) https://github.com/juanfont/headscale/blob/v0.29.5/hscontrol/policy/v2/filter.go (sshAccept sets AllowAgentForwarding / AllowLocalPortForwarding / AllowRemotePortForwarding to true)
- Do not count
check mode as protection here: it gates session start only, and forwarding happens inside the approved session. For privileged rules still prefer a short checkPeriod (minutes or "always").Tailworm report, lines 655-660; Tailworm report, lines 688-691; Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file (checkPeriod 1m to 168h or always; default 12h)
- Detection while unpatched: audit unix-socket unlink/bind/chmod in /tmp, home directories and /run/user, and alert on rename storms in those directories.
Tailworm report, lines 753-756; Tailworm report, lines 763-766
- Plan Tailnet Lock so a captured auth key cannot enroll a rogue node on its own. The admin console needs at least two signing nodes; keep the disablement secrets offline (the report notes they can be captured in transit on a compromised node).
Tailworm report, lines 324-330; Tailscale documentation https://tailscale.com/docs/features/tailnet-lock (at least two signing nodes; max 20; disablement secrets)
- Give identities a clock: enable node-key expiry on server fleets (tagged devices start with expiry disabled) and use TPM / Secure-Enclave-bound state where the hardware allows.
Tailworm report, lines 747-749; Tailworm report, lines 117-118 (summary mitigation (3)); Tailscale documentation https://tailscale.com/docs/features/access-control/key-expiry (default 180 days; tagged devices have expiry disabled on first tag + auth)
- On suspicion, delete the device in the admin console (reimaging alone does not revoke copied keys), then rotate auth keys and credentials reachable from it.
Tailworm report, lines 776-785; Tailworm report, lines 120-123 (summary mitigation (6))
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
- Not affected Every node we can see runs the version that fixes this finding. version_fixed
- Not affected Your policy has no conn25 (experimental app connector) configuration. not conn25_configured
- Affected Users (not only tagged devices) are entitled to conn25 apps. conn25_user_entitlements
- 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
- Remove the connector capability from clients that do not need it.
Tailworm report, lines 713-715
- In Kubernetes, apply a default-deny egress NetworkPolicy to the connector namespace.
Tailworm report, lines 715-716; Tailworm report, lines 459-463
- On connector hosts, enforce a kernel-layer destination allowlist (netfilter FORWARD/INPUT for tailnet-sourced traffic): drop 169.254.0.0/16 always, default-deny RFC1918 with an allowlist for the intended backends. Per-connector, not per-peer.
Tailworm report, lines 716-725; Tailworm report, lines 447-450
- conn25 is experimental and not in the public docs; treat any tailscale.com/app-connectors-experimental or tailscale.com/cap/conn25/ entry as something to justify or remove. Documented app connectors use nodeAttrs app "tailscale.com/app-connectors".
Tailworm report, lines 213-214; Verified in the open-source code (not in the public docs) https://github.com/tailscale/tailscale/blob/v1.104.2/feature/conn25/conn25.go; Tailscale documentation https://tailscale.com/docs/features/app-connectors/how-to/setup
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
- Not affected Every node we can see runs the version that fixes this finding. version_fixed
- Not affected Tailscale SSH is off on every node we can see. not ssh_server_on
- 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
- 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
- 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
- Turn Tailscale SSH off on every node that does not need it:
tailscale set --ssh=false (make sure you have another way in first).Tailscale documentation https://tailscale.com/docs/features/tailscale-ssh (documented way to disable Tailscale SSH, with the lock-out warning); Tailworm report, lines 96-97 (the SSH findings apply only where Tailscale SSH is enabled)
- Rewrite SSH rules as per-pair grants: one specific source to one specific destination. Remove identity-wide (autogroup:member, broad groups) and tag-wide accept rules, and never target root or passwordless-sudo accounts.
Tailworm report, lines 112-113 (mitigation (1), the one control that bounded the spread in the lab); Tailworm report, lines 700-704; Tailscale documentation https://tailscale.com/docs/reference/syntax/policy-file (ssh rule fields: action, src, dst, users, checkPeriod, acceptEnv, srcPosture)
- No Tailscale-side workaround was identified; the fix belongs upstream (bounded buffers in gliderlabs/ssh). Watch tailscaled memory on SSH-enabled nodes.
Tailworm report, lines 471-473
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
- 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
- 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
- Affected A serve TCP forward on this node points at tailscaled's own socket. serve_tailscaled_socket_forward
- 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
- Conditional A non-root operator on this node can change the serve config, so they could create such a forward. serve_operator_nonroot
- 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
- Conditional This node serves a Unix-socket backend. Affected if a non-root user can write to that socket's directory. serve_unix_backend
- 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
- 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
- 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
- Affected TS_SERVE_ALLOW_ALL_INTERFACES is set and this node serves something. serve_allow_all_interfaces and serve_configured
- Conditional TS_SERVE_ALLOW_ALL_INTERFACES is set; any serve config added later is exposed beyond the tunnel. serve_allow_all_interfaces
- 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
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
- 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
- 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
- TS-2026-004 (2026-06-03): SSH Unix socket forwarding did not respect symlink permissions. Fixed in 1.98.10. The bulletin says 1.98.9 or newer; the changelog lists the fix under 1.98.10. We use 1.98.10 or later. An earlier fix in the same area as findings 1 to 3. It does not reference Tailworm.
- 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.
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.