Tailscale’s macOS Network Extension silently breaks Rust binaries

debugging
rust
macos
Published

March 26, 2026

If you’re running Tailscale on macOS and your Rust binary can’t connect to local network IPs — this is probably why.

The symptom

Any TCP connection from a Rust-compiled binary to a local IP (e.g. 10.0.0.167:7777) fails with:

No route to host (os error 65)

Meanwhile curl, python, nc all connect to the same address just fine. Same machine, same moment.

The cause

Tailscale’s macOS Network Extension (io.tailscale.ipn.macsys.network-extension) intercepts TCP connections from non-Apple-signed binaries. Apple’s own system tools are signed arm64e and bypass the extension entirely. Rust binaries compile to arm64 with ad-hoc signatures — so they get intercepted.

This happens below the application layer. It doesn’t matter what HTTP client you use or how you configure it.

What doesn’t work

I tried all of these — none had any effect:

  • reqwest::Client::builder().no_proxy() — problem is below app layer
  • Proxy::custom(|_| None) — same reason
  • tailscale set --exit-node-allow-lan-access=true — no exit node configured, irrelevant
  • Binding the socket to en0’s local IP before connecting — still intercepted
  • Even raw libc::connect() from Rust fails while Python’s socket.connect() succeeds simultaneously

Possible solutions

Still investigating, but the options are:

  1. Code-sign the binary with a real developer certificate (not ad-hoc)
  2. Local proxy relay — use socat or SSH tunnel (Apple-signed) to forward traffic
  3. Switch Tailscale to userspace networking mode instead of Network Extension
  4. Tailscale admin console — exclude local subnets from interception
  5. NEFilterManager API — programmatically request exclusion

You’re not alone

I searched around and found no one reporting this exact combination (Tailscale + Rust binary + local network), but there are two well-documented issues that likely overlap:

Tailscale routing bugs on macOS

  • tailscale/tailscale#15366 — With an exit node active on macOS, local network access breaks even with “Allow Local Network Access” enabled. Tailscale installs incorrect routing rules. Still broken on macOS 26 Beta.
  • tailscale/tailscale#1344 — Hosts on the same subnet become unreachable through Tailscale due to route priority: local subnet routes are narrower and win over Tailscale’s wider routes.

macOS Sequoia Local Network Privacy

Starting with macOS 15 Sequoia, Apple added a Local Network Privacy layer that silently blocks third-party binaries from accessing local network resources:

  • Apple Developer Forums — Third-party binaries launched from launchd agents get denied local network access even after the user approves the privacy prompt. A known Sequoia bug.
  • Mozilla Bug #1919889 — Firefox couldn’t access local network sites on Sequoia — same class of issue.
  • Michael Tsai’s blog — Detailed roundup of developer reports on Sequoia’s Local Network Privacy silently denying permissions.
  • Hackaday — macOS 15.1 forces application signing; ad-hoc signed binaries face stricter restrictions.

What this means

The root cause is likely one or both of these issues stacking:

  1. Tailscale installs broken routes for local subnets on macOS
  2. macOS Sequoia’s Local Network Privacy silently blocks ad-hoc signed binaries

It’s probably not just the Network Extension intercepting traffic — there may be an OS-level privacy gate involved too.

Takeaway

If you’re debugging “No route to host” errors that only affect your compiled binaries on macOS, check two things:

  1. Whether a Network Extension (Tailscale, Little Snitch, etc.) is intercepting your traffic
  2. Whether macOS Local Network Privacy is silently blocking your binary (System Settings > Privacy & Security > Local Network)

Tracking this at rararulab/rara#1023.