Set PDEATHSIG to SIGKILL on child processes after reexec

To execute an SSH command, Teleport re-executes itself and execs the
command from this child process:
  teleport -> teleport exec -> sh -c "user command"

Both parent teleport processes could exit unexpectedly (from SIGKILL or
even connection interruption).

Make sure all child processes get cleaned up and not orphaned to PID 1:
- teleport exec via SIGQUIT to request graceful shutdown
- user command via SIGKILL because it might ignore other signals
This commit is contained in:
Andrew Lytvynov 2020-04-09 13:44:31 -07:00 committed by Andrew Lytvynov
parent c6fe327938
commit 12952b4904
4 changed files with 54 additions and 2 deletions

View file

@ -26,6 +26,7 @@ import (
"os/user"
"path/filepath"
"strconv"
"syscall"
"testing"
"time"
@ -168,6 +169,7 @@ func (s *ExecSuite) TestOSCommandPrep(c *check.C) {
c.Assert(cmd.Args, check.DeepEquals, []string{"-sh"})
c.Assert(cmd.Dir, check.Equals, s.usr.HomeDir)
c.Assert(cmd.Env, check.DeepEquals, expectedEnv)
c.Assert(cmd.SysProcAttr.Pdeathsig, check.Equals, syscall.SIGKILL)
// Non-empty command (exec a prog).
s.ctx.ExecRequest.SetCommand("ls -lh /etc")
@ -180,6 +182,7 @@ func (s *ExecSuite) TestOSCommandPrep(c *check.C) {
c.Assert(cmd.Args, check.DeepEquals, []string{"/bin/sh", "-c", "ls -lh /etc"})
c.Assert(cmd.Dir, check.Equals, s.usr.HomeDir)
c.Assert(cmd.Env, check.DeepEquals, expectedEnv)
c.Assert(cmd.SysProcAttr.Pdeathsig, check.Equals, syscall.SIGKILL)
// Command without args.
s.ctx.ExecRequest.SetCommand("top")
@ -189,6 +192,7 @@ func (s *ExecSuite) TestOSCommandPrep(c *check.C) {
c.Assert(err, check.IsNil)
c.Assert(cmd.Path, check.Equals, "/bin/sh")
c.Assert(cmd.Args, check.DeepEquals, []string{"/bin/sh", "-c", "top"})
c.Assert(cmd.SysProcAttr.Pdeathsig, check.Equals, syscall.SIGKILL)
}
func (s *ExecSuite) TestLoginDefsParser(c *check.C) {

View file

@ -461,6 +461,9 @@ func buildCommand(c *execCommand, tty *os.File, pty *os.File, pamEnvironment []s
uid, gid, groups)
}
// Perform OS-specific tweaks to the command.
userCommandOSTweaks(&cmd)
return &cmd, nil
}
@ -510,7 +513,7 @@ func ConfigureCommand(ctx *ServerContext) (*exec.Cmd, error) {
args := []string{executable, subCommand}
// Build the "teleport exec" command.
return &exec.Cmd{
cmd := &exec.Cmd{
Path: executable,
Args: args,
Dir: executableDir,
@ -518,5 +521,10 @@ func ConfigureCommand(ctx *ServerContext) (*exec.Cmd, error) {
ctx.cmdr,
ctx.contr,
},
}, nil
}
// Perform OS-specific tweaks to the command.
reexecCommandOSTweaks(cmd)
return cmd, nil
}

29
lib/srv/reexec_linux.go Normal file
View file

@ -0,0 +1,29 @@
// +build linux
package srv
import (
"os/exec"
"syscall"
)
func reexecCommandOSTweaks(cmd *exec.Cmd) {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = new(syscall.SysProcAttr)
}
// Linux only: when parent process (node) dies unexpectedly without
// cleaning up child processes, send a signal for graceful shutdown
// to children.
cmd.SysProcAttr.Pdeathsig = syscall.SIGQUIT
}
func userCommandOSTweaks(cmd *exec.Cmd) {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = new(syscall.SysProcAttr)
}
// Linux only: when parent process (this process) dies unexpectedly, kill
// the child process instead of orphaning it.
// SIGKILL because we don't control the child process and it could choose
// to ignore other signals.
cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
}

11
lib/srv/reexec_other.go Normal file
View file

@ -0,0 +1,11 @@
// +build !linux
package srv
import (
"os/exec"
)
func reexecCommandOSTweaks(cmd *exec.Cmd) {}
func userCommandOSTweaks(cmd *exec.Cmd) {}