mirror of
https://github.com/tailscale/tailscale.git
synced 2026-10-29 14:39:16 -04:00
Add the one-shot "be-child ssh-fwd" children that perform forwarded socket work, and the monitor and client plumbing for the socket ops. The conn and session flows do not use the ops yet. The next commit routes them. - fwdchild.go: a child re-execed by the monitor, born unprivileged at clone. The monitor applies the connection's bound identity as SysProcAttr.Credential, so the kernel's DAC sees the connection user's permissions. It dials a unix socket, or creates one with umask-at-bind mode and optional unlink-if-owned, or binds a TCP listener, or creates the agent-forwarding socket in a user-owned directory. The result returns to the monitor over an inherited socketpair as one status line with at most one descriptor attached. - monitor.go: handleDial and handleListen run the ops on one-shot children with a spawn-to-result deadline, monFwdOpDeadline. On expiry the child's group is SIGKILLed. killChild reaps such a child with a bounded non-blocking wait. A child left in uninterruptible sleep is abandoned in its own group rather than blocking the loop goroutine. The dial and listen placeholder replies are gone, and their not-implemented test with them. - monitor_client.go: Dial, Listen, and ListenAgent over the existing request-ID plumbing. - incubator.go, monitor_linux.go, monitor_other.go: registration and per-platform spawn attributes for the fwd children. Tests drive real children across uid boundaries. They cover dial and bind round trips, unix and TCP listens, agent-listen directory hygiene, unlink-if-owned, peer-credential assertions, and the no-identity-keys-on-the-wire shape of every request. Forwarding children spawn with an empty environment through spawnRPCChild: they take everything from argv and the result socket, and the daemon's environment must not reach a child running as the connection's user. Updates tailscale/corp#50380 Signed-off-by: James Tucker <james@tailscale.com>
56 lines
1.7 KiB
Go
56 lines
1.7 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux && !android
|
|
|
|
package tailssh
|
|
|
|
import (
|
|
"math"
|
|
"syscall"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// monChildAttr returns the SysProcAttr for one monitor child. A ctty
|
|
// child gets Setsid+Setctty with the pty slave as its fd 0; Setpgid is
|
|
// set only for non-ctty children. Pdeathsig is advisory: it fires on
|
|
// the exit of the creating thread, not the process, per golang/go#27505.
|
|
func monChildAttr(ctty bool) *syscall.SysProcAttr {
|
|
return &syscall.SysProcAttr{
|
|
Setpgid: !ctty,
|
|
Setsid: ctty,
|
|
Setctty: ctty,
|
|
Pdeathsig: unix.SIGHUP,
|
|
}
|
|
}
|
|
|
|
// scrubFds marks every open descriptor from 3 up close-on-exec, so the
|
|
// monitor cannot leak an inherited or late-created descriptor into a
|
|
// child it execs. close_range(CLOSE_RANGE_CLOEXEC) does this in one
|
|
// atomic syscall on kernels 5.11 and later; older kernels fall back to
|
|
// a /proc/self/fd enumeration.
|
|
func scrubFds() error {
|
|
if err := unix.CloseRange(3, math.MaxUint32, unix.CLOSE_RANGE_CLOEXEC); err == nil {
|
|
return nil
|
|
}
|
|
return scrubFdsByScan("/proc/self/fd")
|
|
}
|
|
|
|
// fwdChildAttr returns the SysProcAttr for a one-shot forwarding child
|
|
// whose identity must be applied, that is a root monitor's child. The
|
|
// Credential carries the connection's bound identity from ma. The child
|
|
// leads its own group so the op deadline and teardown can kill the
|
|
// whole group, and Pdeathsig is set as in monChildAttr.
|
|
func fwdChildAttr(ma monitorArgs) *syscall.SysProcAttr {
|
|
return &syscall.SysProcAttr{
|
|
Credential: &syscall.Credential{
|
|
Uid: ma.uid,
|
|
Gid: ma.gid,
|
|
Groups: fwdCredGroups(ma.gid, ma.gids),
|
|
},
|
|
Setpgid: true,
|
|
Pdeathsig: unix.SIGHUP,
|
|
}
|
|
}
|