2016-11-16 13:47:49 +00:00
package mysql
import (
"database/sql"
2016-12-01 17:00:00 +00:00
"fmt"
2021-05-07 04:05:09 +00:00
"strings"
2016-11-16 13:47:49 +00:00
"time"
2020-12-10 19:04:58 +00:00
"github.com/cenkalti/backoff/v4"
2020-11-11 17:59:12 +00:00
"github.com/fleetdm/fleet/server/kolide"
2020-12-10 19:04:58 +00:00
"github.com/jmoiron/sqlx"
2017-01-04 21:16:17 +00:00
"github.com/pkg/errors"
2016-11-16 13:47:49 +00:00
)
2021-02-18 20:52:43 +00:00
var hostSearchColumns = [ ] string { "host_name" , "uuid" , "hardware_serial" , "primary_ip" }
2016-11-16 13:47:49 +00:00
func ( d * Datastore ) NewHost ( host * kolide . Host ) ( * kolide . Host , error ) {
sqlStatement := `
INSERT INTO hosts (
2016-12-06 19:51:11 +00:00
osquery_host_id ,
2016-11-16 13:47:49 +00:00
detail_update_time ,
2020-04-07 01:10:20 +00:00
label_update_time ,
2016-11-16 13:47:49 +00:00
node_key ,
host_name ,
uuid ,
platform ,
osquery_version ,
os_version ,
uptime ,
2017-01-04 21:16:17 +00:00
physical_memory ,
2021-05-27 20:18:00 +00:00
seen_time ,
team_id
2016-11-16 13:47:49 +00:00
)
2021-05-27 20:18:00 +00:00
VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
2016-11-16 13:47:49 +00:00
`
2020-04-07 01:10:20 +00:00
result , err := d . db . Exec (
sqlStatement ,
host . OsqueryHostID ,
host . DetailUpdateTime ,
host . LabelUpdateTime ,
host . NodeKey ,
host . HostName ,
host . UUID ,
host . Platform ,
host . OsqueryVersion ,
host . OSVersion ,
host . Uptime ,
host . PhysicalMemory ,
host . SeenTime ,
2021-05-27 20:18:00 +00:00
host . TeamID ,
2020-04-07 01:10:20 +00:00
)
2016-11-16 13:47:49 +00:00
if err != nil {
2017-01-04 21:16:17 +00:00
return nil , errors . Wrap ( err , "new host" )
2016-11-16 13:47:49 +00:00
}
id , _ := result . LastInsertId ( )
host . ID = uint ( id )
return host , nil
}
// TODO needs test
func ( d * Datastore ) SaveHost ( host * kolide . Host ) error {
sqlStatement := `
UPDATE hosts SET
detail_update_time = ? ,
2020-04-07 01:10:20 +00:00
label_update_time = ? ,
2016-11-16 13:47:49 +00:00
node_key = ? ,
host_name = ? ,
uuid = ? ,
platform = ? ,
osquery_version = ? ,
os_version = ? ,
uptime = ? ,
physical_memory = ? ,
2016-12-01 17:00:00 +00:00
cpu_type = ? ,
cpu_subtype = ? ,
cpu_brand = ? ,
cpu_physical_cores = ? ,
hardware_vendor = ? ,
hardware_model = ? ,
hardware_version = ? ,
hardware_serial = ? ,
computer_name = ? ,
build = ? ,
platform_like = ? ,
code_name = ? ,
2017-01-04 21:16:17 +00:00
cpu_logical_cores = ? ,
2017-04-06 18:55:24 +00:00
seen_time = ? ,
distributed_interval = ? ,
config_tls_refresh = ? ,
2020-05-21 15:36:00 +00:00
logger_tls_period = ? ,
2020-05-29 16:12:39 +00:00
additional = COALESCE ( ? , additional ) ,
2021-05-31 16:02:05 +00:00
team_id = ? ,
2020-03-11 20:38:44 +00:00
primary_ip = ? ,
2021-05-13 20:09:22 +00:00
primary_mac = ? ,
refetch_requested = ?
2016-11-16 13:47:49 +00:00
WHERE id = ?
`
2020-03-11 20:38:44 +00:00
_ , err := d . db . Exec ( sqlStatement ,
host . DetailUpdateTime ,
2020-04-07 01:10:20 +00:00
host . LabelUpdateTime ,
2020-03-11 20:38:44 +00:00
host . NodeKey ,
host . HostName ,
host . UUID ,
host . Platform ,
host . OsqueryVersion ,
host . OSVersion ,
host . Uptime ,
host . PhysicalMemory ,
host . CPUType ,
host . CPUSubtype ,
host . CPUBrand ,
host . CPUPhysicalCores ,
host . HardwareVendor ,
host . HardwareModel ,
host . HardwareVersion ,
host . HardwareSerial ,
host . ComputerName ,
host . Build ,
host . PlatformLike ,
host . CodeName ,
host . CPULogicalCores ,
host . SeenTime ,
host . DistributedInterval ,
host . ConfigTLSRefresh ,
host . LoggerTLSPeriod ,
host . Additional ,
2021-05-31 16:02:05 +00:00
host . TeamID ,
2020-03-11 20:38:44 +00:00
host . PrimaryIP ,
host . PrimaryMac ,
2021-05-13 20:09:22 +00:00
host . RefetchRequested ,
2020-03-11 20:38:44 +00:00
host . ID ,
)
if err != nil {
return errors . Wrapf ( err , "save host with id %d" , host . ID )
}
2020-03-11 01:14:02 +00:00
2021-05-07 04:05:09 +00:00
// Save host pack stats only if it is non-nil. Empty stats should be
// represented by an empty slice.
if host . PackStats != nil {
if err := d . saveHostPackStats ( host ) ; err != nil {
return err
}
}
return nil
}
func ( d * Datastore ) saveHostPackStats ( host * kolide . Host ) error {
if err := d . withRetryTxx ( func ( tx * sqlx . Tx ) error {
sql := `
DELETE FROM scheduled_query_stats
WHERE host_id = ?
`
if _ , err := tx . Exec ( sql , host . ID ) ; err != nil {
return errors . Wrap ( err , "delete old stats" )
}
// Bulk insert software entries
var args [ ] interface { }
queryCount := 0
for _ , pack := range host . PackStats {
for _ , query := range pack . QueryStats {
queryCount ++
args = append ( args ,
query . PackName ,
query . ScheduledQueryName ,
host . ID ,
query . AverageMemory ,
query . Denylisted ,
query . Executions ,
query . Interval ,
query . LastExecuted ,
query . OutputSize ,
query . SystemTime ,
query . UserTime ,
query . WallTime ,
)
}
}
if queryCount == 0 {
return nil
}
values := strings . TrimSuffix ( strings . Repeat ( "((SELECT sq.id FROM scheduled_queries sq JOIN packs p ON (sq.pack_id = p.id) WHERE p.name = ? AND sq.name = ?),?,?,?,?,?,?,?,?,?,?)," , queryCount ) , "," )
sql = fmt . Sprintf ( `
INSERT IGNORE INTO scheduled_query_stats (
scheduled_query_id ,
host_id ,
average_memory ,
denylisted ,
executions ,
schedule_interval ,
last_executed ,
output_size ,
system_time ,
user_time ,
wall_time
)
VALUES % s
` , values )
if _ , err := tx . Exec ( sql , args ... ) ; err != nil {
return errors . Wrap ( err , "insert pack stats" )
}
return nil
} ) ; err != nil {
return errors . Wrap ( err , "save pack stats" )
}
return nil
}
func ( d * Datastore ) loadHostPackStats ( host * kolide . Host ) error {
sql := `
SELECT
sqs . scheduled_query_id ,
sqs . average_memory ,
sqs . denylisted ,
sqs . executions ,
sqs . schedule_interval ,
sqs . last_executed ,
sqs . output_size ,
sqs . system_time ,
sqs . user_time ,
sqs . wall_time ,
sq . name AS scheduled_query_name ,
sq . id AS scheduled_query_id ,
sq . query_name AS query_name ,
p . name AS pack_name ,
2021-05-07 19:47:52 +00:00
p . id as pack_id ,
q . description
2021-05-07 04:05:09 +00:00
FROM scheduled_query_stats sqs
JOIN scheduled_queries sq ON ( sqs . scheduled_query_id = sq . id )
2021-05-07 19:47:52 +00:00
JOIN packs p ON ( sq . pack_id = p . id )
JOIN queries q ON ( sq . query_name = q . name )
2021-05-07 04:05:09 +00:00
WHERE host_id = ?
`
var stats [ ] kolide . ScheduledQueryStats
if err := d . db . Select ( & stats , sql , host . ID ) ; err != nil {
return errors . Wrap ( err , "load pack stats" )
}
packs := map [ uint ] kolide . PackStats { }
for _ , query := range stats {
pack := packs [ query . PackID ]
pack . PackName = query . PackName
pack . PackID = query . PackID
pack . QueryStats = append ( pack . QueryStats , query )
packs [ pack . PackID ] = pack
}
for _ , pack := range packs {
host . PackStats = append ( host . PackStats , pack )
}
2020-03-11 20:38:44 +00:00
return nil
2016-11-16 13:47:49 +00:00
}
2017-01-04 18:18:21 +00:00
func ( d * Datastore ) DeleteHost ( hid uint ) error {
2020-10-22 17:51:26 +00:00
err := d . deleteEntity ( "hosts" , hid )
2017-01-20 17:22:33 +00:00
if err != nil {
return errors . Wrapf ( err , "deleting host with id %d" , hid )
}
return nil
2016-11-16 13:47:49 +00:00
}
func ( d * Datastore ) Host ( id uint ) ( * kolide . Host , error ) {
sqlStatement := `
2021-05-18 00:52:59 +00:00
SELECT h . * , t . name AS team_name
FROM hosts h LEFT JOIN teams t ON ( h . team_id = t . id )
WHERE h . id = ? LIMIT 1
2016-11-16 13:47:49 +00:00
`
host := & kolide . Host { }
err := d . db . Get ( host , sqlStatement , id )
if err != nil {
2021-05-07 04:05:09 +00:00
return nil , errors . Wrap ( err , "get host by id" )
}
if err := d . loadHostPackStats ( host ) ; err != nil {
return nil , err
2016-11-16 13:47:49 +00:00
}
return host , nil
}
2020-03-30 02:19:54 +00:00
func ( d * Datastore ) ListHosts ( opt kolide . HostListOptions ) ( [ ] * kolide . Host , error ) {
2021-05-18 00:52:59 +00:00
sql := ` SELECT
h . id ,
h . osquery_host_id ,
h . created_at ,
h . updated_at ,
h . detail_update_time ,
h . node_key ,
h . host_name ,
h . uuid ,
h . platform ,
h . osquery_version ,
h . os_version ,
h . build ,
h . platform_like ,
h . code_name ,
h . uptime ,
h . physical_memory ,
h . cpu_type ,
h . cpu_subtype ,
h . cpu_brand ,
h . cpu_physical_cores ,
h . cpu_logical_cores ,
h . hardware_vendor ,
h . hardware_model ,
h . hardware_version ,
h . hardware_serial ,
h . computer_name ,
h . primary_ip_id ,
h . seen_time ,
h . distributed_interval ,
h . logger_tls_period ,
h . config_tls_refresh ,
h . primary_ip ,
h . primary_mac ,
h . label_update_time ,
h . team_id ,
2021-05-19 17:16:54 +00:00
h . refetch_requested ,
2021-05-18 00:52:59 +00:00
t . name AS team_name ,
Add host additional info filters (#28)
This change adds the ability to filter additional host info via the list hosts endpoint; a continuation from [here](https://github.com/kolide/fleet/pull/2330), but now filtering is accomplished via SQL.
Additional object without filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"time": [
{
"day": "13",
"hour": "3",
"year": "2020",
"month": "10",
"minutes": "43",
"seconds": "11",
"weekday": "Tuesday",
"datetime": "2020-10-13T03:43:11Z",
"iso_8601": "2020-10-13T03:43:11Z",
"timezone": "GMT",
"timestamp": "Tue Oct 13 03:43:11 2020 UTC",
"unix_time": "1602560591",
"local_time": "1602560591",
"local_timezone": "UTC"
}
},
...
```
Additional object with filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts?additional_info_filters=macs,notreal'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"notreal": null
},
...
```
2020-11-14 00:33:25 +00:00
`
2020-03-30 02:19:54 +00:00
var params [ ] interface { }
Add host additional info filters (#28)
This change adds the ability to filter additional host info via the list hosts endpoint; a continuation from [here](https://github.com/kolide/fleet/pull/2330), but now filtering is accomplished via SQL.
Additional object without filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"time": [
{
"day": "13",
"hour": "3",
"year": "2020",
"month": "10",
"minutes": "43",
"seconds": "11",
"weekday": "Tuesday",
"datetime": "2020-10-13T03:43:11Z",
"iso_8601": "2020-10-13T03:43:11Z",
"timezone": "GMT",
"timestamp": "Tue Oct 13 03:43:11 2020 UTC",
"unix_time": "1602560591",
"local_time": "1602560591",
"local_timezone": "UTC"
}
},
...
```
Additional object with filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts?additional_info_filters=macs,notreal'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"notreal": null
},
...
```
2020-11-14 00:33:25 +00:00
// Filter additional info by extracting into a new json object.
if len ( opt . AdditionalFilters ) > 0 {
sql += ` JSON_OBJECT (
`
for _ , field := range opt . AdditionalFilters {
2021-05-17 17:29:50 +00:00
sql += ` ?, JSON_EXTRACT(additional, ?), `
Add host additional info filters (#28)
This change adds the ability to filter additional host info via the list hosts endpoint; a continuation from [here](https://github.com/kolide/fleet/pull/2330), but now filtering is accomplished via SQL.
Additional object without filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"time": [
{
"day": "13",
"hour": "3",
"year": "2020",
"month": "10",
"minutes": "43",
"seconds": "11",
"weekday": "Tuesday",
"datetime": "2020-10-13T03:43:11Z",
"iso_8601": "2020-10-13T03:43:11Z",
"timezone": "GMT",
"timestamp": "Tue Oct 13 03:43:11 2020 UTC",
"unix_time": "1602560591",
"local_time": "1602560591",
"local_timezone": "UTC"
}
},
...
```
Additional object with filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts?additional_info_filters=macs,notreal'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"notreal": null
},
...
```
2020-11-14 00:33:25 +00:00
params = append ( params , field , fmt . Sprintf ( ` $."%s" ` , field ) )
}
sql = sql [ : len ( sql ) - 2 ]
sql += `
) AS additional
`
} else {
sql += `
additional
`
}
2021-05-18 00:52:59 +00:00
sql += ` FROM hosts h LEFT JOIN teams t ON ( h . team_id = t . id )
2021-02-17 00:53:42 +00:00
WHERE TRUE
Add host additional info filters (#28)
This change adds the ability to filter additional host info via the list hosts endpoint; a continuation from [here](https://github.com/kolide/fleet/pull/2330), but now filtering is accomplished via SQL.
Additional object without filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"time": [
{
"day": "13",
"hour": "3",
"year": "2020",
"month": "10",
"minutes": "43",
"seconds": "11",
"weekday": "Tuesday",
"datetime": "2020-10-13T03:43:11Z",
"iso_8601": "2020-10-13T03:43:11Z",
"timezone": "GMT",
"timestamp": "Tue Oct 13 03:43:11 2020 UTC",
"unix_time": "1602560591",
"local_time": "1602560591",
"local_timezone": "UTC"
}
},
...
```
Additional object with filter:
```
curl 'https://localhost:8080/api/v1/kolide/hosts?additional_info_filters=macs,notreal'
...
"additional": {
"macs": [
{
"mac": "00:00:00:00:00:00"
},
{
"mac": "02:42:c0:a8:10:05"
}
],
"notreal": null
},
...
```
2020-11-14 00:33:25 +00:00
`
2020-03-30 02:19:54 +00:00
switch opt . StatusFilter {
case "new" :
2021-05-18 00:52:59 +00:00
sql += "AND DATE_ADD(h.created_at, INTERVAL 1 DAY) >= ?"
2020-03-30 02:19:54 +00:00
params = append ( params , time . Now ( ) )
case "online" :
2021-05-18 00:52:59 +00:00
sql += fmt . Sprintf ( "AND DATE_ADD(h.seen_time, INTERVAL LEAST(h.distributed_interval, h.config_tls_refresh) + %d SECOND) > ?" , kolide . OnlineIntervalBuffer )
2020-03-30 02:19:54 +00:00
params = append ( params , time . Now ( ) )
case "offline" :
2021-05-18 00:52:59 +00:00
sql += fmt . Sprintf ( "AND DATE_ADD(h.seen_time, INTERVAL LEAST(h.distributed_interval, h.config_tls_refresh) + %d SECOND) <= ? AND DATE_ADD(h.seen_time, INTERVAL 30 DAY) >= ?" , kolide . OnlineIntervalBuffer )
2020-03-30 02:19:54 +00:00
params = append ( params , time . Now ( ) , time . Now ( ) )
case "mia" :
2021-05-18 00:52:59 +00:00
sql += "AND DATE_ADD(h.seen_time, INTERVAL 30 DAY) <= ?"
2020-03-30 02:19:54 +00:00
params = append ( params , time . Now ( ) )
}
2021-02-17 00:53:42 +00:00
2021-02-18 20:52:43 +00:00
sql , params = searchLike ( sql , params , opt . MatchQuery , hostSearchColumns ... )
2021-02-17 00:53:42 +00:00
2020-03-30 02:19:54 +00:00
sql = appendListOptionsToSQL ( sql , opt . ListOptions )
2016-11-16 13:47:49 +00:00
hosts := [ ] * kolide . Host { }
2020-03-30 02:19:54 +00:00
if err := d . db . Select ( & hosts , sql , params ... ) ; err != nil {
2017-01-04 21:16:17 +00:00
return nil , errors . Wrap ( err , "list hosts" )
2016-11-16 13:47:49 +00:00
}
2016-12-06 19:51:11 +00:00
return hosts , nil
}
2019-04-09 18:11:11 +00:00
func ( d * Datastore ) CleanupIncomingHosts ( now time . Time ) error {
sqlStatement := `
DELETE FROM hosts
WHERE host_name = ' ' AND osquery_version = ' '
AND created_at < ( ? - INTERVAL 5 MINUTE )
`
if _ , err := d . db . Exec ( sqlStatement , now ) ; err != nil {
return errors . Wrap ( err , "cleanup incoming hosts" )
}
return nil
}
2017-04-18 17:39:50 +00:00
func ( d * Datastore ) GenerateHostStatusStatistics ( now time . Time ) ( online , offline , mia , new uint , e error ) {
// The logic in this function should remain synchronized with
// host.Status and CountHostsInTargets
sqlStatement := fmt . Sprintf ( `
SELECT
COALESCE ( SUM ( CASE WHEN DATE_ADD ( seen_time , INTERVAL 30 DAY ) <= ? THEN 1 ELSE 0 END ) , 0 ) mia ,
COALESCE ( SUM ( CASE WHEN DATE_ADD ( seen_time , INTERVAL LEAST ( distributed_interval , config_tls_refresh ) + % d SECOND ) <= ? AND DATE_ADD ( seen_time , INTERVAL 30 DAY ) >= ? THEN 1 ELSE 0 END ) , 0 ) offline ,
COALESCE ( SUM ( CASE WHEN DATE_ADD ( seen_time , INTERVAL LEAST ( distributed_interval , config_tls_refresh ) + % d SECOND ) > ? THEN 1 ELSE 0 END ) , 0 ) online ,
COALESCE ( SUM ( CASE WHEN DATE_ADD ( created_at , INTERVAL 1 DAY ) >= ? THEN 1 ELSE 0 END ) , 0 ) new
2017-01-04 21:16:17 +00:00
FROM hosts
LIMIT 1 ;
2017-04-18 17:39:50 +00:00
` , kolide . OnlineIntervalBuffer , kolide . OnlineIntervalBuffer )
2017-01-04 21:16:17 +00:00
counts := struct {
MIA uint ` db:"mia" `
Offline uint ` db:"offline" `
Online uint ` db:"online" `
2017-01-20 13:57:47 +00:00
New uint ` db:"new" `
2017-01-04 21:16:17 +00:00
} { }
2017-04-18 17:39:50 +00:00
err := d . db . Get ( & counts , sqlStatement , now , now , now , now , now )
2017-01-16 19:52:03 +00:00
if err != nil && err != sql . ErrNoRows {
2017-01-04 21:16:17 +00:00
e = errors . Wrap ( err , "generating host statistics" )
return
}
mia = counts . MIA
offline = counts . Offline
online = counts . Online
2017-01-20 13:57:47 +00:00
new = counts . New
return online , offline , mia , new , nil
2017-01-04 21:16:17 +00:00
}
2016-11-16 13:47:49 +00:00
// EnrollHost enrolls a host
2021-05-31 16:02:05 +00:00
func ( d * Datastore ) EnrollHost ( osqueryHostID , nodeKey string , teamID * uint , cooldown time . Duration ) ( * kolide . Host , error ) {
2016-12-06 19:51:11 +00:00
if osqueryHostID == "" {
2017-01-04 21:16:17 +00:00
return nil , fmt . Errorf ( "missing osquery host identifier" )
2016-11-16 13:47:49 +00:00
}
2016-12-06 19:51:11 +00:00
2020-12-10 19:04:58 +00:00
var host kolide . Host
err := d . withRetryTxx ( func ( tx * sqlx . Tx ) error {
zeroTime := time . Unix ( 0 , 0 ) . Add ( 24 * time . Hour )
2016-11-16 13:47:49 +00:00
2020-12-10 19:04:58 +00:00
var id int64
err := tx . Get ( & host , ` SELECT id, last_enroll_time FROM hosts WHERE osquery_host_id = ? ` , osqueryHostID )
2021-01-19 22:45:58 +00:00
switch {
case err != nil && ! errors . Is ( err , sql . ErrNoRows ) :
return errors . Wrap ( err , "check existing" )
case errors . Is ( err , sql . ErrNoRows ) :
2020-12-10 19:04:58 +00:00
// Create new host record
sqlInsert := `
INSERT INTO hosts (
detail_update_time ,
label_update_time ,
osquery_host_id ,
seen_time ,
node_key ,
2021-05-31 16:02:05 +00:00
team_id
2020-12-10 19:04:58 +00:00
) VALUES ( ? , ? , ? , ? , ? , ? )
`
2021-05-31 16:02:05 +00:00
result , err := tx . Exec ( sqlInsert , zeroTime , zeroTime , osqueryHostID , time . Now ( ) . UTC ( ) , nodeKey , teamID )
2016-11-16 13:47:49 +00:00
2020-12-10 19:04:58 +00:00
if err != nil {
return errors . Wrap ( err , "insert host" )
}
2016-11-16 13:47:49 +00:00
2020-12-10 19:04:58 +00:00
id , _ = result . LastInsertId ( )
2021-01-19 22:45:58 +00:00
default :
2020-12-10 19:04:58 +00:00
// Prevent hosts from enrolling too often with the same identifier.
// Prior to adding this we saw many hosts (probably VMs) with the
// same identifier competing for enrollment and causing perf issues.
2021-03-09 05:26:09 +00:00
if cooldown > 0 && time . Since ( host . LastEnrollTime ) < cooldown {
2020-12-10 19:04:58 +00:00
return backoff . Permanent ( fmt . Errorf ( "host identified by %s enrolling too often" , osqueryHostID ) )
}
id = int64 ( host . ID )
// Update existing host record
sqlUpdate := `
UPDATE hosts
SET node_key = ? ,
2021-05-31 16:02:05 +00:00
team_id = ? ,
2020-12-10 19:04:58 +00:00
last_enroll_time = NOW ( )
WHERE osquery_host_id = ?
`
2021-05-31 16:02:05 +00:00
_ , err := tx . Exec ( sqlUpdate , nodeKey , teamID , osqueryHostID )
2016-11-16 13:47:49 +00:00
2020-12-10 19:04:58 +00:00
if err != nil {
return errors . Wrap ( err , "update host" )
}
}
2020-04-07 01:10:20 +00:00
2020-12-10 19:04:58 +00:00
sqlSelect := `
SELECT * FROM hosts WHERE id = ? LIMIT 1
`
err = tx . Get ( & host , sqlSelect , id )
if err != nil {
return errors . Wrap ( err , "getting the host to return" )
}
_ , err = tx . Exec ( ` INSERT IGNORE INTO label_membership (host_id, label_id) VALUES (?, (SELECT id FROM labels WHERE name = 'All Hosts' AND label_type = 1)) ` , id )
if err != nil {
return errors . Wrap ( err , "insert new host into all hosts label" )
}
2016-11-16 13:47:49 +00:00
2020-12-10 19:04:58 +00:00
return nil
} )
if err != nil {
return nil , err
}
return & host , nil
2016-11-16 13:47:49 +00:00
}
func ( d * Datastore ) AuthenticateHost ( nodeKey string ) ( * kolide . Host , error ) {
2021-03-09 17:01:26 +00:00
// Select everything besides `additional`
2016-11-16 13:47:49 +00:00
sqlStatement := `
2020-05-21 15:36:00 +00:00
SELECT
id ,
osquery_host_id ,
created_at ,
updated_at ,
detail_update_time ,
2020-04-22 20:54:32 +00:00
label_update_time ,
2020-05-21 15:36:00 +00:00
node_key ,
host_name ,
uuid ,
platform ,
osquery_version ,
os_version ,
build ,
platform_like ,
code_name ,
uptime ,
physical_memory ,
cpu_type ,
cpu_subtype ,
cpu_brand ,
cpu_physical_cores ,
cpu_logical_cores ,
hardware_vendor ,
hardware_model ,
hardware_version ,
hardware_serial ,
computer_name ,
primary_ip_id ,
seen_time ,
distributed_interval ,
logger_tls_period ,
2020-05-29 16:12:39 +00:00
config_tls_refresh ,
2021-03-09 17:01:26 +00:00
primary_ip ,
primary_mac ,
2021-05-31 16:02:05 +00:00
refetch_requested ,
team_id
2016-12-06 19:51:11 +00:00
FROM hosts
2020-10-22 17:51:26 +00:00
WHERE node_key = ?
2016-12-06 19:51:11 +00:00
LIMIT 1
2016-11-16 13:47:49 +00:00
`
2016-12-06 19:51:11 +00:00
2016-11-16 13:47:49 +00:00
host := & kolide . Host { }
if err := d . db . Get ( host , sqlStatement , nodeKey ) ; err != nil {
switch err {
case sql . ErrNoRows :
2017-05-25 21:10:12 +00:00
return nil , notFound ( "Host" )
2016-11-16 13:47:49 +00:00
default :
2021-05-31 16:02:05 +00:00
return nil , errors . New ( "find host" )
2016-11-16 13:47:49 +00:00
}
}
2016-12-06 19:51:11 +00:00
return host , nil
2016-11-16 13:47:49 +00:00
}
2016-11-16 23:12:59 +00:00
func ( d * Datastore ) MarkHostSeen ( host * kolide . Host , t time . Time ) error {
sqlStatement := `
UPDATE hosts SET
2017-01-04 21:16:17 +00:00
seen_time = ?
2016-11-16 23:12:59 +00:00
WHERE node_key = ?
`
_ , err := d . db . Exec ( sqlStatement , t , host . NodeKey )
if err != nil {
2017-01-04 21:16:17 +00:00
return errors . Wrap ( err , "marking host seen" )
2016-11-16 23:12:59 +00:00
}
host . UpdatedAt = t
return nil
2016-11-16 13:47:49 +00:00
}
2021-04-12 23:22:22 +00:00
func ( d * Datastore ) MarkHostsSeen ( hostIDs [ ] uint , t time . Time ) error {
if len ( hostIDs ) == 0 {
return nil
}
if err := d . withRetryTxx ( func ( tx * sqlx . Tx ) error {
query := `
UPDATE hosts SET
seen_time = ?
WHERE id IN ( ? )
`
query , args , err := sqlx . In ( query , t , hostIDs )
if err != nil {
return errors . Wrap ( err , "sqlx in" )
}
query = d . db . Rebind ( query )
if _ , err := d . db . Exec ( query , args ... ) ; err != nil {
return errors . Wrap ( err , "exec update" )
}
return nil
} ) ; err != nil {
return errors . Wrap ( err , "MarkHostsSeen transaction" )
}
return nil
}
2021-05-25 04:34:08 +00:00
func ( d * Datastore ) searchHostsWithOmits ( filter kolide . TeamFilter , query string , omit ... uint ) ( [ ] * kolide . Host , error ) {
2019-10-16 17:12:35 +00:00
hostQuery := transformQuery ( query )
2016-12-05 19:16:23 +00:00
ipQuery := ` " ` + query + ` " `
2016-12-01 17:00:00 +00:00
2021-05-25 04:34:08 +00:00
sql := fmt . Sprintf ( `
SELECT DISTINCT *
FROM hosts
WHERE
(
MATCH ( host_name , uuid ) AGAINST ( ? IN BOOLEAN MODE )
OR MATCH ( primary_ip , primary_mac ) AGAINST ( ? IN BOOLEAN MODE )
)
AND id NOT IN ( ? ) AND % s
LIMIT 10
` , d . whereFilterHostsByTeams ( filter , "hosts" ) ,
)
2016-12-05 19:16:23 +00:00
2021-05-25 04:34:08 +00:00
sql , args , err := sqlx . In ( sql , hostQuery , ipQuery , omit )
2016-12-05 19:16:23 +00:00
if err != nil {
2017-01-04 21:16:17 +00:00
return nil , errors . Wrap ( err , "searching hosts" )
2016-11-16 13:47:49 +00:00
}
2016-12-05 19:16:23 +00:00
sql = d . db . Rebind ( sql )
2016-11-16 13:47:49 +00:00
2016-12-06 19:51:11 +00:00
hosts := [ ] * kolide . Host { }
2016-12-05 19:16:23 +00:00
err = d . db . Select ( & hosts , sql , args ... )
if err != nil {
2017-01-04 21:16:17 +00:00
return nil , errors . Wrap ( err , "searching hosts rebound" )
2016-11-16 13:47:49 +00:00
}
return hosts , nil
}
2021-05-25 04:34:08 +00:00
func ( d * Datastore ) searchHostsDefault ( filter kolide . TeamFilter , omit ... uint ) ( [ ] * kolide . Host , error ) {
sql := fmt . Sprintf ( `
SELECT * FROM hosts
WHERE id NOT in ( ? ) AND % s
ORDER BY seen_time DESC
LIMIT 5
` , d . whereFilterHostsByTeams ( filter , "hosts" ) ,
)
2017-01-17 14:51:04 +00:00
var in interface { }
{
// use -1 if there are no values to omit.
2020-10-22 17:51:26 +00:00
// Avoids empty args error for `sqlx.In`
2017-01-17 14:51:04 +00:00
in = omit
if len ( omit ) == 0 {
in = - 1
}
}
var hosts [ ] * kolide . Host
2021-05-25 04:34:08 +00:00
sql , args , err := sqlx . In ( sql , in )
2017-01-17 14:51:04 +00:00
if err != nil {
return nil , errors . Wrap ( err , "searching default hosts" )
}
sql = d . db . Rebind ( sql )
err = d . db . Select ( & hosts , sql , args ... )
if err != nil {
return nil , errors . Wrap ( err , "searching default hosts rebound" )
}
return hosts , nil
}
2019-10-16 17:12:35 +00:00
// SearchHosts find hosts by query containing an IP address, a host name or UUID.
// Optionally pass a list of IDs to omit from the search
2021-05-25 04:34:08 +00:00
func ( d * Datastore ) SearchHosts ( filter kolide . TeamFilter , query string , omit ... uint ) ( [ ] * kolide . Host , error ) {
2019-10-16 17:12:35 +00:00
hostQuery := transformQuery ( query )
if ! queryMinLength ( hostQuery ) {
2021-05-25 04:34:08 +00:00
return d . searchHostsDefault ( filter , omit ... )
2017-01-17 14:51:04 +00:00
}
2016-11-16 13:47:49 +00:00
if len ( omit ) > 0 {
2021-05-25 04:34:08 +00:00
return d . searchHostsWithOmits ( filter , query , omit ... )
2016-11-16 13:47:49 +00:00
}
2016-12-05 19:16:23 +00:00
// Needs quotes to avoid each . marking a word boundary
ipQuery := ` " ` + query + ` " `
2016-12-01 17:00:00 +00:00
2021-05-25 04:34:08 +00:00
sql := fmt . Sprintf ( `
SELECT DISTINCT *
FROM hosts
WHERE
(
MATCH ( host_name , uuid ) AGAINST ( ? IN BOOLEAN MODE )
OR MATCH ( primary_ip , primary_mac ) AGAINST ( ? IN BOOLEAN MODE )
) AND % s
LIMIT 10
` , d . whereFilterHostsByTeams ( filter , "hosts" ) ,
)
2016-11-16 13:47:49 +00:00
2021-05-25 04:34:08 +00:00
hosts := [ ] * kolide . Host { }
if err := d . db . Select ( & hosts , sql , hostQuery , ipQuery ) ; err != nil {
2017-01-04 21:16:17 +00:00
return nil , errors . Wrap ( err , "searching hosts" )
2016-11-16 13:47:49 +00:00
}
2016-12-06 19:51:11 +00:00
2016-11-16 13:47:49 +00:00
return hosts , nil
}
2018-05-17 22:54:34 +00:00
func ( d * Datastore ) HostIDsByName ( hostnames [ ] string ) ( [ ] uint , error ) {
2020-01-24 05:27:20 +00:00
if len ( hostnames ) == 0 {
return [ ] uint { } , nil
}
2018-05-17 22:54:34 +00:00
sqlStatement := `
SELECT id FROM hosts
WHERE host_name IN ( ? )
`
sql , args , err := sqlx . In ( sqlStatement , hostnames )
if err != nil {
return nil , errors . Wrap ( err , "building query to get host IDs" )
}
var hostIDs [ ] uint
if err := d . db . Select ( & hostIDs , sql , args ... ) ; err != nil {
return nil , errors . Wrap ( err , "get host IDs" )
}
return hostIDs , nil
}
2020-04-22 20:54:32 +00:00
func ( d * Datastore ) HostByIdentifier ( identifier string ) ( * kolide . Host , error ) {
sql := `
SELECT * FROM hosts
WHERE ? IN ( host_name , osquery_host_id , node_key , uuid )
LIMIT 1
`
host := & kolide . Host { }
err := d . db . Get ( host , sql , identifier )
if err != nil {
return nil , errors . Wrap ( err , "get host by identifier" )
}
2021-05-07 04:05:09 +00:00
if err := d . loadHostPackStats ( host ) ; err != nil {
return nil , err
}
2020-04-22 20:54:32 +00:00
return host , nil
}
2021-05-17 19:23:21 +00:00
func ( d * Datastore ) AddHostsToTeam ( teamID * uint , hostIDs [ ] uint ) error {
if len ( hostIDs ) == 0 {
return nil
}
sql := `
UPDATE hosts SET team_id = ?
WHERE id IN ( ? )
`
sql , args , err := sqlx . In ( sql , teamID , hostIDs )
if err != nil {
return errors . Wrap ( err , "sqlx.In AddHostsToTeam" )
}
if _ , err := d . db . Exec ( sql , args ... ) ; err != nil {
return errors . Wrap ( err , "exec AddHostsToTeam" )
}
return nil
}