ssh/tailssh: add envknob for default PATH

As backup plan, just in case the earlier fix's logic wasn't correct
and we want to experiment in the field or have users have a quicker
fix.

Updates #5285

Change-Id: I7447466374d11f8f609de6dfbc4d9a944770826d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2022-12-15 14:28:14 -08:00
committed by Brad Fitzpatrick
parent fc0fe99edf
commit 651e0d8aad
2 changed files with 49 additions and 9 deletions

View File

@@ -35,6 +35,7 @@ import (
gossh "golang.org/x/crypto/ssh"
"golang.org/x/sys/unix"
"tailscale.com/cmd/tailscaled/childproc"
"tailscale.com/envknob"
"tailscale.com/hostinfo"
"tailscale.com/tempfork/gliderlabs/ssh"
"tailscale.com/types/logger"
@@ -583,7 +584,21 @@ func envForUser(u *user.User) []string {
}
}
// defaultPathTmpl specifies the default PATH template to use for new sessions.
//
// If empty, a default value is used based on the OS & distro to match OpenSSH's
// usually-hardcoded behavior. (see
// https://github.com/tailscale/tailscale/issues/5285 for background).
//
// The template may contain @{HOME} or @{PAM_USER} which expand to the user's
// home directory and username, respectively. (PAM is not used, despite the
// name)
var defaultPathTmpl = envknob.RegisterString("TAILSCALE_SSH_DEFAULT_PATH")
func defaultPathForUser(u *user.User) string {
if s := defaultPathTmpl(); s != "" {
return expandDefaultPathTmpl(s, u)
}
isRoot := u.Uid == "0"
switch distro.Get() {
case distro.Debian:
@@ -626,19 +641,24 @@ func pathFromPAMEnvLine(line []byte, u *user.User) (path string) {
rest := strings.TrimSpace(strings.TrimPrefix(string(line), "PATH"))
if quoted, ok := strs.CutPrefix(rest, "DEFAULT="); ok {
if path, err := strconv.Unquote(quoted); err == nil {
path = strings.NewReplacer(
"@{HOME}", u.HomeDir,
"@{PAM_USER}", u.Username,
).Replace(path)
if !strings.Contains(path, "@{") {
// If no more expansions, use it. Otherwise we fail closed.
return path
}
return expandDefaultPathTmpl(path, u)
}
}
return ""
}
func expandDefaultPathTmpl(t string, u *user.User) string {
p := strings.NewReplacer(
"@{HOME}", u.HomeDir,
"@{PAM_USER}", u.Username,
).Replace(t)
if strings.Contains(p, "@{") {
// If there are unknown expansions, conservatively fail closed.
return ""
}
return p
}
// updateStringInSlice mutates ss to change the first occurrence of a
// to b.
func updateStringInSlice(ss []string, a, b string) {