improve primary ip host detail query (#8407)

Previously, the primary ip was determined using the interface with the most traffic. This had a few issues and required explicitly filtering out certain interfaces that we don't want eg loopback, docker.

Instead, use the default route to determine the primary (private) ip. If there are multiple, use the one with the lowest metric and prefer ipv4 addresses.
This commit is contained in:
Michal Nicpon 2022-10-27 16:34:49 -06:00 committed by GitHub
parent 868d94d595
commit 3801f1c6d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 228 additions and 321 deletions

View file

@ -0,0 +1 @@
* Improve host detail query to populate primary ip and mac address on host.

View file

@ -560,10 +560,11 @@ func TestHostDetailQueries(t *testing.T) {
queries, discovery, err = svc.detailQueriesForHost(context.Background(), &host)
require.NoError(t, err)
// 2 additional queries, but -3 expected queries due to removed disk space query (only 1 of 2
// active for a given platform) and removed two Windows-specific operating system queries,
// so the result is -1.
require.Equal(t, len(expectedDetailQueries)-1, len(queries), distQueriesMapKeys(queries))
// 2 - 4 = -2
// └───┼────┼─► additional queries: bim, foobar
// └────┼─► windows specific queries: os_windows, os_version_windows, network_interface_windows, disk_space_windows
// └─► net difference
require.Equal(t, len(expectedDetailQueries)-2, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
for name := range queries {
assert.True(t,
@ -743,9 +744,8 @@ func TestLabelQueries(t *testing.T) {
// should be turned on so that we can quickly fill labels)
queries, discovery, acc, err := svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// -3 expected queries due to removed disk space query (only 1 of 2 active for a given platform)
// and removed two Windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
// -4 windows specific queries: disk_space_windows, network_interface_windows, os_windows, os_version_windows
require.Equal(t, len(expectedDetailQueries)-4, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
assert.NotZero(t, acc)
@ -835,9 +835,8 @@ func TestLabelQueries(t *testing.T) {
ctx = hostctx.NewContext(ctx, host)
queries, discovery, acc, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// +3 for label queries, but -3 expected queries due to removed disk space query (only 1 of 2
// active for a given platform) and removed two Windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries), len(queries), distQueriesMapKeys(queries))
// +3 for label queries, but -4 windows specific queries
require.Equal(t, len(expectedDetailQueries)-1, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
assert.Zero(t, acc)
@ -905,10 +904,9 @@ func TestDetailQueriesWithEmptyStrings(t *testing.T) {
// queries)
queries, discovery, acc, err := svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// -5 due to windows not having battery, mdm, munki_info and removed disk space query and
// operating system query (only 1 of 2 active for a given platform)
// -3 unix, -2 macos,
// -1 due to 'windows_update_history'
if !assert.Equal(t, len(expectedDetailQueries)-5, len(queries)-1) {
if !assert.Equal(t, len(expectedDetailQueries)-6, len(queries)-1) {
// this is just to print the diff between the expected and actual query
// keys when the count assertion fails, to help debugging - they are not
// expected to match.
@ -919,99 +917,85 @@ func TestDetailQueriesWithEmptyStrings(t *testing.T) {
resultJSON := `
{
"fleet_detail_query_network_interface": [
{
"address": "192.168.0.1",
"broadcast": "192.168.0.255",
"ibytes": "",
"ierrors": "",
"interface": "en0",
"ipackets": "25698094",
"last_change": "1474233476",
"mac": "5f:3d:4b:10:25:82",
"mask": "255.255.255.0",
"metric": "",
"mtu": "",
"obytes": "",
"oerrors": "",
"opackets": "",
"point_to_point": "",
"type": ""
}
],
"fleet_detail_query_os_version": [
{
"platform": "darwin",
"build": "15G1004",
"major": "10",
"minor": "10",
"name": "Mac OS X",
"patch": "6"
}
],
"fleet_detail_query_osquery_info": [
{
"build_distro": "10.10",
"build_platform": "darwin",
"config_hash": "3c6e4537c4d0eb71a7c6dda19d",
"config_valid": "1",
"extensions": "active",
"pid": "38113",
"start_time": "1475603155",
"version": "1.8.2",
"watcher": "38112"
}
],
"fleet_detail_query_system_info": [
{
"computer_name": "computer",
"cpu_brand": "Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz",
"cpu_logical_cores": "8",
"cpu_physical_cores": "4",
"cpu_subtype": "Intel x86-64h Haswell",
"cpu_type": "x86_64h",
"hardware_model": "MacBookPro11,4",
"hardware_serial": "ABCDEFGH",
"hardware_vendor": "Apple Inc.",
"hardware_version": "1.0",
"hostname": "computer.local",
"physical_memory": "17179869184",
"uuid": "uuid"
}
],
"fleet_detail_query_uptime": [
{
"days": "20",
"hours": "0",
"minutes": "48",
"seconds": "13",
"total_seconds": "1730893"
}
],
"fleet_detail_query_osquery_flags": [
{
"name":"config_tls_refresh",
"value":""
},
{
"name":"distributed_interval",
"value":""
},
{
"name":"logger_tls_period",
"value":""
}
],
"fleet_detail_query_orbit_info": [
{
"name":"version",
"value":"42"
},
{
"name":"device_auth_token",
"value":"foo"
}
]
"fleet_detail_query_network_interface_windows": [
{
"address": "192.168.0.1",
"mac": "5f:3d:4b:10:25:82"
}
],
"fleet_detail_query_os_version": [
{
"platform": "darwin",
"build": "15G1004",
"major": "10",
"minor": "10",
"name": "Mac OS X",
"patch": "6"
}
],
"fleet_detail_query_osquery_info": [
{
"build_distro": "10.10",
"build_platform": "darwin",
"config_hash": "3c6e4537c4d0eb71a7c6dda19d",
"config_valid": "1",
"extensions": "active",
"pid": "38113",
"start_time": "1475603155",
"version": "1.8.2",
"watcher": "38112"
}
],
"fleet_detail_query_system_info": [
{
"computer_name": "computer",
"cpu_brand": "Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz",
"cpu_logical_cores": "8",
"cpu_physical_cores": "4",
"cpu_subtype": "Intel x86-64h Haswell",
"cpu_type": "x86_64h",
"hardware_model": "MacBookPro11,4",
"hardware_serial": "ABCDEFGH",
"hardware_vendor": "Apple Inc.",
"hardware_version": "1.0",
"hostname": "computer.local",
"physical_memory": "17179869184",
"uuid": "uuid"
}
],
"fleet_detail_query_uptime": [
{
"days": "20",
"hours": "0",
"minutes": "48",
"seconds": "13",
"total_seconds": "1730893"
}
],
"fleet_detail_query_osquery_flags": [
{
"name": "config_tls_refresh",
"value": ""
},
{
"name": "distributed_interval",
"value": ""
},
{
"name": "logger_tls_period",
"value": ""
}
],
"fleet_detail_query_orbit_info": [
{
"name": "version",
"value": "42"
},
{
"name": "device_auth_token",
"value": "foo"
}
]
}
`
@ -1066,9 +1050,9 @@ func TestDetailQueriesWithEmptyStrings(t *testing.T) {
queries, discovery, acc, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// somehow confusingly, the query response above changed the host's platform
// from windows to darwin, so now it has all expected queries except the
// extra disk space one and two windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
// from windows to darwin
// -4 windows queries
require.Equal(t, len(expectedDetailQueries)-4, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
assert.Zero(t, acc)
}
@ -1131,9 +1115,8 @@ func TestDetailQueries(t *testing.T) {
// queries)
queries, discovery, acc, err := svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// -6 due to linux platform, so battery, mdm, and munki are missing, and the extra disk space
// query, and two windows-specific operating system queries, then +1 due to software inventory being enabled.
if !assert.Equal(t, len(expectedDetailQueries)-5, len(queries)) {
// -3 macos queries, -4 windows quries, +1 for software inventory
if !assert.Equal(t, len(expectedDetailQueries)-6, len(queries)) {
// this is just to print the diff between the expected and actual query
// keys when the count assertion fails, to help debugging - they are not
// expected to match.
@ -1390,10 +1373,8 @@ func TestDetailQueries(t *testing.T) {
queries, discovery, acc, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// host platform changed to darwin, so -3 for the
// extra disk space query and two windows-specific operating system queries,
// +1 for the software inventory enabled.
require.Equal(t, len(expectedDetailQueries)-2, len(queries), distQueriesMapKeys(queries))
// -4 windows queries, +1 software inventory
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
assert.Zero(t, acc)
}
@ -1517,8 +1498,8 @@ func TestDistributedQueryResults(t *testing.T) {
// Now we should get the active distributed query
queries, discovery, acc, err := svc.GetDistributedQueries(hostCtx)
require.NoError(t, err)
// -3 for the non-windows queries, +1 for the distributed query for campaign ID 42
if !assert.Equal(t, len(expectedDetailQueries)-3, len(queries)) {
// -3 unix, -2 macos, +1 for the distributed query for campaign ID 42
if !assert.Equal(t, len(expectedDetailQueries)-4, len(queries)) {
// this is just to print the diff between the expected and actual query
// keys when the count assertion fails, to help debugging - they are not
// expected to match.
@ -2132,9 +2113,9 @@ func TestDistributedQueriesLogsManyErrors(t *testing.T) {
err := svc.SubmitDistributedQueryResults(
ctx,
map[string][]map[string]string{
hostDetailQueryPrefix + "network_interface": {{"col1": "val1"}}, // we need one detail query that updates hosts.
hostLabelQueryPrefix + "1": {{"col1": "val1"}},
hostAdditionalQueryPrefix + "1": {{"col1": "val1"}},
hostDetailQueryPrefix + "network_interface_unix": {{"col1": "val1"}}, // we need one detail query that updates hosts.
hostLabelQueryPrefix + "1": {{"col1": "val1"}},
hostAdditionalQueryPrefix + "1": {{"col1": "val1"}},
},
map[string]fleet.OsqueryStatus{},
map[string]string{},
@ -2146,11 +2127,12 @@ func TestDistributedQueriesLogsManyErrors(t *testing.T) {
logs := buf.String()
parts := strings.Split(strings.TrimSpace(logs), "\n")
require.Len(t, parts, 1)
logData := make(map[string]json.RawMessage)
var logData map[string]interface{}
err = json.Unmarshal([]byte(parts[0]), &logData)
require.NoError(t, err)
assert.Equal(t, json.RawMessage(`"something went wrong || something went wrong"`), logData["err"])
assert.Equal(t, json.RawMessage(`"Missing authorization check"`), logData["internal"])
assert.Equal(t, "something went wrong || something went wrong", logData["err"])
assert.Equal(t, "Missing authorization check", logData["internal"])
}
func TestDistributedQueriesReloadsHostIfDetailsAreIn(t *testing.T) {
@ -2174,7 +2156,7 @@ func TestDistributedQueriesReloadsHostIfDetailsAreIn(t *testing.T) {
err := svc.SubmitDistributedQueryResults(
ctx,
map[string][]map[string]string{
hostDetailQueryPrefix + "network_interface": {{"col1": "val1"}},
hostDetailQueryPrefix + "network_interface_unix": {{"col1": "val1"}},
},
map[string]fleet.OsqueryStatus{},
map[string]string{},
@ -2357,9 +2339,8 @@ func TestPolicyQueries(t *testing.T) {
queries, discovery, _, err := svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all queries -3 for the extra disk space one and two windows-specific operating system queries,
// and +2 for the policy queries
require.Equal(t, len(expectedDetailQueries)-1, len(queries), distQueriesMapKeys(queries))
// all queries -4 windows only queries +2 policy queries
require.Equal(t, len(expectedDetailQueries)-2, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
checkPolicyResults := func(queries map[string]string) {
@ -2415,8 +2396,8 @@ func TestPolicyQueries(t *testing.T) {
ctx = hostctx.NewContext(context.Background(), host)
queries, discovery, _, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all standard queries minus the extra disk space and two windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
// all standard queries -4 windows only queries
require.Equal(t, len(expectedDetailQueries)-4, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
noPolicyResults(queries)
@ -2425,9 +2406,8 @@ func TestPolicyQueries(t *testing.T) {
queries, discovery, _, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all standard queries -3 (the extra disk space and two windows-specific operating system
// queries) and +2 policy queries
require.Equal(t, len(expectedDetailQueries)-1, len(queries), distQueriesMapKeys(queries))
// all standard queries -4 windows only queries +2 policy queries
require.Equal(t, len(expectedDetailQueries)-2, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
checkPolicyResults(queries)
@ -2455,8 +2435,8 @@ func TestPolicyQueries(t *testing.T) {
ctx = hostctx.NewContext(context.Background(), host)
queries, discovery, _, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all standard queries minus the extra disk space and two windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
// all standard queries -4 windows only queries
require.Equal(t, len(expectedDetailQueries)-4, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
noPolicyResults(queries)
@ -2465,9 +2445,8 @@ func TestPolicyQueries(t *testing.T) {
ctx = hostctx.NewContext(context.Background(), host)
queries, discovery, _, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all standard queries3-2 (the extra disk space and two windows-specific operating system
// query) and +2 policy queries
require.Equal(t, len(expectedDetailQueries)-1, len(queries), distQueriesMapKeys(queries))
// all standard queries -4 windows only queries +2 policy queries
require.Equal(t, len(expectedDetailQueries)-2, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
checkPolicyResults(queries)
@ -2497,8 +2476,8 @@ func TestPolicyQueries(t *testing.T) {
ctx = hostctx.NewContext(context.Background(), host)
queries, discovery, _, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all standard queries minus the extra disk space and two windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
// all standard queries -4 windows only queries
require.Equal(t, len(expectedDetailQueries)-4, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
noPolicyResults(queries)
}
@ -2563,8 +2542,8 @@ func TestPolicyWebhooks(t *testing.T) {
queries, discovery, _, err := svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all queries -3 for extra disk space and two windows-specific operating system queries, +3 for policies
require.Equal(t, len(expectedDetailQueries), len(queries), distQueriesMapKeys(queries))
// all queries -4 windows only, +3 for policies
require.Equal(t, len(expectedDetailQueries)-1, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
checkPolicyResults := func(queries map[string]string) {
@ -2677,8 +2656,8 @@ func TestPolicyWebhooks(t *testing.T) {
ctx = hostctx.NewContext(context.Background(), host)
queries, discovery, _, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all standard queries minus the extra disk space and two windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
// all queries -4 windows only queries
require.Equal(t, len(expectedDetailQueries)-4, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
noPolicyResults(queries)
@ -2687,8 +2666,8 @@ func TestPolicyWebhooks(t *testing.T) {
queries, discovery, _, err = svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all queries -3 for extra disk space and two windows-specific operating system queries, +3 for policies
require.Equal(t, len(expectedDetailQueries)-0, len(queries), distQueriesMapKeys(queries))
// all queries -4 windows only queries, +3 for policies
require.Equal(t, len(expectedDetailQueries)-1, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
checkPolicyResults(queries)
@ -2811,8 +2790,8 @@ func TestLiveQueriesFailing(t *testing.T) {
queries, discovery, _, err := svc.GetDistributedQueries(ctx)
require.NoError(t, err)
// all standard queries minus the extra disk space and two windows-specific operating system queries
require.Equal(t, len(expectedDetailQueries)-3, len(queries), distQueriesMapKeys(queries))
// all standard queries minus windows only queries
require.Equal(t, len(expectedDetailQueries)-4, len(queries), distQueriesMapKeys(queries))
verifyDiscovery(t, queries, discovery)
logs, err := io.ReadAll(buf)

View file

@ -3,7 +3,6 @@ package osquery_utils
import (
"context"
"fmt"
"net"
"os"
"regexp"
"strconv"
@ -62,71 +61,47 @@ func (q *DetailQuery) RunsForPlatform(platform string) bool {
//
// This map should not be modified at runtime.
var hostDetailQueries = map[string]DetailQuery{
"network_interface": {
Query: `select ia.address, id.mac, id.interface
from interface_details id join interface_addresses ia
on ia.interface = id.interface where length(mac) > 0
order by (ibytes + obytes) desc`,
IngestFunc: func(ctx context.Context, logger log.Logger, host *fleet.Host, rows []map[string]string) (err error) {
if len(rows) == 0 {
logger.Log("component", "service", "method", "IngestFunc", "err",
"detail_query_network_interface expected 1 or more results")
return nil
}
// Rows are ordered by traffic, so we will get the most active
// interface by iterating in order
var firstIPv4, firstIPv6 map[string]string
for _, row := range rows {
ip := net.ParseIP(row["address"])
if ip == nil {
continue
}
// Skip link-local and loopback interfaces
if ip.IsLinkLocalUnicast() || ip.IsLoopback() {
continue
}
// Skip docker interfaces as these are sometimes heavily
// trafficked, but rarely the interface that Fleet users want to
// see. https://github.com/fleetdm/fleet/issues/4754.
if strings.Contains(row["interface"], "docker") {
continue
}
if strings.Contains(row["address"], ":") {
// IPv6
if firstIPv6 == nil {
firstIPv6 = row
}
} else {
// IPv4
if firstIPv4 == nil {
firstIPv4 = row
}
}
}
var selected map[string]string
switch {
// Prefer IPv4
case firstIPv4 != nil:
selected = firstIPv4
// Otherwise IPv6
case firstIPv6 != nil:
selected = firstIPv6
// If only link-local and loopback found, still use the first
// interface so that we don't get an empty value.
default:
selected = rows[0]
}
host.PrimaryIP = selected["address"]
host.PrimaryMac = selected["mac"]
host.PublicIP = publicip.FromContext(ctx)
return nil
},
"network_interface_unix": {
Query: `
select
ia.address,
id.mac
from
interface_addresses ia
join interface_details id on id.interface = ia.interface
join routes r on r.interface = ia.interface
where
r.destination = '0.0.0.0'
and r.netmask = 0
and r.type = 'gateway'
and instr(ia.address, '.') > 0
order by
r.metric asc
limit 1
`,
Platforms: append(fleet.HostLinuxOSs, "darwin"),
IngestFunc: ingestNetworkInterface,
},
"network_interface_windows": {
Query: `
select
ia.address,
id.mac
from
interface_addresses ia
join interface_details id on id.interface = ia.interface
join routes r on r.interface = ia.address
where
r.destination = '0.0.0.0'
and r.netmask = 0
and r.type = 'remote'
and instr(ia.address, '.') > 0
order by
r.metric asc
limit 1
`,
Platforms: []string{"windows"},
IngestFunc: ingestNetworkInterface,
},
"os_version": {
// Collect operating system information for the `hosts` table.
@ -408,6 +383,55 @@ FROM logical_drives WHERE file_system = 'NTFS' LIMIT 1;`,
},
}
func ingestNetworkInterface(ctx context.Context, logger log.Logger, host *fleet.Host, rows []map[string]string) error {
if len(rows) != 1 {
logger.Log("component", "service", "method", "IngestFunc", "err",
fmt.Sprintf("detail_query_network_interface expected single result, got %d", len(rows)))
return nil
}
host.PrimaryIP = rows[0]["address"]
host.PrimaryMac = rows[0]["mac"]
host.PublicIP = publicip.FromContext(ctx)
return nil
}
func directIngestDiskSpace(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string, failed bool) error {
if failed {
level.Error(logger).Log("op", "directIngestDiskSpace", "err", "failed")
return nil
}
if len(rows) != 1 {
logger.Log("component", "service", "method", "directIngestDiskSpace", "err",
fmt.Sprintf("detail_query_disk_space expected single result got %d", len(rows)))
return nil
}
gigsAvailable, err := strconv.ParseFloat(EmptyToZero(rows[0]["gigs_disk_space_available"]), 64)
if err != nil {
return err
}
percentAvailable, err := strconv.ParseFloat(EmptyToZero(rows[0]["percent_disk_space_available"]), 64)
if err != nil {
return err
}
return ds.SetOrUpdateHostDisksSpace(ctx, host.ID, gigsAvailable, percentAvailable)
}
func ingestKubequeryInfo(ctx context.Context, logger log.Logger, host *fleet.Host, rows []map[string]string) error {
if len(rows) != 1 {
return fmt.Errorf("kubernetes_info expected single result got: %d", len(rows))
}
host.Hostname = fmt.Sprintf("kubequery %s", rows[0]["cluster_name"])
// These values are not provided by kubequery
host.OsqueryVersion = "kubequery"
host.Platform = "kubequery"
return nil
}
// extraDetailQueries defines extra detail queries that should be run on the host, as
// well as how the results of those queries should be ingested into the hosts related tables
// (via DirectIngestFunc).
@ -1155,29 +1179,6 @@ func directIngestUsers(ctx context.Context, logger log.Logger, host *fleet.Host,
return nil
}
func directIngestDiskSpace(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string, failed bool) error {
if failed {
level.Error(logger).Log("op", "directIngestDiskSpace", "err", "failed")
return nil
}
if len(rows) != 1 {
logger.Log("component", "service", "method", "directIngestDiskSpace", "err",
fmt.Sprintf("detail_query_disk_space expected single result got %d", len(rows)))
return nil
}
gigsAvailable, err := strconv.ParseFloat(EmptyToZero(rows[0]["gigs_disk_space_available"]), 64)
if err != nil {
return err
}
percentAvailable, err := strconv.ParseFloat(EmptyToZero(rows[0]["percent_disk_space_available"]), 64)
if err != nil {
return err
}
return ds.SetOrUpdateHostDisksSpace(ctx, host.ID, gigsAvailable, percentAvailable)
}
func directIngestMDM(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string, failed bool) error {
if len(rows) == 0 || failed {
// assume the extension is not there
@ -1222,19 +1223,6 @@ func directIngestMunkiInfo(ctx context.Context, logger log.Logger, host *fleet.H
return ds.SetOrUpdateMunkiInfo(ctx, host.ID, rows[0]["version"], errList, warnList)
}
func ingestKubequeryInfo(ctx context.Context, logger log.Logger, host *fleet.Host, rows []map[string]string) error {
if len(rows) != 1 {
return fmt.Errorf("kubernetes_info expected single result got: %d", len(rows))
}
host.Hostname = fmt.Sprintf("kubequery %s", rows[0]["cluster_name"])
// These values are not provided by kubequery
host.OsqueryVersion = "kubequery"
host.Platform = "kubequery"
return nil
}
func GetDetailQueries(fleetConfig config.FleetConfig, features *fleet.Features) map[string]DetailQuery {
generatedMap := make(map[string]DetailQuery)
for key, query := range hostDetailQueries {

View file

@ -24,84 +24,22 @@ func TestDetailQueryNetworkInterfaces(t *testing.T) {
var initialHost fleet.Host
host := initialHost
ingest := GetDetailQueries(config.FleetConfig{}, nil)["network_interface"].IngestFunc
ingest := GetDetailQueries(config.FleetConfig{}, nil)["network_interface_unix"].IngestFunc
assert.NoError(t, ingest(context.Background(), log.NewNopLogger(), &host, nil))
assert.Equal(t, initialHost, host)
var rows []map[string]string
// docker interface should be skipped even though it shows up first
require.NoError(t, json.Unmarshal([]byte(`
[
{"address":"127.0.0.1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"::1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"fe80::1%lo0","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"172.17.0.1","mac":"d3:4d:b3:3f:58:5b","interface":"docker0"},
{"address":"fe80::df:429b:971c:d051%en0","mac":"f4:5c:89:92:57:5b","interface":"en0"},
{"address":"192.168.1.3","mac":"f4:5d:79:93:58:5b","interface":"en0"},
{"address":"fe80::241a:9aff:fe60:d80a%awdl0","mac":"27:1b:aa:60:e8:0a","interface":"en0"},
{"address":"fe80::3a6f:582f:86c5:8296%utun0","mac":"00:00:00:00:00:00","interface":"utun0"}
{"address":"10.0.1.2","mac":"bc:d0:74:4b:10:6d"}
]`),
&rows,
))
assert.NoError(t, ingest(context.Background(), log.NewNopLogger(), &host, rows))
assert.Equal(t, "192.168.1.3", host.PrimaryIP)
assert.Equal(t, "f4:5d:79:93:58:5b", host.PrimaryMac)
// Only IPv6
require.NoError(t, json.Unmarshal([]byte(`
[
{"address":"127.0.0.1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"::1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"fe80::1%lo0","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"fe80::df:429b:971c:d051%en0","mac":"f4:5c:89:92:57:5b","interface":"en0"},
{"address":"2604:3f08:1337:9411:cbe:814f:51a6:e4e3","mac":"27:1b:aa:60:e8:0a","interface":"en0"},
{"address":"3333:3f08:1337:9411:cbe:814f:51a6:e4e3","mac":"bb:1b:aa:60:e8:bb","interface":"en0"},
{"address":"fe80::3a6f:582f:86c5:8296%utun0","mac":"00:00:00:00:00:00","interface":"utun0"}
]`),
&rows,
))
assert.NoError(t, ingest(context.Background(), log.NewNopLogger(), &host, rows))
assert.Equal(t, "2604:3f08:1337:9411:cbe:814f:51a6:e4e3", host.PrimaryIP)
assert.Equal(t, "27:1b:aa:60:e8:0a", host.PrimaryMac)
// IPv6 appears before IPv4 (v4 should be prioritized)
require.NoError(t, json.Unmarshal([]byte(`
[
{"address":"127.0.0.1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"::1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"fe80::1%lo0","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"fe80::df:429b:971c:d051%en0","mac":"f4:5c:89:92:57:5b","interface":"en0"},
{"address":"2604:3f08:1337:9411:cbe:814f:51a6:e4e3","mac":"27:1b:aa:60:e8:0a","interface":"en0"},
{"address":"205.111.43.79","mac":"ab:1b:aa:60:e8:0a","interface":"en1"},
{"address":"205.111.44.80","mac":"bb:bb:aa:60:e8:0a","interface":"en1"},
{"address":"fe80::3a6f:582f:86c5:8296%utun0","mac":"00:00:00:00:00:00","interface":"utun0"}
]`),
&rows,
))
assert.NoError(t, ingest(context.Background(), log.NewNopLogger(), &host, rows))
assert.Equal(t, "205.111.43.79", host.PrimaryIP)
assert.Equal(t, "ab:1b:aa:60:e8:0a", host.PrimaryMac)
// Only link-local/loopback
require.NoError(t, json.Unmarshal([]byte(`
[
{"address":"127.0.0.1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"::1","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"fe80::1%lo0","mac":"00:00:00:00:00:00","interface":"lo0"},
{"address":"fe80::df:429b:971c:d051%en0","mac":"f4:5c:89:92:57:5b","interface":"en0"},
{"address":"fe80::241a:9aff:fe60:d80a%awdl0","mac":"27:1b:aa:60:e8:0a","interface":"en0"},
{"address":"fe80::3a6f:582f:86c5:8296%utun0","mac":"00:00:00:00:00:00","interface":"utun0"}
]`),
&rows,
))
assert.NoError(t, ingest(context.Background(), log.NewNopLogger(), &host, rows))
assert.Equal(t, "127.0.0.1", host.PrimaryIP)
assert.Equal(t, "00:00:00:00:00:00", host.PrimaryMac)
assert.Equal(t, "10.0.1.2", host.PrimaryIP)
assert.Equal(t, "bc:d0:74:4b:10:6d", host.PrimaryMac)
}
func TestDetailQueryScheduledQueryStats(t *testing.T) {
@ -297,10 +235,11 @@ func sortedKeysCompare(t *testing.T, m map[string]DetailQuery, expectedKeys []st
func TestGetDetailQueries(t *testing.T) {
queriesNoConfig := GetDetailQueries(config.FleetConfig{}, nil)
require.Len(t, queriesNoConfig, 18)
require.Len(t, queriesNoConfig, 19)
baseQueries := []string{
"network_interface",
"network_interface_unix",
"network_interface_windows",
"os_version",
"os_version_windows",
"osquery_flags",
@ -322,14 +261,14 @@ func TestGetDetailQueries(t *testing.T) {
sortedKeysCompare(t, queriesNoConfig, baseQueries)
queriesWithoutWinOSVuln := GetDetailQueries(config.FleetConfig{Vulnerabilities: config.VulnerabilitiesConfig{DisableWinOSVulnerabilities: true}}, nil)
require.Len(t, queriesWithoutWinOSVuln, 17)
require.Len(t, queriesWithoutWinOSVuln, 18)
queriesWithUsers := GetDetailQueries(config.FleetConfig{App: config.AppConfig{EnableScheduledQueryStats: true}}, &fleet.Features{EnableHostUsers: true})
require.Len(t, queriesWithUsers, 20)
require.Len(t, queriesWithUsers, 21)
sortedKeysCompare(t, queriesWithUsers, append(baseQueries, "users", "scheduled_query_stats"))
queriesWithUsersAndSoftware := GetDetailQueries(config.FleetConfig{App: config.AppConfig{EnableScheduledQueryStats: true}}, &fleet.Features{EnableHostUsers: true, EnableSoftwareInventory: true})
require.Len(t, queriesWithUsersAndSoftware, 23)
require.Len(t, queriesWithUsersAndSoftware, 24)
sortedKeysCompare(t, queriesWithUsersAndSoftware,
append(baseQueries, "users", "software_macos", "software_linux", "software_windows", "scheduled_query_stats"))
}