mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
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:
parent
868d94d595
commit
3801f1c6d1
4 changed files with 228 additions and 321 deletions
1
changes/issue-4754-improve-primary-ip
Normal file
1
changes/issue-4754-improve-primary-ip
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Improve host detail query to populate primary ip and mac address on host.
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue