diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index 7cc9aff35..352442b51 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -182,6 +182,10 @@ func main() { return } + if runtime.GOOS == "plan9" && os.Getenv("_NETSHELL_CHILD_") != "" { + os.Args = []string{"tailscaled", "be-child", "plan9-netshell"} + } + if len(os.Args) > 1 { sub := os.Args[1] if fp, ok := subCommands[sub]; ok { diff --git a/go.mod b/go.mod index 42454ddd4..5f8219464 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module tailscale.com go 1.24.0 require ( + 9fans.net/go v0.0.8-0.20250307142834-96bdba94b63f filippo.io/mkcert v1.4.4 fyne.io/systray v1.11.0 github.com/akutz/memconn v0.1.0 diff --git a/go.sum b/go.sum index 753441721..c6fd5a2d8 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ 4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs= 4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc= 4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU= +9fans.net/go v0.0.8-0.20250307142834-96bdba94b63f h1:1C7nZuxUMNz7eiQALRfiqNOm04+m3edWlRff/BYHf0Q= +9fans.net/go v0.0.8-0.20250307142834-96bdba94b63f/go.mod h1:hHyrZRryGqVdqrknjq5OWDLGCTJ2NeEvtrpR96mjraM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= diff --git a/ssh/tailssh/incubator_plan9.go b/ssh/tailssh/incubator_plan9.go index d6a3b233b..ec8b779da 100644 --- a/ssh/tailssh/incubator_plan9.go +++ b/ssh/tailssh/incubator_plan9.go @@ -22,28 +22,18 @@ import ( "strconv" "strings" "sync/atomic" - "syscall" "github.com/pkg/sftp" "tailscale.com/cmd/tailscaled/childproc" "tailscale.com/tailcfg" + "tailscale.com/tempfork/netshell" "tailscale.com/types/logger" ) func init() { childproc.Add("ssh", beIncubator) childproc.Add("sftp", beSFTP) -} - -// maybeStartLoginSession informs the system that we are about to log someone -// in. On success, it may return a non-nil close func which must be closed to -// release the session. -// We can only do this if we are running as root. -// This is best effort to still allow running on machines where -// we don't support starting sessions, e.g. darwin. -// See maybeStartLoginSessionLinux. -var maybeStartLoginSession = func(dlogf logger.Logf, ia incubatorArgs) (close func() error) { - return nil + childproc.Add("plan9-netshell", beNetshell) } // newIncubatorCommand returns a new exec.Cmd configured with @@ -85,7 +75,6 @@ func (ss *sshSession) newIncubatorCommand(logf logger.Logf) (cmd *exec.Cmd, err lu := ss.conn.localUser ci := ss.conn.info - groups := strings.Join(ss.conn.userGroupIDs, ",") remoteUser := ci.uprof.LoginName if ci.node.IsTagged() { remoteUser = strings.Join(ci.node.Tags().AsSlice(), ",") @@ -94,10 +83,9 @@ func (ss *sshSession) newIncubatorCommand(logf logger.Logf) (cmd *exec.Cmd, err incubatorArgs := []string{ "be-child", "ssh", - "--login-shell=" + lu.LoginShell(), - "--uid=" + lu.Uid, - "--gid=" + lu.Gid, - "--groups=" + groups, + // "--login-shell=" + lu.LoginShell(), + // "--uid=" + lu.Uid, + // "--gid=" + lu.Gid, "--local-user=" + lu.Username, "--home-dir=" + lu.HomeDir, "--remote-user=" + remoteUser, @@ -212,6 +200,9 @@ func parseIncubatorArgs(args []string) (incubatorArgs, error) { flags.Parse(args) for _, g := range strings.Split(groups, ",") { + if g == "" { + continue + } gid, err := strconv.Atoi(g) if err != nil { return ia, fmt.Errorf("unable to parse group id %q: %w", g, err) @@ -250,6 +241,11 @@ func (ia incubatorArgs) forwadedEnviron() ([]string, string, error) { return environ, allowListKeys, nil } +func beNetshell(args []string) error { + netshell.Main() + return nil +} + // beIncubator is the entrypoint to the `tailscaled be-child ssh` subcommand. // It is responsible for informing the system of a new login session for the // user. This is sometimes necessary for mounting home directories and @@ -275,6 +271,11 @@ func beIncubator(args []string) error { return fmt.Errorf("--sftp and --shell are mutually exclusive") } + if ia.isShell { + netshell.Main() + return nil + } + dlogf := logger.Discard if ia.debugTest { // In testing, we don't always have syslog, so log to a temp file. @@ -301,11 +302,6 @@ func handleInProcess(dlogf logger.Logf, ia incubatorArgs) error { func handleSFTPInProcess(dlogf logger.Logf, ia incubatorArgs) error { dlogf("handling sftp") - sessionCloser := maybeStartLoginSession(dlogf, ia) - if sessionCloser != nil { - defer sessionCloser() - } - return serveSFTP() } @@ -332,10 +328,6 @@ func serveSFTP() error { // specified values, and then launches the requested `--cmd` in the user's // login shell. func handleSSHInProcess(dlogf logger.Logf, ia incubatorArgs) error { - sessionCloser := maybeStartLoginSession(dlogf, ia) - if sessionCloser != nil { - defer sessionCloser() - } environ, _, err := ia.forwadedEnviron() if err != nil { @@ -344,7 +336,7 @@ func handleSSHInProcess(dlogf logger.Logf, ia incubatorArgs) error { args := shellArgs(ia.isShell, ia.cmd) dlogf("running %s %q", ia.loginShell, args) - cmd := newCommand(ia.hasTTY, ia.loginShell, environ, args) + cmd := newCommand(ia.loginShell, environ, args) err = cmd.Run() if ee, ok := err.(*exec.ExitError); ok { ps := ee.ProcessState @@ -361,23 +353,13 @@ func handleSSHInProcess(dlogf logger.Logf, ia incubatorArgs) error { return err } -func newCommand(hasTTY bool, cmdPath string, cmdEnviron []string, cmdArgs []string) *exec.Cmd { +func newCommand(cmdPath string, cmdEnviron []string, cmdArgs []string) *exec.Cmd { cmd := exec.Command(cmdPath, cmdArgs...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = cmdEnviron - if hasTTY { - // If we were launched with a tty then we should mark that as the ctty - // of the child. However, as the ctty is being passed from the parent - // we set the child to foreground instead which also passes the ctty. - // However, we can not do this if never had a tty to begin with. - cmd.SysProcAttr = &syscall.SysProcAttr{ - // XXX TODO - } - } - return cmd }