Back

27/10/2026

Responding to Tailworm: hardening our Tailscale network without breaking production

How we checked our Tailscale network against the Tailworm findings, removed the Tailscale SSH exposure and kept every production job running.

Summary

On October 25, 2026, a security researcher published “Tailworm”: 11 findings in Tailscale and headscale, released without coordinated disclosure. At the time of writing, Tailscale has not published a fix. The same evening, we checked our own Tailscale network against each finding. We removed the one exposure that applied to us, tightened access, and kept every production job running throughout.

This post covers what we checked, what we changed, how we avoided an outage, and what we learned. It is not an independent verification of the report.

What Tailworm reported, briefly

The 11 findings fall into a few groups:

  • Tailscale SSH (the most serious). An authorized SSH session could be escalated to root on the target machine. In the researcher’s lab, this then spread from machine to machine.
  • App connectors. Destinations are not validated.
  • Memory exhaustion, in the control server and in clients.
  • Serve and Funnel edge cases.

Every finding requires an existing foothold. None of them is a remote attack from the internet. Some security practitioners argue the framing overstates the risk, but the main exposure, Tailscale SSH, is real and easy to remove.

Step 1: Check before you change

We started with read-only checks and recorded the results before touching anything:

  • the Tailscale client versions in use
  • which features were actually turned on
  • 60 days of SSH session logs, to see who connected from where and whether anyone used port or socket forwarding
  • the device list, including devices that had not been seen for months

Findings:

  • Tailscale SSH was enabled in part of our environment, so findings F1–F3 and F5 were relevant to us.
  • The features that the other findings depend on were not in use, so most of them did not apply.
  • The SSH logs showed no unexpected sources and no forwarding requests.

Step 2: Remove the exposure without an outage

The obvious fix was to turn Tailscale SSH off and use standard OpenSSH key authentication instead. Our pre-check found a trap.

Tailscale SSH authenticates by tailnet identity, not by SSH key. Our automation scripts passed an SSH key on the command line, but that key was passphrase-protected and could never have worked unattended. The jobs only ran because Tailscale SSH never looked at the key. If we had simply turned Tailscale SSH off, every automated job would have stopped.

So we did the switch in this order:

  1. Created a dedicated key for automation, restricted to a single source machine, with forwarding disabled.
  2. Tested the new path before the switch. We opened a temporary OpenSSH port, reachable only inside the tailnet, and connected from an environment like the one our jobs run in (no SSH agent, no terminal), using every way our scripts call SSH.
  3. Ran negative tests. The new key was rejected from outside its allowed source, and forwarding requests were refused.
  4. Armed an automatic rollback first. Before turning Tailscale SSH off, we scheduled a timer that would turn it back on by itself. This recovery path did not depend on SSH working.
  5. Switched, then checked every automated job with new connections. Each job completed a real run. We did not count an already-open session as proof.
  6. Removed the temporary pieces — the extra port, the firewall rule and the rollback timer — and verified once more.

Step 3: Tighten what was left

  • Emptied the Tailscale SSH policy. The default policy let any of a user’s devices open SSH sessions to their other devices, including as root, after a weekly browser re-approval. We emptied it, so turning Tailscale SSH back on by mistake no longer grants access.
  • Closed SSH to the internet. Before closing it, we confirmed that an out-of-band console login works, so we could not lock ourselves out. Afterwards we verified from an external network that SSH no longer answers.
  • Required device approval. New devices must be approved by an administrator before they can join.
  • Checked enrollment keys. There were none to revoke.
  • Confirmed MFA on the administrator account. Network-level controls can be undone by anyone who takes over the admin login.
  • Started observing real traffic between devices, so that the next step — narrowing which devices may talk to which services — is based on evidence, not guesses.

Tailnet Lock and device approval both protect against a rogue device joining, but they cannot be used together, so choose one deliberately.

Step 4: Fix the runbook, not just the server

Our old recovery procedure said to reconnect the server with Tailscale SSH turned on. Followed after this change, it would have quietly brought the risky feature back. We rewrote it:

  • Diagnose first. Is the network connection down, or is it SSH itself (keys, configuration, firewall)? The two have different fixes.
  • Reconnect without changing settings. Use no extra flags, save the node’s settings before reconnecting, and compare them afterwards.
  • Open emergency access only on a timer. Get approval first, schedule an automatic close before opening, restrict the source, and check IPv4 and IPv6.
  • Do not re-enable Tailscale SSH, even in an emergency. The access-policy change it needs cannot be rolled back automatically.

What we learned

  1. Know what a security feature actually authenticates before you remove it. The key our scripts named had never been used.
  2. Test the exact path you are moving to, in the same environment your jobs run in, before you cut over.
  3. Set up a rollback that does not depend on the thing you are changing. A timer, not “we’ll SSH back in and fix it.”
  4. Check recovery access before closing a door. We confirmed console access before closing SSH to the internet.
  5. Update the runbook in the same change. An old recovery step can undo a security fix months later.
  6. Read default policies. “Allow your own devices, including root” was the default we started with.
  7. Keep “the change worked” separate from “the vulnerability is resolved.” We are no longer exposed to the Tailscale SSH findings, but Tailworm as a whole is not closed until upstream fixes ship. We will update our clients when they do.
  8. Measure, then narrow. Observed traffic and a list of real dependencies come before tighter access rules, so hardening does not break the business.

What’s next

We are narrowing network access between devices to the minimum each service needs, based on observed traffic. We will install Tailscale’s fixes as soon as they are released.

References: the Tailworm report (tailworm.com) and Tailscale’s documentation on Tailnet Lock.