From 8510eedaf217a5da9c7d033042787b5b60a0f443 Mon Sep 17 00:00:00 2001 From: Christian Zunker Date: Tue, 26 Sep 2023 08:59:14 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20handling=20of=20socketMap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1434 Signed-off-by: Christian Zunker --- providers/os/resources/port.go | 64 +---------------- providers/os/resources/port_test.go | 42 ----------- providers/os/resources/processes.go | 21 +++++- providers/os/resources/processes/dockertop.go | 5 ++ providers/os/resources/processes/linuxproc.go | 5 ++ providers/os/resources/processes/manager.go | 2 + .../os/resources/processes/ps1getprocess.go | 5 ++ .../testdata/find_nginx_container.txt | 0 providers/os/resources/processes/unixps.go | 72 +++++++++++++++++++ .../os/resources/processes/unixps_test.go | 45 ++++++++++++ 10 files changed, 154 insertions(+), 107 deletions(-) rename providers/os/resources/{ports => processes}/testdata/find_nginx_container.txt (100%) diff --git a/providers/os/resources/port.go b/providers/os/resources/port.go index 2088e45f9c..3668b57975 100644 --- a/providers/os/resources/port.go +++ b/providers/os/resources/port.go @@ -82,20 +82,6 @@ var reLinuxProcNet = regexp.MustCompile( "", // lots of other stuff if we want it... ) -// "lrwx------ 1 0 0 64 Dec 6 13:56 /proc/1/fd/12 -> socket:[37364]" -var reFindSockets = regexp.MustCompile( - "^[lrwx-]+\\s+" + - "\\d+\\s+" + - "\\d+\\s+" + // uid - "\\d+\\s+" + // gid - "\\d+\\s+" + - "[^ ]+\\s+" + // month, e.g. Dec - "\\d+\\s+" + // day - "\\d+:\\d+\\s+" + // time - "/proc/(\\d+)/fd/\\d+\\s+" + // path - "->\\s+" + - ".*socket:\\[(\\d+)\\].*\\s*") // target - var TCP_STATES = map[int64]string{ 1: "established", 2: "syn sent", @@ -229,55 +215,7 @@ func (p *mqlPorts) processesBySocket() (map[int64]*mqlProcess, error) { return nil, err } - conn := p.MqlRuntime.Connection.(shared.Connection) - res := map[int64]*mqlProcess{} - if len(res) == 0 { - c, err := conn.RunCommand("find /proc -maxdepth 4 -path '/proc/*/fd/*' -exec ls -n {} \\;") - if err != nil { - return nil, fmt.Errorf("processes> could not run command: %v", err) - } - - processesBySocket := map[int64]*mqlProcess{} - scanner := bufio.NewScanner(c.Stdout) - for scanner.Scan() { - line := scanner.Text() - pid, inode, err := parseLinuxFindLine(line) - if err != nil || (pid == 0 && inode == 0) { - continue - } - - processesBySocket[inode] = processes.ByPID[pid] - } - processes.BySocketID = processesBySocket - res = processesBySocket - } - - return res, err -} - -func parseLinuxFindLine(line string) (int64, int64, error) { - if strings.HasSuffix(line, "Permission denied") || strings.HasSuffix(line, "No such file or directory") { - return 0, 0, nil - } - - m := reFindSockets.FindStringSubmatch(line) - if len(m) == 0 { - return 0, 0, nil - } - - pid, err := strconv.ParseInt(m[1], 10, 64) - if err != nil { - log.Error().Err(err).Msg("cannot parse unix pid " + m[1]) - return 0, 0, err - } - - inode, err := strconv.ParseInt(m[2], 10, 64) - if err != nil { - log.Error().Err(err).Msg("cannot parse socket inode " + m[2]) - return 0, 0, err - } - - return pid, inode, nil + return processes.BySocketID, err } // See: diff --git a/providers/os/resources/port_test.go b/providers/os/resources/port_test.go index aa1160c168..b33fa0a582 100644 --- a/providers/os/resources/port_test.go +++ b/providers/os/resources/port_test.go @@ -85,45 +85,3 @@ func TestParseLinuxProcNetIPv6(t *testing.T) { assert.Equal(t, int64(0), port.RemotePort) assert.Equal(t, "[::]", port.RemoteAddress) } - -func TestParseLinuxFind(t *testing.T) { - fi, err := os.Open("./ports/testdata/find_nginx_container.txt") - require.NoError(t, err) - defer fi.Close() - - scanner := bufio.NewScanner(fi) - scanner.Scan() - line := scanner.Text() - pid, inode, err := parseLinuxFindLine(line) - require.NoError(t, err) - require.Equal(t, int64(0), pid) - require.Equal(t, int64(0), inode) - - scanner.Scan() - line = scanner.Text() - pid, inode, err = parseLinuxFindLine(line) - require.NoError(t, err) - require.Equal(t, int64(0), pid) - require.Equal(t, int64(0), inode) - - scanner.Scan() - line = scanner.Text() - pid, inode, err = parseLinuxFindLine(line) - require.NoError(t, err) - require.Equal(t, int64(1), pid) - require.Equal(t, int64(41866685), inode) - - scanner.Scan() - line = scanner.Text() - pid, inode, err = parseLinuxFindLine(line) - require.NoError(t, err) - require.Equal(t, int64(0), pid) - require.Equal(t, int64(0), inode) - - scanner.Scan() - line = scanner.Text() - pid, inode, err = parseLinuxFindLine(line) - require.NoError(t, err) - require.Equal(t, int64(0), pid) - require.Equal(t, int64(0), inode) -} diff --git a/providers/os/resources/processes.go b/providers/os/resources/processes.go index 2d39815c75..71eacb63b9 100644 --- a/providers/os/resources/processes.go +++ b/providers/os/resources/processes.go @@ -134,6 +134,12 @@ func (p *mqlProcesses) list() ([]interface{}, error) { } log.Debug().Int("processes", len(processes)).Msg("mql[processes]> running processes") + processesInodesByPid, err := opm.ListSocketInodesByProcess() + if err != nil { + log.Warn().Err(err).Msg("mql[processes]> could not retrieve processes socket inodes") + return nil, fmt.Errorf("could not retrieve processes socket inodes") + } + procs := make([]interface{}, len(processes)) for i := range processes { @@ -149,10 +155,21 @@ func (p *mqlProcesses) list() ([]interface{}, error) { return nil, err } + socketInodes := []int64{} + var socketInodesErr error + if _, ok := processesInodesByPid[proc.Pid]; ok { + socketInodes = processesInodesByPid[proc.Pid].Data + socketInodesErr = processesInodesByPid[proc.Pid].Error + } else { + if len(proc.SocketInodes) > 0 { + socketInodes = proc.SocketInodes + socketInodesErr = proc.SocketInodesError + } + } process := o.(*mqlProcess) process.SocketInodes = plugin.TValue[[]int64]{ - Data: proc.SocketInodes, - Error: proc.SocketInodesError, + Data: socketInodes, + Error: socketInodesErr, State: plugin.StateIsSet, } diff --git a/providers/os/resources/processes/dockertop.go b/providers/os/resources/processes/dockertop.go index 9259d6edd0..91eaed8388 100644 --- a/providers/os/resources/processes/dockertop.go +++ b/providers/os/resources/processes/dockertop.go @@ -8,6 +8,7 @@ import ( "fmt" "strconv" + "go.mondoo.com/cnquery/providers-sdk/v1/plugin" "go.mondoo.com/cnquery/providers/os/connection" "go.mondoo.com/cnquery/providers/os/connection/shared" ) @@ -90,3 +91,7 @@ func (lpm *DockerTopManager) Process(pid int64) (*OSProcess, error) { } return nil, fmt.Errorf("process with PID %d does not exist", pid) } + +func (lpm *DockerTopManager) ListSocketInodesByProcess() (map[int64]plugin.TValue[[]int64], error) { + return nil, nil +} diff --git a/providers/os/resources/processes/linuxproc.go b/providers/os/resources/processes/linuxproc.go index aea3014a61..6310268bb1 100644 --- a/providers/os/resources/processes/linuxproc.go +++ b/providers/os/resources/processes/linuxproc.go @@ -10,6 +10,7 @@ import ( "github.com/cockroachdb/errors" "github.com/rs/zerolog/log" "github.com/spf13/afero" + "go.mondoo.com/cnquery/providers-sdk/v1/plugin" "go.mondoo.com/cnquery/providers/os/connection/shared" "go.mondoo.com/cnquery/providers/os/resources/procfs" ) @@ -108,3 +109,7 @@ func (lpm *LinuxProcManager) Process(pid int64) (*OSProcess, error) { return process, nil } + +func (lpm *LinuxProcManager) ListSocketInodesByProcess() (map[int64]plugin.TValue[[]int64], error) { + return nil, nil +} diff --git a/providers/os/resources/processes/manager.go b/providers/os/resources/processes/manager.go index 4464b6bfb6..5dcc872a05 100644 --- a/providers/os/resources/processes/manager.go +++ b/providers/os/resources/processes/manager.go @@ -6,6 +6,7 @@ package processes import ( "errors" + "go.mondoo.com/cnquery/providers-sdk/v1/plugin" "go.mondoo.com/cnquery/providers/os/connection" "go.mondoo.com/cnquery/providers/os/connection/mock" "go.mondoo.com/cnquery/providers/os/connection/shared" @@ -26,6 +27,7 @@ type OSProcessManager interface { Exists(pid int64) (bool, error) Process(pid int64) (*OSProcess, error) List() ([]*OSProcess, error) + ListSocketInodesByProcess() (map[int64]plugin.TValue[[]int64], error) } func ResolveManager(conn shared.Connection) (OSProcessManager, error) { diff --git a/providers/os/resources/processes/ps1getprocess.go b/providers/os/resources/processes/ps1getprocess.go index a7ffd2f88d..92e5319d92 100644 --- a/providers/os/resources/processes/ps1getprocess.go +++ b/providers/os/resources/processes/ps1getprocess.go @@ -10,6 +10,7 @@ import ( "io" "github.com/rs/zerolog/log" + "go.mondoo.com/cnquery/providers-sdk/v1/plugin" "go.mondoo.com/cnquery/providers/os/connection/shared" "go.mondoo.com/cnquery/providers/os/resources/powershell" ) @@ -176,3 +177,7 @@ func (wpm *WindowsProcessManager) Exists(pid int64) (bool, error) { func (wpm *WindowsProcessManager) Process(pid int64) (*OSProcess, error) { return nil, errors.New("not implemented") } + +func (wpm *WindowsProcessManager) ListSocketInodesByProcess() (map[int64]plugin.TValue[[]int64], error) { + return nil, errors.New("not implemented") +} diff --git a/providers/os/resources/ports/testdata/find_nginx_container.txt b/providers/os/resources/processes/testdata/find_nginx_container.txt similarity index 100% rename from providers/os/resources/ports/testdata/find_nginx_container.txt rename to providers/os/resources/processes/testdata/find_nginx_container.txt diff --git a/providers/os/resources/processes/unixps.go b/providers/os/resources/processes/unixps.go index 75a3bbdd35..1f085fbfad 100644 --- a/providers/os/resources/processes/unixps.go +++ b/providers/os/resources/processes/unixps.go @@ -9,16 +9,32 @@ import ( "io" "regexp" "strconv" + "strings" "github.com/kballard/go-shellquote" "github.com/rs/zerolog/log" "go.mondoo.com/cnquery/providers-sdk/v1/inventory" + "go.mondoo.com/cnquery/providers-sdk/v1/plugin" "go.mondoo.com/cnquery/providers/os/connection/shared" ) var ( LINUX_PS_REGEX = regexp.MustCompile(`^\s*([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ].*)?$`) UNIX_PS_REGEX = regexp.MustCompile(`^\s*([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ].*)$`) + + // "lrwx------ 1 0 0 64 Dec 6 13:56 /proc/1/fd/12 -> socket:[37364]" + reFindSockets = regexp.MustCompile( + "^[lrwx-]+\\s+" + + "\\d+\\s+" + + "\\d+\\s+" + // uid + "\\d+\\s+" + // gid + "\\d+\\s+" + + "[^ ]+\\s+" + // month, e.g. Dec + "\\d+\\s+" + // day + "\\d+:\\d+\\s+" + // time + "/proc/(\\d+)/fd/\\d+\\s+" + // path + "->\\s+" + + ".*socket:\\[(\\d+)\\].*\\s*") // target ) type ProcessEntry struct { @@ -197,6 +213,37 @@ func (upm *UnixProcessManager) List() ([]*OSProcess, error) { return ps, nil } +// ListSocketInodesByProcess returns a map with a pid as key and a list of socket inodes as value +func (upm *UnixProcessManager) ListSocketInodesByProcess() (map[int64]plugin.TValue[[]int64], error) { + c, err := upm.conn.RunCommand("find /proc -maxdepth 4 -path '/proc/*/fd/*' -exec ls -n {} \\;") + if err != nil { + return nil, fmt.Errorf("processes> could not run command: %v", err) + } + + processesInodesByPid := map[int64]plugin.TValue[[]int64]{} + scanner := bufio.NewScanner(c.Stdout) + for scanner.Scan() { + line := scanner.Text() + pid, inode, err := ParseLinuxFindLine(line) + if err != nil || (pid == 0 && inode == 0) { + pluginValue := processesInodesByPid[pid] + pluginValue.Error = err + processesInodesByPid[pid] = pluginValue + continue + } + pluginValue := plugin.TValue[[]int64]{} + if _, ok := processesInodesByPid[pid]; ok { + pluginValue = processesInodesByPid[pid] + pluginValue.Data = append(pluginValue.Data, inode) + } else { + pluginValue.Data = []int64{inode} + } + processesInodesByPid[pid] = pluginValue + } + + return processesInodesByPid, nil +} + func (upm *UnixProcessManager) Exists(pid int64) (bool, error) { process, err := upm.Process(pid) if err != nil { @@ -224,3 +271,28 @@ func (upm *UnixProcessManager) Process(pid int64) (*OSProcess, error) { return nil, nil } + +func ParseLinuxFindLine(line string) (int64, int64, error) { + if strings.HasSuffix(line, "Permission denied") || strings.HasSuffix(line, "No such file or directory") { + return 0, 0, nil + } + + m := reFindSockets.FindStringSubmatch(line) + if len(m) == 0 { + return 0, 0, nil + } + + pid, err := strconv.ParseInt(m[1], 10, 64) + if err != nil { + log.Error().Err(err).Msg("cannot parse unix pid " + m[1]) + return 0, 0, err + } + + inode, err := strconv.ParseInt(m[2], 10, 64) + if err != nil { + log.Error().Err(err).Msg("cannot parse socket inode " + m[2]) + return 0, 0, err + } + + return pid, inode, nil +} diff --git a/providers/os/resources/processes/unixps_test.go b/providers/os/resources/processes/unixps_test.go index 3423af8767..03c122d9f1 100644 --- a/providers/os/resources/processes/unixps_test.go +++ b/providers/os/resources/processes/unixps_test.go @@ -4,9 +4,12 @@ package processes_test import ( + "bufio" + "os" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.mondoo.com/cnquery/providers-sdk/v1/inventory" "go.mondoo.com/cnquery/providers/os/connection/mock" "go.mondoo.com/cnquery/providers/os/resources/processes" @@ -99,3 +102,45 @@ func TestUnixPSProcessParser(t *testing.T) { assert.Equal(t, int64(88), m[20].Pid, "process pid detected") assert.Equal(t, int64(0), m[20].Uid, "process uid detected") } + +func TestParseLinuxFind(t *testing.T) { + fi, err := os.Open("./testdata/find_nginx_container.txt") + require.NoError(t, err) + defer fi.Close() + + scanner := bufio.NewScanner(fi) + scanner.Scan() + line := scanner.Text() + pid, inode, err := processes.ParseLinuxFindLine(line) + require.NoError(t, err) + require.Equal(t, int64(0), pid) + require.Equal(t, int64(0), inode) + + scanner.Scan() + line = scanner.Text() + pid, inode, err = processes.ParseLinuxFindLine(line) + require.NoError(t, err) + require.Equal(t, int64(0), pid) + require.Equal(t, int64(0), inode) + + scanner.Scan() + line = scanner.Text() + pid, inode, err = processes.ParseLinuxFindLine(line) + require.NoError(t, err) + require.Equal(t, int64(1), pid) + require.Equal(t, int64(41866685), inode) + + scanner.Scan() + line = scanner.Text() + pid, inode, err = processes.ParseLinuxFindLine(line) + require.NoError(t, err) + require.Equal(t, int64(0), pid) + require.Equal(t, int64(0), inode) + + scanner.Scan() + line = scanner.Text() + pid, inode, err = processes.ParseLinuxFindLine(line) + require.NoError(t, err) + require.Equal(t, int64(0), pid) + require.Equal(t, int64(0), inode) +}