diff --git a/Changes b/Changes index 349acedd..5588dd0a 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,8 @@ This file documents the revision history for the SNClient+ agent. +next: + - drop privileges when running node exporter + 0.12 Tue Nov 7 23:52:33 CET 2023 - add built-in check_http / check_tcp / check_dns - add check_omd check diff --git a/packaging/snclient.ini b/packaging/snclient.ini index 90406735..aea4a6fe 100644 --- a/packaging/snclient.ini +++ b/packaging/snclient.ini @@ -121,6 +121,9 @@ default module = ; agent max memory - set a memory limit for the agent (agent will be restarted if the rss is higher, set to 0 to disabled) ;agent max memory = 256M +; agent user - set user this agent should run as (requires root permissions) +;agent user = nobody + ; port - Port to use for the node exporter. ;port = ${/settings/WEB/server/port} @@ -161,6 +164,9 @@ agent address = 127.0.0.1:9990 ; agent max memory - set a memory limit for the agent (agent will be restarted if the rss is higher, set to 0 to disabled) agent max memory = 256M +; agent user - set user this agent should run as (requires root permissions) +agent user = nobody + ; port - Port to use for the node exporter. port = ${/settings/WEB/server/port} diff --git a/pkg/snclient/listen_managedexporter.go b/pkg/snclient/listen_managedexporter.go index 2c7c4631..85b131c3 100644 --- a/pkg/snclient/listen_managedexporter.go +++ b/pkg/snclient/listen_managedexporter.go @@ -27,6 +27,7 @@ type HandlerManagedExporter struct { agentAddress string agentMaxMem uint64 agentExtraArgs string + agentUser string cmd *exec.Cmd pid int snc *Agent @@ -127,6 +128,10 @@ func (l *HandlerManagedExporter) Init(snc *Agent, conf *ConfigSection, _ *Config l.agentAddress = agentAddress } + if agentUser, ok := conf.GetString("agent user"); ok { + l.agentUser = agentUser + } + uri, err := url.Parse("http://" + l.agentAddress + "/metrics") if err != nil { return fmt.Errorf("cannot parse agent url: %s", err.Error()) @@ -163,6 +168,16 @@ func (l *HandlerManagedExporter) procMainLoop() { args = append(args, extra) } cmd := exec.Command(l.agentPath, args...) //nolint:gosec // input source is the config file + + if l.agentUser != "" { + if err := setCmdUser(cmd, l.agentUser); err != nil { + err = fmt.Errorf("failed to drop privileges for %s agent: %s", l.Type(), err.Error()) + log.Errorf("agent startup error: %s", err) + + return + } + } + log.Debugf("starting %s agent: %s", l.Type(), cmd.Path) l.snc.passthroughLogs("stdout", "["+l.Type()+"] ", log.Debugf, cmd.StdoutPipe) l.snc.passthroughLogs("stderr", "["+l.Type()+"] ", l.logPass, cmd.StderrPipe) diff --git a/pkg/snclient/listen_nodeexporter.go b/pkg/snclient/listen_nodeexporter.go index 4f88bcb6..6970cbaf 100644 --- a/pkg/snclient/listen_nodeexporter.go +++ b/pkg/snclient/listen_nodeexporter.go @@ -9,6 +9,7 @@ func NewHandlerNodeExporter() Module { name: "nodeexporter", urlPrefix: "/node", agentExtraArgs: "--web.listen-address=${agent address}", + agentUser: "nobody", } return mod diff --git a/pkg/snclient/snclient_unix.go b/pkg/snclient/snclient_unix.go index eedf5704..9dd3962e 100644 --- a/pkg/snclient/snclient_unix.go +++ b/pkg/snclient/snclient_unix.go @@ -4,15 +4,18 @@ package snclient import ( "context" + "fmt" "os" "os/exec" "os/signal" + "os/user" "runtime" "runtime/pprof" "strings" "syscall" "time" + "pkg/convert" "pkg/utils" ) @@ -120,3 +123,28 @@ func makeCmd(ctx context.Context, command string) (*exec.Cmd, error) { return cmd, nil } + +func setCmdUser(cmd *exec.Cmd, username string) error { + usr, err := user.Lookup(username) + if err != nil { + return fmt.Errorf("user.lookup: %s: %s", username, err.Error()) + } + + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + + uid, err := convert.Float64E(usr.Uid) + if err != nil { + return fmt.Errorf("cannot convert uid to number for user %s (uid:%s): %s", username, usr.Uid, err.Error()) + } + + gid, err := convert.Float64E(usr.Gid) + if err != nil { + return fmt.Errorf("cannot convert gid to number for user %s (gid:%s): %s", username, usr.Gid, err.Error()) + } + + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + + return nil +} diff --git a/pkg/snclient/snclient_windows.go b/pkg/snclient/snclient_windows.go index 58274fc3..febd295a 100644 --- a/pkg/snclient/snclient_windows.go +++ b/pkg/snclient/snclient_windows.go @@ -325,3 +325,7 @@ func QuotePathWithSpaces(path string) string { return strings.Join(quotedComponents, `/`) } + +func setCmdUser(_ *exec.Cmd, _ string) error { + return fmt.Errorf("droping privileges is not supported on windows") +}