@ -210,8 +210,6 @@ type incubatorArgs struct {
debugTest bool
debugTest bool
isSELinuxEnforcing bool
isSELinuxEnforcing bool
encodedEnv string
encodedEnv string
allowListEnvKeys string
forwardedEnviron [ ] string
}
}
func parseIncubatorArgs ( args [ ] string ) ( incubatorArgs , error ) {
func parseIncubatorArgs ( args [ ] string ) ( incubatorArgs , error ) {
@ -246,31 +244,35 @@ func parseIncubatorArgs(args []string) (incubatorArgs, error) {
ia . gids = append ( ia . gids , gid )
ia . gids = append ( ia . gids , gid )
}
}
ia . forwardedEnviron = os . Environ ( )
return ia , nil
}
func ( ia incubatorArgs ) forwadedEnviron ( ) ( [ ] string , string , error ) {
environ := os . Environ ( )
// pass through SSH_AUTH_SOCK environment variable to support ssh agent forwarding
// pass through SSH_AUTH_SOCK environment variable to support ssh agent forwarding
ia . allowListEnvKeys = "SSH_AUTH_SOCK"
allowListKeys : = "SSH_AUTH_SOCK"
if ia . encodedEnv != "" {
if ia . encodedEnv != "" {
unquoted , err := strconv . Unquote ( ia . encodedEnv )
unquoted , err := strconv . Unquote ( ia . encodedEnv )
if err != nil {
if err != nil {
return ia , fmt . Errorf ( "unable to parse encodedEnv %q: %w" , ia . encodedEnv , err )
return nil , "" , fmt . Errorf ( "unable to parse encodedEnv %q: %w" , ia . encodedEnv , err )
}
}
var extraEnviron [ ] string
var extraEnviron [ ] string
err = json . Unmarshal ( [ ] byte ( unquoted ) , & extraEnviron )
err = json . Unmarshal ( [ ] byte ( unquoted ) , & extraEnviron )
if err != nil {
if err != nil {
return ia , fmt . Errorf ( "unable to parse encodedEnv %q: %w" , ia . encodedEnv , err )
return nil , "" , fmt . Errorf ( "unable to parse encodedEnv %q: %w" , ia . encodedEnv , err )
}
}
ia. forward edE nviron = append ( ia. forward edE nviron, extraEnviron ... )
environ = append ( environ, extraEnviron ... )
for _ , v := range extraEnviron {
for _ , v := range extraEnviron {
ia. allowListEnv Keys = fmt . Sprintf ( "%s,%s" , ia. allowListEnv Keys, strings . Split ( v , "=" ) [ 0 ] )
allowListKeys = fmt . Sprintf ( "%s,%s" , allowListKeys, strings . Split ( v , "=" ) [ 0 ] )
}
}
}
}
return ia, nil
return env iron, allowListKeys , nil
}
}
// beIncubator is the entrypoint to the `tailscaled be-child ssh` subcommand.
// beIncubator is the entrypoint to the `tailscaled be-child ssh` subcommand.
@ -450,8 +452,13 @@ func tryExecLogin(dlogf logger.Logf, ia incubatorArgs) error {
loginArgs := ia . loginArgs ( loginCmdPath )
loginArgs := ia . loginArgs ( loginCmdPath )
dlogf ( "logging in with %+v" , loginArgs )
dlogf ( "logging in with %+v" , loginArgs )
environ , _ , err := ia . forwadedEnviron ( )
if err != nil {
return err
}
// If Exec works, the Go code will not proceed past this:
// If Exec works, the Go code will not proceed past this:
err = unix . Exec ( loginCmdPath , loginArgs , ia . forwardedEnviron )
err = unix . Exec ( loginCmdPath , loginArgs , environ)
// If we made it here, Exec failed.
// If we made it here, Exec failed.
return err
return err
@ -484,9 +491,14 @@ func trySU(dlogf logger.Logf, ia incubatorArgs) (handled bool, err error) {
defer sessionCloser ( )
defer sessionCloser ( )
}
}
environ , allowListEnvKeys , err := ia . forwadedEnviron ( )
if err != nil {
return false , err
}
loginArgs := [ ] string {
loginArgs := [ ] string {
su ,
su ,
"-w" , ia . allowListEnvKeys ,
"-w" , allowListEnvKeys,
"-l" ,
"-l" ,
ia . localUser ,
ia . localUser ,
}
}
@ -498,7 +510,7 @@ func trySU(dlogf logger.Logf, ia incubatorArgs) (handled bool, err error) {
dlogf ( "logging in with %+v" , loginArgs )
dlogf ( "logging in with %+v" , loginArgs )
// If Exec works, the Go code will not proceed past this:
// If Exec works, the Go code will not proceed past this:
err = unix . Exec ( su , loginArgs , ia. forward edE nviron)
err = unix . Exec ( su , loginArgs , environ)
// If we made it here, Exec failed.
// If we made it here, Exec failed.
return true , err
return true , err
@ -527,11 +539,16 @@ func findSU(dlogf logger.Logf, ia incubatorArgs) string {
return ""
return ""
}
}
_ , allowListEnvKeys , err := ia . forwadedEnviron ( )
if err != nil {
return ""
}
// First try to execute su -w <allow listed env> -l <user> -c true
// First try to execute su -w <allow listed env> -l <user> -c true
// to make sure su supports the necessary arguments.
// to make sure su supports the necessary arguments.
err = exec . Command (
err = exec . Command (
su ,
su ,
"-w" , ia . allowListEnvKeys ,
"-w" , allowListEnvKeys,
"-l" ,
"-l" ,
ia . localUser ,
ia . localUser ,
"-c" , "true" ,
"-c" , "true" ,
@ -558,10 +575,15 @@ func handleSSHInProcess(dlogf logger.Logf, ia incubatorArgs) error {
return err
return err
}
}
environ , _ , err := ia . forwadedEnviron ( )
if err != nil {
return err
}
args := shellArgs ( ia . isShell , ia . cmd )
args := shellArgs ( ia . isShell , ia . cmd )
dlogf ( "running %s %q" , ia . loginShell , args )
dlogf ( "running %s %q" , ia . loginShell , args )
cmd := newCommand ( ia . hasTTY , ia . loginShell , ia . forwardedEnviron , args )
cmd := newCommand ( ia . hasTTY , ia . loginShell , environ, args )
err := cmd . Run ( )
err = cmd . Run ( )
if ee , ok := err . ( * exec . ExitError ) ; ok {
if ee , ok := err . ( * exec . ExitError ) ; ok {
ps := ee . ProcessState
ps := ee . ProcessState
code := ps . ExitCode ( )
code := ps . ExitCode ( )