mirror of
https://github.com/tailscale/tailscale.git
synced 2026-10-30 23:20:13 -04:00
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>