James Tucker eba00ab363 ssh/tailssh: route SSH forwarding through the privileged monitor
Route tailssh's forwarding through the per-connection ssh-monitor, so
on unix forwarded sockets are dialed and bound by children with the
authenticated local user's credentials, never by root tailscaled.

- unix -L, direct-streamlocal: the lexical allowlist check stays in
  tailscaled as coarse policy. The allowlist is home, /tmp, and
  /run/user/<uid>. Relative and abstract paths are rejected. The dial
  then runs as the user through the monitor. The EvalSymlinks
  re-validation is dropped, and the kernel's DAC is the boundary. The
  allowlist is capped. A home directory that cleans to "/" or is empty
  would prefix-match every absolute path, and such an entry is dropped
  instead. Paths at or beyond the sun_path capacity reject lexically
  rather than dying at bind or dial with a generic EINVAL.
- unix -R, streamlocal-forward: the listener is created by a user-cred
  child with umask-at-bind 0600 and unlink-if-owned. Closing it does
  not unlink the socket, and the stale file on cancel is accepted.
- TCP -R, tcpip-forward: a tailssh-owned handler replaces gliderssh's
  ForwardedTCPHandler. The bind goes through the monitor's listen op
  with the local user's credentials, so privileged ports fail by
  kernel construction for non-root-mapped users. Root-mapped users are
  allowed, as in OpenSSH. Bind addresses are loopback-only unless
  TS_SSH_FORWARD_GATEWAY_PORTS is set, the equivalent of sshd's
  GatewayPorts.
- agent forwarding: the socket and its containing directory are
  created by a user-cred child, a 0600 socket in a 0700 user-owned
  directory, instead of a root-run MkdirTemp, Chmod, and Chown
  sequence. Session-end cleanup is unchanged.

The monitor is spawned lazily on the first forwarding or agent need,
from the tailscaled binary, and closed when the SSH connection ends.
closeFwdMonitor latches a closed flag under the fwd lock, so a caller
racing the teardown is refused by fwdMonitor, or reads the current
state without spawning through fwdMonitorIfAny, instead of respawning
a monitor nobody ever closes.

plan9 keeps today's behavior through a build-tagged seam, fwdmonitor.go
and fwdplan9.go. There is no monitor on plan9. Unix forwarding is
rejected there, and reverse TCP forwarding keeps the gliderssh
handler with the mayReversePortForwardTo gate.

Tests cover conn-level callbacks against a real monitor and real
one-shot fwd children: allowlist rejections, user-owned 0600 sockets, a
foreign sticky-dir socket not unlinked, loopback, gateway-ports, and
privileged-port TCP binds, agent directory hygiene and cleanup, and
TS_SSH_DISABLE_FORWARDING coverage, plus refused respawn after
teardown. The unix-fwd root-dial regressions cover a static
in-allowlist symlink to a root-owned 0600 canary, which must fail
permission denied as the bound user, and a dir-swap race, which must
never serve the protected canary.

Updates tailscale/corp#50380

Signed-off-by: James Tucker <james@tailscale.com>
2026-10-25 20:08:58 -07:00
2026-10-19 10:18:56 -07:00
2026-09-29 13:16:16 -07:00
2026-10-22 10:52:43 -04:00
2026-01-27 16:15:17 -08:00
2026-08-28 12:47:04 -07:00
2026-09-17 16:55:23 -07:00

Tailscale

https://tailscale.com

Private WireGuard® networks made easy

Overview

This repository contains the majority of Tailscale's open source code. Notably, it includes the tailscaled daemon and the tailscale CLI tool. The tailscaled daemon runs on Linux, Windows, macOS, and to varying degrees on FreeBSD and OpenBSD. The Tailscale iOS and Android apps use this repo's code, but this repo doesn't contain the mobile GUI code.

Other Tailscale repos of note:

For background on which parts of Tailscale are open source and why, see https://tailscale.com/opensource/.

Using

We serve packages for a variety of distros and platforms at https://pkgs.tailscale.com.

Other clients

The macOS, iOS, and Windows clients use the code in this repository but additionally include small GUI wrappers. The GUI wrappers on non-open source platforms are themselves not open source.

Building

We always require the latest Go release, currently Go 1.27. (While we build releases with our Go fork, its use is not required.)

go install tailscale.com/cmd/tailscale{,d}

If you're packaging Tailscale for distribution, use build_dist.sh instead, to burn commit IDs and version info into the binaries:

./build_dist.sh tailscale.com/cmd/tailscale
./build_dist.sh tailscale.com/cmd/tailscaled

If your distro has conventions that preclude the use of build_dist.sh, please do the equivalent of what it does in your distro's way, so that bug reports contain useful version information.

Bugs

Please file any issues about this code or the hosted service on the issue tracker.

Contributing

PRs welcome! But please file bugs. Commit messages should reference bugs.

We require Developer Certificate of Origin Signed-off-by lines in commits.

See commit-messages.md (or skim git log) for our commit message style.

About Us

Tailscale is primarily developed by the people at https://github.com/orgs/tailscale/people. For other contributors, see:

WireGuard is a registered trademark of Jason A. Donenfeld.

S
Description
No description provided
Readme BSD-3-Clause
233 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 96.4%
C 1.2%
TypeScript 0.8%
Shell 0.5%
Swift 0.3%
Other 0.4%