diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index a3073e1ee..e6b3609fb 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -3,6 +3,7 @@ package executor import ( "context" "fmt" + "io" "io/ioutil" "log" "net" @@ -16,6 +17,7 @@ import ( "syscall" "time" + syslog "github.com/RackSec/srslog" "github.com/armon/circbuf" "github.com/hashicorp/go-multierror" "github.com/mitchellh/go-ps" @@ -774,3 +776,40 @@ func (e *UniversalExecutor) Signal(s os.Signal) error { return nil } + +func (e *UniversalExecutor) LaunchSyslogServer() (*SyslogServerState, error) { + // Ensure the context has been set first + if e.ctx == nil { + return nil, fmt.Errorf("SetContext must be called before launching the Syslog Server") + } + + e.syslogChan = make(chan *logging.SyslogMessage, 2048) + l, err := e.getListener(e.ctx.PortLowerBound, e.ctx.PortUpperBound) + if err != nil { + return nil, err + } + e.logger.Printf("[DEBUG] syslog-server: launching syslog server on addr: %v", l.Addr().String()) + if err := e.configureLoggers(); err != nil { + return nil, err + } + + e.syslogServer = logging.NewSyslogServer(l, e.syslogChan, e.logger) + go e.syslogServer.Start() + go e.collectLogs(e.lre, e.lro) + syslogAddr := fmt.Sprintf("%s://%s", l.Addr().Network(), l.Addr().String()) + return &SyslogServerState{Addr: syslogAddr}, nil +} + +func (e *UniversalExecutor) collectLogs(we io.Writer, wo io.Writer) { + for logParts := range e.syslogChan { + // If the severity of the log line is err then we write to stderr + // otherwise all messages go to stdout + if logParts.Severity == syslog.LOG_ERR { + e.lre.Write(logParts.Message) + e.lre.Write([]byte{'\n'}) + } else { + e.lro.Write(logParts.Message) + e.lro.Write([]byte{'\n'}) + } + } +} diff --git a/client/driver/executor/executor_basic.go b/client/driver/executor/executor_basic.go index 123ed4703..cdf262d5d 100644 --- a/client/driver/executor/executor_basic.go +++ b/client/driver/executor/executor_basic.go @@ -1,4 +1,4 @@ -// +build darwin dragonfly freebsd netbsd openbsd solaris windows +// +build !linux package executor @@ -17,10 +17,6 @@ func (e *UniversalExecutor) removeChrootMounts() error { return nil } -func (e *UniversalExecutor) runAs(userid string) error { - return nil -} - func (e *UniversalExecutor) applyLimits(pid int) error { return nil } diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index 37bcb3bfc..be0ce78be 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -3,9 +3,7 @@ package executor import ( "fmt" "os" - "os/user" "path/filepath" - "strconv" "strings" "syscall" "time" @@ -166,56 +164,6 @@ func (e *UniversalExecutor) Stats() (*cstructs.TaskResourceUsage, error) { return &taskResUsage, nil } -// runAs takes a user id as a string and looks up the user, and sets the command -// to execute as that user. -func (e *UniversalExecutor) runAs(userid string) error { - u, err := user.Lookup(userid) - if err != nil { - return fmt.Errorf("Failed to identify user %v: %v", userid, err) - } - - // Get the groups the user is a part of - gidStrings, err := u.GroupIds() - if err != nil { - return fmt.Errorf("Unable to lookup user's group membership: %v", err) - } - - gids := make([]uint32, len(gidStrings)) - for _, gidString := range gidStrings { - u, err := strconv.Atoi(gidString) - if err != nil { - return fmt.Errorf("Unable to convert user's group to int %s: %v", gidString, err) - } - - gids = append(gids, uint32(u)) - } - - // Convert the uid and gid - uid, err := strconv.ParseUint(u.Uid, 10, 32) - if err != nil { - return fmt.Errorf("Unable to convert userid to uint32: %s", err) - } - gid, err := strconv.ParseUint(u.Gid, 10, 32) - if err != nil { - return fmt.Errorf("Unable to convert groupid to uint32: %s", err) - } - - // Set the command to run as that user and group. - if e.cmd.SysProcAttr == nil { - e.cmd.SysProcAttr = &syscall.SysProcAttr{} - } - if e.cmd.SysProcAttr.Credential == nil { - e.cmd.SysProcAttr.Credential = &syscall.Credential{} - } - e.cmd.SysProcAttr.Credential.Uid = uint32(uid) - e.cmd.SysProcAttr.Credential.Gid = uint32(gid) - e.cmd.SysProcAttr.Credential.Groups = gids - - e.logger.Printf("[DEBUG] executor: running as user:group %d:%d with group membership in %v", uid, gid, gids) - - return nil -} - // configureChroot configures a chroot func (e *UniversalExecutor) configureChroot() error { if e.cmd.SysProcAttr == nil { diff --git a/client/driver/executor/executor_unix.go b/client/driver/executor/executor_unix.go index 90efa32e6..40efc350f 100644 --- a/client/driver/executor/executor_unix.go +++ b/client/driver/executor/executor_unix.go @@ -1,49 +1,60 @@ -// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows +// +build !windows package executor import ( "fmt" - "io" - - syslog "github.com/RackSec/srslog" - - "github.com/hashicorp/nomad/client/driver/logging" + "os/user" + "strconv" + "syscall" ) -func (e *UniversalExecutor) LaunchSyslogServer() (*SyslogServerState, error) { - // Ensure the context has been set first - if e.ctx == nil { - return nil, fmt.Errorf("SetContext must be called before launching the Syslog Server") - } - - e.syslogChan = make(chan *logging.SyslogMessage, 2048) - l, err := e.getListener(e.ctx.PortLowerBound, e.ctx.PortUpperBound) +// runAs takes a user id as a string and looks up the user, and sets the command +// to execute as that user. +func (e *UniversalExecutor) runAs(userid string) error { + u, err := user.Lookup(userid) if err != nil { - return nil, err - } - e.logger.Printf("[DEBUG] syslog-server: launching syslog server on addr: %v", l.Addr().String()) - if err := e.configureLoggers(); err != nil { - return nil, err + return fmt.Errorf("Failed to identify user %v: %v", userid, err) } - e.syslogServer = logging.NewSyslogServer(l, e.syslogChan, e.logger) - go e.syslogServer.Start() - go e.collectLogs(e.lre, e.lro) - syslogAddr := fmt.Sprintf("%s://%s", l.Addr().Network(), l.Addr().String()) - return &SyslogServerState{Addr: syslogAddr}, nil -} + // Get the groups the user is a part of + gidStrings, err := u.GroupIds() + if err != nil { + return fmt.Errorf("Unable to lookup user's group membership: %v", err) + } -func (e *UniversalExecutor) collectLogs(we io.Writer, wo io.Writer) { - for logParts := range e.syslogChan { - // If the severity of the log line is err then we write to stderr - // otherwise all messages go to stdout - if logParts.Severity == syslog.LOG_ERR { - e.lre.Write(logParts.Message) - e.lre.Write([]byte{'\n'}) - } else { - e.lro.Write(logParts.Message) - e.lro.Write([]byte{'\n'}) + gids := make([]uint32, len(gidStrings)) + for _, gidString := range gidStrings { + u, err := strconv.Atoi(gidString) + if err != nil { + return fmt.Errorf("Unable to convert user's group to int %s: %v", gidString, err) } + + gids = append(gids, uint32(u)) } + + // Convert the uid and gid + uid, err := strconv.ParseUint(u.Uid, 10, 32) + if err != nil { + return fmt.Errorf("Unable to convert userid to uint32: %s", err) + } + gid, err := strconv.ParseUint(u.Gid, 10, 32) + if err != nil { + return fmt.Errorf("Unable to convert groupid to uint32: %s", err) + } + + // Set the command to run as that user and group. + if e.cmd.SysProcAttr == nil { + e.cmd.SysProcAttr = &syscall.SysProcAttr{} + } + if e.cmd.SysProcAttr.Credential == nil { + e.cmd.SysProcAttr.Credential = &syscall.Credential{} + } + e.cmd.SysProcAttr.Credential.Uid = uint32(uid) + e.cmd.SysProcAttr.Credential.Gid = uint32(gid) + e.cmd.SysProcAttr.Credential.Groups = gids + + e.logger.Printf("[DEBUG] executor: running as user:group %d:%d with group membership in %v", uid, gid, gids) + + return nil } diff --git a/client/driver/raw_exec_test.go b/client/driver/raw_exec_test.go index d8d98fb0b..0060653d2 100644 --- a/client/driver/raw_exec_test.go +++ b/client/driver/raw_exec_test.go @@ -7,8 +7,6 @@ import ( "io/ioutil" "path/filepath" "reflect" - "runtime" - "strings" "testing" "time" @@ -260,45 +258,6 @@ func TestRawExecDriver_Start_Kill_Wait(t *testing.T) { } } -func TestRawExecDriverUser(t *testing.T) { - t.Parallel() - if runtime.GOOS != "linux" { - t.Skip("Linux only test") - } - task := &structs.Task{ - Name: "sleep", - Driver: "raw_exec", - User: "alice", - Config: map[string]interface{}{ - "command": testtask.Path(), - "args": []string{"sleep", "45s"}, - }, - LogConfig: &structs.LogConfig{ - MaxFiles: 10, - MaxFileSizeMB: 10, - }, - Resources: basicResources, - } - testtask.SetTaskEnv(task) - - ctx := testDriverContexts(t, task) - defer ctx.AllocDir.Destroy() - d := NewRawExecDriver(ctx.DriverCtx) - - if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { - t.Fatalf("prestart err: %v", err) - } - resp, err := d.Start(ctx.ExecCtx, task) - if err == nil { - resp.Handle.Kill() - t.Fatalf("Should've failed") - } - msg := "unknown user alice" - if !strings.Contains(err.Error(), msg) { - t.Fatalf("Expecting '%v' in '%v'", msg, err) - } -} - func TestRawExecDriver_HandlerExec(t *testing.T) { t.Parallel() task := &structs.Task{ diff --git a/client/driver/raw_exec_unix_test.go b/client/driver/raw_exec_unix_test.go index 770559d3c..f92695698 100644 --- a/client/driver/raw_exec_unix_test.go +++ b/client/driver/raw_exec_unix_test.go @@ -10,10 +10,47 @@ import ( "testing" "time" + "github.com/hashicorp/nomad/helper/testtask" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/testutil" ) +func TestRawExecDriver_User(t *testing.T) { + t.Parallel() + task := &structs.Task{ + Name: "sleep", + Driver: "raw_exec", + User: "alice", + Config: map[string]interface{}{ + "command": testtask.Path(), + "args": []string{"sleep", "45s"}, + }, + LogConfig: &structs.LogConfig{ + MaxFiles: 10, + MaxFileSizeMB: 10, + }, + Resources: basicResources, + } + testtask.SetTaskEnv(task) + + ctx := testDriverContexts(t, task) + defer ctx.AllocDir.Destroy() + d := NewRawExecDriver(ctx.DriverCtx) + + if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { + t.Fatalf("prestart err: %v", err) + } + resp, err := d.Start(ctx.ExecCtx, task) + if err == nil { + resp.Handle.Kill() + t.Fatalf("Should've failed") + } + msg := "unknown user alice" + if !strings.Contains(err.Error(), msg) { + t.Fatalf("Expecting '%v' in '%v'", msg, err) + } +} + func TestRawExecDriver_Signal(t *testing.T) { t.Parallel() task := &structs.Task{