Skip to content

Commit

Permalink
🐛 fix system detection
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-rock committed Sep 28, 2023
1 parent 908af3c commit 1f1fadb
Show file tree
Hide file tree
Showing 11 changed files with 1,216 additions and 13 deletions.
9 changes: 9 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@
"args": [
"go", "./providers/os/resources/os.lr", "--dist", "./providers/os/dist",
],
},
{
"name": "cnquery-status",
"type": "go",
"request": "launch",
"program": "${workspaceRoot}/apps/cnquery/cnquery.go",
"args": [
"status",
],
}
]
}
44 changes: 32 additions & 12 deletions cli/sysinfo/sysinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ package sysinfo
import (
"errors"

"github.com/rs/zerolog/log"
"go.mondoo.com/cnquery/providers/os/resources/networkinterface"

"go.mondoo.com/cnquery"
"go.mondoo.com/cnquery/cli/execruntime"
"go.mondoo.com/cnquery/llx"
Expand Down Expand Up @@ -44,12 +47,17 @@ func GatherSystemInfo(opts ...SystemInfoOption) (*SystemInfo, error) {
opt(cfg)
}

log.Debug().Msg("Gathering system information")
if cfg.runtime == nil {

cfg.runtime = providers.Coordinator.NewRuntime()

// TODO: we need to ensure that the os provider is available here

// init runtime
if err := cfg.runtime.UseProvider(providers.DefaultOsID); err != nil {
return nil, err
}

args, err := cfg.runtime.Provider.Instance.Plugin.ParseCLI(&plugin.ParseCLIReq{
Connector: "local",
})
Expand All @@ -70,7 +78,8 @@ func GatherSystemInfo(opts ...SystemInfoOption) (*SystemInfo, error) {
}

exec := mql.New(cfg.runtime, nil)
raw, err := exec.Exec("asset { name arch title family build version kind runtime labels }", nil)
// TODO: it is not returning it as a MQL SingleValue, therefore we need to force it with return
raw, err := exec.Exec("return asset { name arch title family build version kind runtime labels ids }", nil)
if err != nil {
return sysInfo, err
}
Expand All @@ -87,20 +96,31 @@ func GatherSystemInfo(opts ...SystemInfoOption) (*SystemInfo, error) {
Runtime: llx.TRaw2T[string](vals["runtime"]),
Labels: llx.TRaw2TMap[string](vals["labels"]),
}

platformID := llx.TRaw2TArr[string](vals["ids"])
if len(platformID) > 0 {
sysInfo.PlatformId = platformID[0]
}
} else {
return sysInfo, errors.New("returned asset detection type is incorrect")
}

// TODO: platform IDs
// idDetector := providers.HostnameDetector
// if pi.IsFamily(platform.FAMILY_WINDOWS) {
// idDetector = providers.MachineIdDetector
// }
// sysInfo.PlatformId = info.IDs[0]
// TODO: outbound ip
// sysInfo.IP = ip
// TODO: hostname
// sysInfo.Hostname = hn
// determine hostname
osRaw, err := exec.Exec("return os.hostname", nil)
if err != nil {
return sysInfo, err
}

if hostname, ok := osRaw.Value.(string); ok {
sysInfo.Hostname = hostname
}

// determine ip address
// TODO: move this to MQL and expose that information in the graph
ipAddr, err := networkinterface.GetOutboundIP()
if err == nil {
sysInfo.IP = ipAddr.String()
}

// detect the execution runtime
execEnv := execruntime.Detect()
Expand Down
2 changes: 1 addition & 1 deletion providers/os/connection/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (c *Connection) ID() uint32 {
}

func (c *Connection) Type() shared.ConnectionType {
return "local"
return "mock"
}

func (c *Connection) Asset() *inventory.Asset {
Expand Down
106 changes: 106 additions & 0 deletions providers/os/resources/networkinterface/hostip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package networkinterface

import (
"fmt"
"net"
"sort"

"github.com/cockroachdb/errors"
"github.com/rs/zerolog/log"
)

func filterNetworkInterface(interfaces []Interface, flagFilter func(flags net.Flags) bool) []Interface {
i := []Interface{}
for _, v := range interfaces {
if flagFilter(v.Flags) {
i = append(i, v)
}
}
return i
}

// byIfaceIndex Interface by its index
type byIfaceIndex []Interface

func (iface byIfaceIndex) Len() int { return len(iface) }
func (iface byIfaceIndex) Less(i, j int) bool { return iface[i].Index < iface[j].Index }
func (iface byIfaceIndex) Swap(i, j int) { iface[i], iface[j] = iface[j], iface[i] }

// HostIP extracts the best-guess for the IP of the host
// It will search ip v4 first and fallback to v6
func HostIP(interfaces []Interface) (ip string, err error) {
log.Debug().Int("interfaces", len(interfaces)).Msg("search ip")
// filter interfaces that are not up or a loopback/p2p interface
interfaces = filterNetworkInterface(interfaces, func(flags net.Flags) bool {
if (flags&net.FlagUp != 0) &&
(flags&net.FlagLoopback == 0) &&
(flags&net.FlagPointToPoint == 0) {
return true
}
return false
})

// sort interfaces by its index
sort.Sort(byIfaceIndex(interfaces))

var foundIPv4 net.IP
foundIPsv6 := []net.IP{}

// search for IPv4
for _, i := range interfaces {
addrs := i.Addrs
for _, addr := range addrs {
var foundIPv6 net.IP
switch v := addr.(type) {
case *net.IPAddr:
foundIPv4 = v.IP.To4()
foundIPv6 = v.IP.To16()
case *net.IPNet:
foundIPv4 = v.IP.To4()
foundIPv6 = v.IP.To16()
case *ipAddr:
foundIPv4 = v.IP.To4()
foundIPv6 = v.IP.To16()
}

if foundIPv4 != nil {
return foundIPv4.String(), nil
}
if foundIPv6 != nil {
foundIPsv6 = append(foundIPsv6, foundIPv6)
}
}
}

// search for IPv6
if len(foundIPsv6) > 0 {
return foundIPsv6[0].String(), nil
}

return "", fmt.Errorf("no IP address found")
}

// GetOutboundIP returns the local IP that is used for outbound connections
// It does not establish a real connection and the destination does not need to valid.
// Since its using udp protocol (unlike TCP) a handshake nor connection is required,
// / then it gets the local up address if it would connect to that target
// conn.LocalAddr().String() returns the local ip and port
//
// # NOTE be aware that this code does not work on remote targets
//
// @see this approach is derived from https://stackoverflow.com/a/37382208
func GetOutboundIP() (net.IP, error) {
conn, err := net.Dial("udp", "1.1.1.1:80")
if err != nil {
return nil, errors.Wrap(err, "could not determine outbound ip")
}
defer conn.Close()

localAddr := conn.LocalAddr().(*net.UDPAddr)

if localAddr == nil {
return nil, errors.New("could not determine outbound ip")
}

return localAddr.IP, nil
}
Loading

0 comments on commit 1f1fadb

Please sign in to comment.