ssh/tailssh: get login shell when running as non-root

And also reject attempts to use other users.

Updates #3802

Change-Id: Iddc85f6ea2dba17d12be66a50408d24c1f92833e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/3985/head
Brad Fitzpatrick 3 years ago committed by Brad Fitzpatrick
parent e1e20f6d39
commit 03caa95bf2

@ -16,6 +16,9 @@ import (
"net" "net"
"os" "os"
"os/exec" "os/exec"
"os/user"
"runtime"
"strings"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
@ -100,9 +103,9 @@ func (srv *server) handleSSH(s ssh.Session) {
lb := srv.lb lb := srv.lb
logf := srv.logf logf := srv.logf
user := s.User() sshUser := s.User()
addr := s.RemoteAddr() addr := s.RemoteAddr()
logf("Handling SSH from %v for user %v", addr, user) logf("Handling SSH from %v for user %v", addr, sshUser)
ta, ok := addr.(*net.TCPAddr) ta, ok := addr.(*net.TCPAddr)
if !ok { if !ok {
logf("tsshd: rejecting non-TCP addr %T %v", addr, addr) logf("tsshd: rejecting non-TCP addr %T %v", addr, addr)
@ -140,7 +143,7 @@ func (srv *server) handleSSH(s ssh.Session) {
srcIP := srcIPP.IP() srcIP := srcIPP.IP()
sctx := &sshContext{ sctx := &sshContext{
now: time.Now(), now: time.Now(),
sshUser: s.User(), sshUser: sshUser,
srcIP: srcIP, srcIP: srcIP,
node: node, node: node,
uprof: &uprof, uprof: &uprof,
@ -165,8 +168,19 @@ func (srv *server) handleSSH(s ssh.Session) {
return return
} }
var cmd *exec.Cmd var cmd *exec.Cmd
if os.Getuid() != 0 || localUser == "root" { if os.Getuid() != 0 {
cmd = exec.Command("/bin/bash") u, err := user.Current()
if err != nil {
logf("failed to get current user: %v", err)
s.Exit(1)
return
}
if u.Username != localUser {
fmt.Fprintf(s, "can't switch user\n")
s.Exit(1)
return
}
cmd = exec.Command(loginShell(u.Uid))
} else { } else {
cmd = exec.Command("/usr/bin/env", "su", "-", localUser) cmd = exec.Command("/usr/bin/env", "su", "-", localUser)
} }
@ -297,3 +311,19 @@ func matchesPrincipal(ps []*tailcfg.SSHPrincipal, sctx *sshContext) bool {
} }
return false return false
} }
func loginShell(uid string) string {
switch runtime.GOOS {
case "linux":
out, _ := exec.Command("getent", "passwd", uid).Output()
// out is "root:x:0:0:root:/root:/bin/bash"
f := strings.SplitN(string(out), ":", 10)
if len(f) > 6 {
return f[6] // shell
}
}
if e := os.Getenv("SHELL"); e != "" {
return e
}
return "/bin/bash"
}

Loading…
Cancel
Save