mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
261b7f916c
When an osqueryd agent sends an enroll request it automatically sends some details about the system. We now save these details which helps ensure we send the correct platform config. Closes #2065
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/kolide/fleet/server/contexts/host"
|
|
"github.com/kolide/fleet/server/health"
|
|
"github.com/kolide/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
|
|
}
|