mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
134 lines
3.2 KiB
Go
134 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/AbGuthrie/goquery/v2"
|
|
gqconfig "github.com/AbGuthrie/goquery/v2/config"
|
|
gqhosts "github.com/AbGuthrie/goquery/v2/hosts"
|
|
gqmodels "github.com/AbGuthrie/goquery/v2/models"
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
"github.com/fleetdm/fleet/v4/server/service"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
type activeQuery struct {
|
|
status string
|
|
results []map[string]string
|
|
}
|
|
|
|
type goqueryClient struct {
|
|
client *service.Client
|
|
queryCounter int
|
|
queries map[string]activeQuery
|
|
// goquery passes the UUID, while we need the hostname (or ID) to
|
|
// query against Fleet. Keep a mapping so that we know how to target
|
|
// the host.
|
|
hostnameByUUID map[string]string
|
|
}
|
|
|
|
func newGoqueryClient(fleetClient *service.Client) *goqueryClient {
|
|
return &goqueryClient{
|
|
client: fleetClient,
|
|
queryCounter: 0,
|
|
queries: make(map[string]activeQuery),
|
|
hostnameByUUID: make(map[string]string),
|
|
}
|
|
}
|
|
|
|
func (c *goqueryClient) CheckHost(query string) (gqhosts.Host, error) {
|
|
res, err := c.client.SearchTargets(query, nil, nil)
|
|
if err != nil {
|
|
return gqhosts.Host{}, err
|
|
}
|
|
|
|
var host *fleet.Host
|
|
for _, h := range res.Hosts {
|
|
// We allow hosts to be looked up by hostname in addition to UUID
|
|
if query == h.UUID || query == h.Hostname || query == h.ComputerName {
|
|
host = h
|
|
break
|
|
}
|
|
}
|
|
|
|
if host == nil {
|
|
return gqhosts.Host{}, fmt.Errorf("host %s not found", query)
|
|
}
|
|
|
|
c.hostnameByUUID[host.UUID] = host.Hostname
|
|
|
|
return gqhosts.Host{
|
|
UUID: host.UUID,
|
|
ComputerName: host.ComputerName,
|
|
Platform: host.Platform,
|
|
Version: host.OsqueryVersion,
|
|
}, nil
|
|
}
|
|
|
|
func (c *goqueryClient) ScheduleQuery(uuid, query string) (string, error) {
|
|
c.queryCounter++
|
|
queryName := strconv.Itoa(c.queryCounter)
|
|
|
|
hostname, ok := c.hostnameByUUID[uuid]
|
|
if !ok {
|
|
return "", errors.New("could not lookup host")
|
|
}
|
|
|
|
res, err := c.client.LiveQuery(query, []string{}, []string{hostname})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
c.queries[queryName] = activeQuery{status: "Pending"}
|
|
|
|
// We need to start a separate thread due to goquery expecting
|
|
// scheduling a query and retrieving results to be separate
|
|
// operations.
|
|
go func() {
|
|
select {
|
|
case hostResult := <-res.Results():
|
|
c.queries[queryName] = activeQuery{status: "Completed", results: hostResult.Rows}
|
|
|
|
// Print an error
|
|
case err := <-res.Errors():
|
|
c.queries[queryName] = activeQuery{status: "error: " + err.Error()}
|
|
}
|
|
}()
|
|
|
|
gqhosts.AddQueryToHost(uuid, gqhosts.Query{Name: queryName, SQL: query})
|
|
return queryName, nil
|
|
}
|
|
|
|
func (c *goqueryClient) FetchResults(queryName string) (gqmodels.Rows, string, error) {
|
|
res, ok := c.queries[queryName]
|
|
if !ok {
|
|
return nil, "", fmt.Errorf("Unknown query %s", queryName)
|
|
}
|
|
|
|
return res.results, res.status, nil
|
|
}
|
|
|
|
func goqueryCommand() *cli.Command {
|
|
return &cli.Command{
|
|
Name: "goquery",
|
|
Usage: "Start the goquery interface",
|
|
Flags: []cli.Flag{
|
|
configFlag(),
|
|
contextFlag(),
|
|
yamlFlag(),
|
|
debugFlag(),
|
|
},
|
|
Action: func(c *cli.Context) error {
|
|
fleet, err := clientFromCLI(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
goquery.Run(newGoqueryClient(fleet), gqconfig.Config{})
|
|
return nil
|
|
},
|
|
}
|
|
}
|