mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
156 lines
4.9 KiB
Go
156 lines
4.9 KiB
Go
package launcher
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/go-kit/kit/log"
|
|
"github.com/kolide/osquery-go/plugin/distributed"
|
|
"github.com/kolide/osquery-go/plugin/logger"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/fleetdm/fleet/server/contexts/host"
|
|
"github.com/fleetdm/fleet/server/health"
|
|
"github.com/fleetdm/fleet/server/kolide"
|
|
)
|
|
|
|
// launcherWrapper wraps the TLS interface.
|
|
type launcherWrapper struct {
|
|
tls kolide.OsqueryService
|
|
logger log.Logger
|
|
healthCheckers map[string]health.Checker
|
|
}
|
|
|
|
func (svc *launcherWrapper) RequestEnrollment(ctx context.Context, enrollSecret, hostIdentifier string) (string, bool, error) {
|
|
nodeKey, err := svc.tls.EnrollAgent(ctx, enrollSecret, hostIdentifier, map[string](map[string]string){})
|
|
if err != nil {
|
|
if authErr, ok := err.(nodeInvalidErr); ok {
|
|
return "", authErr.NodeInvalid(), err
|
|
}
|
|
return "", false, err
|
|
}
|
|
return nodeKey, false, nil
|
|
}
|
|
|
|
func (svc *launcherWrapper) RequestConfig(ctx context.Context, nodeKey string) (string, bool, error) {
|
|
newCtx, invalid, err := svc.authenticateHost(ctx, nodeKey)
|
|
if err != nil {
|
|
return "", invalid, err
|
|
}
|
|
|
|
config, err := svc.tls.GetClientConfig(newCtx)
|
|
if err != nil {
|
|
return "", false, errors.Wrap(err, "get config for launcher")
|
|
}
|
|
|
|
if options, ok := config["options"].(map[string]interface{}); ok {
|
|
// Launcher manages plugins so remove them from configuration if they exist.
|
|
for _, optionName := range []string{"distributed_plugin", "logger_plugin"} {
|
|
delete(options, optionName)
|
|
}
|
|
}
|
|
|
|
configJSON, err := json.Marshal(config)
|
|
if err != nil {
|
|
return "", false, errors.Wrap(err, "encoding config for launcher")
|
|
}
|
|
|
|
return string(configJSON), false, nil
|
|
}
|
|
|
|
func (svc *launcherWrapper) RequestQueries(ctx context.Context, nodeKey string) (*distributed.GetQueriesResult, bool, error) {
|
|
newCtx, invalid, err := svc.authenticateHost(ctx, nodeKey)
|
|
if err != nil {
|
|
return nil, invalid, err
|
|
}
|
|
|
|
queryMap, accelerate, err := svc.tls.GetDistributedQueries(newCtx)
|
|
if err != nil {
|
|
return nil, false, errors.Wrap(err, "get queries for launcher")
|
|
}
|
|
|
|
result := &distributed.GetQueriesResult{
|
|
Queries: queryMap,
|
|
AccelerateSeconds: int(accelerate),
|
|
}
|
|
|
|
return result, false, nil
|
|
}
|
|
|
|
func (svc *launcherWrapper) PublishLogs(ctx context.Context, nodeKey string, logType logger.LogType, logs []string) (string, string, bool, error) {
|
|
newCtx, invalid, err := svc.authenticateHost(ctx, nodeKey)
|
|
if err != nil {
|
|
return "", "", invalid, errors.Wrap(err, "authenticate launcher")
|
|
}
|
|
|
|
switch logType {
|
|
case logger.LogTypeStatus:
|
|
var statuses []json.RawMessage
|
|
for _, log := range logs {
|
|
statuses = append(statuses, []byte(log))
|
|
}
|
|
err = svc.tls.SubmitStatusLogs(newCtx, statuses)
|
|
return "", "", false, errors.Wrap(err, "submit status logs from launcher")
|
|
case logger.LogTypeSnapshot, logger.LogTypeString:
|
|
var results []json.RawMessage
|
|
for _, log := range logs {
|
|
results = append(results, []byte(log))
|
|
}
|
|
err = svc.tls.SubmitResultLogs(newCtx, results)
|
|
return "", "", false, errors.Wrap(err, "submit result logs from launcher")
|
|
default:
|
|
// We have a logTypeAgent which is not there in the osquery-go enum.
|
|
// See https://github.com/kolide/launcher/issues/183
|
|
panic(fmt.Sprintf("%s log type not implemented", logType))
|
|
}
|
|
}
|
|
|
|
func (svc *launcherWrapper) PublishResults(ctx context.Context, nodeKey string, results []distributed.Result) (string, string, bool, error) {
|
|
newCtx, invalid, err := svc.authenticateHost(ctx, nodeKey)
|
|
if err != nil {
|
|
return "", "", invalid, err
|
|
}
|
|
|
|
osqueryResults := make(kolide.OsqueryDistributedQueryResults, len(results))
|
|
statuses := make(map[string]kolide.OsqueryStatus, len(results))
|
|
|
|
for _, result := range results {
|
|
statuses[result.QueryName] = kolide.OsqueryStatus(result.Status)
|
|
osqueryResults[result.QueryName] = result.Rows
|
|
}
|
|
|
|
err = svc.tls.SubmitDistributedQueryResults(newCtx, osqueryResults, statuses)
|
|
return "", "", false, errors.Wrap(err, "submit launcher results")
|
|
}
|
|
|
|
func (svc *launcherWrapper) CheckHealth(ctx context.Context) (int32, error) {
|
|
healthy := health.CheckHealth(svc.logger, svc.healthCheckers)
|
|
if !healthy {
|
|
return 1, nil
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
// authenticateHost verifies the host node key using the TLS API and returns back a
|
|
// context which includes the host as a context value.
|
|
// In the kolide.OsqueryService authentication is done via endpoint middleware, but all launcher endpoints require
|
|
// an explicit return for NodeInvalid, so we check in this helper method instead.
|
|
func (svc *launcherWrapper) authenticateHost(ctx context.Context, nodeKey string) (context.Context, bool, error) {
|
|
node, err := svc.tls.AuthenticateHost(ctx, nodeKey)
|
|
if err != nil {
|
|
if authErr, ok := err.(nodeInvalidErr); ok {
|
|
return ctx, authErr.NodeInvalid(), err
|
|
}
|
|
return ctx, false, err
|
|
}
|
|
|
|
ctx = host.NewContext(ctx, *node)
|
|
return ctx, false, nil
|
|
}
|
|
|
|
type nodeInvalidErr interface {
|
|
error
|
|
NodeInvalid() bool
|
|
}
|