mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Update agent options storage for teams (#754)
- Allow agent options to be set on per-team basis. - Move global agent options into app configs. - Update logic for calculating agent options for hosts. - Updates to relevant testing.
This commit is contained in:
parent
40f2452e46
commit
b1a98a6e91
@ -27,7 +27,6 @@ type specGroup struct {
|
||||
Queries []*kolide.QuerySpec
|
||||
Packs []*kolide.PackSpec
|
||||
Labels []*kolide.LabelSpec
|
||||
Options *kolide.OptionsSpec
|
||||
AppConfig *kolide.AppConfigPayload
|
||||
EnrollSecret *kolide.EnrollSecretSpec
|
||||
}
|
||||
@ -73,17 +72,6 @@ func specGroupFromBytes(b []byte) (*specGroup, error) {
|
||||
}
|
||||
specs.Labels = append(specs.Labels, labelSpec)
|
||||
|
||||
case kolide.OptionsKind:
|
||||
if specs.Options != nil {
|
||||
return nil, errors.New("options defined twice in the same file")
|
||||
}
|
||||
|
||||
var optionSpec *kolide.OptionsSpec
|
||||
if err := yaml.Unmarshal(s.Spec, &optionSpec); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshaling "+kind+" spec")
|
||||
}
|
||||
specs.Options = optionSpec
|
||||
|
||||
case kolide.AppConfigKind:
|
||||
if specs.AppConfig != nil {
|
||||
return nil, errors.New("config defined twice in the same file")
|
||||
@ -175,13 +163,6 @@ func applyCommand() *cli.Command {
|
||||
fmt.Printf("[+] applied %d packs\n", len(specs.Packs))
|
||||
}
|
||||
|
||||
if specs.Options != nil {
|
||||
if err := fleet.ApplyOptions(specs.Options); err != nil {
|
||||
return errors.Wrap(err, "applying options")
|
||||
}
|
||||
fmt.Printf("[+] applied options\n")
|
||||
}
|
||||
|
||||
if specs.AppConfig != nil {
|
||||
if err := fleet.ApplyAppConfig(specs.AppConfig); err != nil {
|
||||
return errors.Wrap(err, "applying fleet config")
|
||||
|
@ -120,24 +120,6 @@ func printPack(c *cli.Context, pack *kolide.PackSpec) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func printOption(c *cli.Context, option *kolide.OptionsSpec) error {
|
||||
spec := specGeneric{
|
||||
Kind: kolide.OptionsKind,
|
||||
Version: kolide.ApiVersion,
|
||||
Spec: option,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if c.Bool(jsonFlagName) {
|
||||
err = printJSON(spec)
|
||||
} else {
|
||||
err = printYaml(spec)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func printSecret(c *cli.Context, secret *kolide.EnrollSecretSpec) error {
|
||||
spec := specGeneric{
|
||||
Kind: kolide.EnrollSecretKind,
|
||||
@ -209,7 +191,6 @@ func getCommand() *cli.Command {
|
||||
getQueriesCommand(),
|
||||
getPacksCommand(),
|
||||
getLabelsCommand(),
|
||||
getOptionsCommand(),
|
||||
getHostsCommand(),
|
||||
getEnrollSecretCommand(),
|
||||
getAppConfigCommand(),
|
||||
@ -484,38 +465,6 @@ func getLabelsCommand() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func getOptionsCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "options",
|
||||
Usage: "Retrieve the osquery configuration",
|
||||
Flags: []cli.Flag{
|
||||
jsonFlag(),
|
||||
yamlFlag(),
|
||||
configFlag(),
|
||||
contextFlag(),
|
||||
debugFlag(),
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
fleet, err := clientFromCLI(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options, err := fleet.GetOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = printOption(c, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getEnrollSecretCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "enroll_secret",
|
||||
|
@ -79,9 +79,6 @@ var TestFunctions = [...]func(*testing.T, kolide.Datastore){
|
||||
testCountHostsInTargets,
|
||||
testHostStatus,
|
||||
testHostIDsInTargets,
|
||||
testApplyOsqueryOptions,
|
||||
testApplyOsqueryOptionsNoOverrides,
|
||||
testOsqueryOptionsForHost,
|
||||
testApplyQueries,
|
||||
testApplyPackSpecRoundtrip,
|
||||
testApplyPackSpecMissingQueries,
|
||||
|
@ -1,95 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testApplyOsqueryOptions(t *testing.T, ds kolide.Datastore) {
|
||||
expectedOpts := &kolide.OptionsSpec{
|
||||
Config: json.RawMessage(`{"foo": "bar"}`),
|
||||
Overrides: kolide.OptionsOverrides{
|
||||
Platforms: map[string]json.RawMessage{
|
||||
"darwin": json.RawMessage(`{"froob": "ling"}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := ds.ApplyOptions(expectedOpts)
|
||||
require.Nil(t, err)
|
||||
|
||||
retrievedOpts, err := ds.GetOptions()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, expectedOpts, retrievedOpts)
|
||||
|
||||
// Re-apply and verify everything has been replaced.
|
||||
expectedOpts = &kolide.OptionsSpec{
|
||||
Config: json.RawMessage(`{"blue": "smurf"}`),
|
||||
Overrides: kolide.OptionsOverrides{
|
||||
Platforms: map[string]json.RawMessage{
|
||||
"linux": json.RawMessage(`{"transitive": "nightfall"}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = ds.ApplyOptions(expectedOpts)
|
||||
require.Nil(t, err)
|
||||
|
||||
retrievedOpts, err = ds.GetOptions()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, expectedOpts, retrievedOpts)
|
||||
}
|
||||
|
||||
func testApplyOsqueryOptionsNoOverrides(t *testing.T, ds kolide.Datastore) {
|
||||
expectedOpts := &kolide.OptionsSpec{
|
||||
Config: json.RawMessage(`{}`),
|
||||
}
|
||||
|
||||
err := ds.ApplyOptions(expectedOpts)
|
||||
require.Nil(t, err)
|
||||
|
||||
retrievedOpts, err := ds.GetOptions()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, expectedOpts.Config, retrievedOpts.Config)
|
||||
assert.Empty(t, retrievedOpts.Overrides.Platforms)
|
||||
}
|
||||
|
||||
func testOsqueryOptionsForHost(t *testing.T, ds kolide.Datastore) {
|
||||
defaultOpts := json.RawMessage(`{"foo": "bar"}`)
|
||||
darwinOpts := json.RawMessage(`{"darwin": "macintosh"}`)
|
||||
linuxOpts := json.RawMessage(`{"linux": "FOSS"}`)
|
||||
expectedOpts := &kolide.OptionsSpec{
|
||||
Config: defaultOpts,
|
||||
Overrides: kolide.OptionsOverrides{
|
||||
Platforms: map[string]json.RawMessage{
|
||||
"darwin": darwinOpts,
|
||||
"linux": linuxOpts,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := ds.ApplyOptions(expectedOpts)
|
||||
require.Nil(t, err)
|
||||
|
||||
var testCases = []struct {
|
||||
host kolide.Host
|
||||
expectedOpts json.RawMessage
|
||||
}{
|
||||
{kolide.Host{Platform: "windows"}, defaultOpts},
|
||||
{kolide.Host{Platform: "linux"}, linuxOpts},
|
||||
{kolide.Host{Platform: "darwin"}, darwinOpts},
|
||||
{kolide.Host{Platform: "some_other_platform"}, defaultOpts},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
opts, err := ds.OptionsForPlatform(tt.host.Platform)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, tt.expectedOpts, opts)
|
||||
})
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/jmoiron/sqlx/reflectx"
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -153,7 +152,7 @@ func migrateOptions(tx *sql.Tx) error {
|
||||
override_type, override_identifier, options
|
||||
) VALUES (?, ?, ?)
|
||||
`
|
||||
if _, err = txx.Exec(query, kolide.OptionOverrideTypeDefault, "", string(confJSON)); err != nil {
|
||||
if _, err = txx.Exec(query, 0, "", string(confJSON)); err != nil {
|
||||
return errors.Wrap(err, "saving converted options")
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,85 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/jmoiron/sqlx/reflectx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func init() {
|
||||
MigrationClient.AddMigration(Up_20210510111225, Down_20210510111225)
|
||||
}
|
||||
|
||||
func Up_20210510111225(tx *sql.Tx) error {
|
||||
existingOptions, err := copyOptions(tx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get existing options")
|
||||
}
|
||||
|
||||
sql := `
|
||||
ALTER TABLE app_configs
|
||||
ADD COLUMN agent_options JSON
|
||||
`
|
||||
if _, err := tx.Exec(sql); err != nil {
|
||||
return errors.Wrap(err, "add column agent_options")
|
||||
}
|
||||
|
||||
sql = `UPDATE app_configs SET agent_options = ?`
|
||||
if _, err := tx.Exec(sql, existingOptions); err != nil {
|
||||
return errors.Wrap(err, "insert existing options")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Below code copied and adapted from osquery options code removed in this commit.
|
||||
|
||||
func copyOptions(tx *sql.Tx) (json.RawMessage, error) {
|
||||
// Migrate pre teams osquery options to the new osquery option storage in app config.
|
||||
txx := sqlx.Tx{Tx: tx, Mapper: reflectx.NewMapperFunc("db", sqlx.NameMapper)}
|
||||
|
||||
var rows []optionsRow
|
||||
if err := txx.Select(&rows, "SELECT * FROM osquery_options"); err != nil {
|
||||
return nil, errors.Wrap(err, "selecting options")
|
||||
}
|
||||
|
||||
opt := &kolide.AgentOptions{
|
||||
Overrides: kolide.AgentOptionsOverrides{
|
||||
Platforms: make(map[string]json.RawMessage),
|
||||
},
|
||||
}
|
||||
for _, row := range rows {
|
||||
switch row.OverrideType {
|
||||
case 0: // was kolide.OptionOverrideTypeDefault
|
||||
opt.Config = json.RawMessage(row.Options)
|
||||
|
||||
case 1: // was kolide.OptionOverrideTypePlatform
|
||||
opt.Overrides.Platforms[row.OverrideIdentifier] = json.RawMessage(row.Options)
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("unknown override type: %d", row.OverrideType)
|
||||
}
|
||||
}
|
||||
|
||||
jsonVal, err := json.Marshal(opt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "marshal options")
|
||||
}
|
||||
|
||||
return jsonVal, nil
|
||||
}
|
||||
|
||||
type optionsRow struct {
|
||||
ID int `db:"id"`
|
||||
OverrideType int `db:"override_type"`
|
||||
OverrideIdentifier string `db:"override_identifier"`
|
||||
Options string `db:"options"`
|
||||
}
|
||||
|
||||
func Down_20210510111225(tx *sql.Tx) error {
|
||||
return nil
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-kit/kit/log/level"
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type optionsRow struct {
|
||||
ID int `db:"id"`
|
||||
OverrideType kolide.OptionOverrideType `db:"override_type"`
|
||||
OverrideIdentifier string `db:"override_identifier"`
|
||||
Options string `db:"options"`
|
||||
}
|
||||
|
||||
func (d *Datastore) ApplyOptions(spec *kolide.OptionsSpec) (err error) {
|
||||
tx, err := d.db.Begin()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "begin ApplyOptions transaction")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
rbErr := tx.Rollback()
|
||||
// It seems possible that there might be a case in
|
||||
// which the error we are dealing with here was thrown
|
||||
// by the call to tx.Commit(), and the docs suggest
|
||||
// this call would then result in sql.ErrTxDone.
|
||||
if rbErr != nil && rbErr != sql.ErrTxDone {
|
||||
panic(fmt.Sprintf("got err '%s' rolling back after err '%s'", rbErr, err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Clear all the existing options
|
||||
_, err = tx.Exec("DELETE FROM osquery_options")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "delete existing options")
|
||||
}
|
||||
|
||||
// Save new options
|
||||
sql := `
|
||||
INSERT INTO osquery_options (
|
||||
override_type, override_identifier, options
|
||||
) VALUES (?, ?, ?)
|
||||
`
|
||||
|
||||
// Default options
|
||||
_, err = tx.Exec(sql, kolide.OptionOverrideTypeDefault, "", string(spec.Config))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "saving default config")
|
||||
}
|
||||
|
||||
// Platform overrides
|
||||
for platform, opts := range spec.Overrides.Platforms {
|
||||
_, err = tx.Exec(sql, kolide.OptionOverrideTypePlatform, platform, string(opts))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "saving %s platform config", platform)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Success!
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "commit ApplyOptions transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) GetOptions() (*kolide.OptionsSpec, error) {
|
||||
var rows []optionsRow
|
||||
if err := d.db.Select(&rows, "SELECT * FROM osquery_options"); err != nil {
|
||||
return nil, errors.Wrap(err, "selecting options")
|
||||
}
|
||||
|
||||
spec := &kolide.OptionsSpec{
|
||||
Overrides: kolide.OptionsOverrides{
|
||||
Platforms: make(map[string]json.RawMessage),
|
||||
},
|
||||
}
|
||||
for _, row := range rows {
|
||||
switch row.OverrideType {
|
||||
case kolide.OptionOverrideTypeDefault:
|
||||
spec.Config = json.RawMessage(row.Options)
|
||||
|
||||
case kolide.OptionOverrideTypePlatform:
|
||||
spec.Overrides.Platforms[row.OverrideIdentifier] = json.RawMessage(row.Options)
|
||||
|
||||
default:
|
||||
level.Info(d.logger).Log(
|
||||
"err", "ignoring unkown override type",
|
||||
"type", row.OverrideType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) OptionsForPlatform(platform string) (json.RawMessage, error) {
|
||||
// SQL uses a custom ordering function to return the single correct
|
||||
// config with the highest precedence override (the FIELD function
|
||||
// defines this ordering). If there is no override, it returns the
|
||||
// default.
|
||||
sql := `
|
||||
SELECT * FROM osquery_options
|
||||
WHERE override_type = ? OR
|
||||
(override_type = ? AND override_identifier = ?)
|
||||
ORDER BY FIELD(override_type, ?, ?)
|
||||
LIMIT 1
|
||||
`
|
||||
var row optionsRow
|
||||
err := d.db.Get(
|
||||
&row, sql,
|
||||
kolide.OptionOverrideTypeDefault,
|
||||
kolide.OptionOverrideTypePlatform, platform,
|
||||
// Order of the following arguments defines precedence of
|
||||
// overrides.
|
||||
kolide.OptionOverrideTypePlatform, kolide.OptionOverrideTypeDefault,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "retrieving osquery options for platform '%s'", platform)
|
||||
}
|
||||
|
||||
return json.RawMessage(row.Options), nil
|
||||
}
|
36
server/kolide/agent_options.go
Normal file
36
server/kolide/agent_options.go
Normal file
@ -0,0 +1,36 @@
|
||||
package kolide
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type AgentOptionsService interface {
|
||||
// AgentOptionsForHost gets the agent options for the provided host.
|
||||
//
|
||||
// The host information should be used for filtering based on team,
|
||||
// platform, etc.
|
||||
AgentOptionsForHost(ctx context.Context, host *Host) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
type AgentOptions struct {
|
||||
// Config is the base config options.
|
||||
Config json.RawMessage `json:"config"`
|
||||
// Overrides includes any platform-based overrides.
|
||||
Overrides AgentOptionsOverrides `json:"overrides,omitempty"`
|
||||
}
|
||||
|
||||
type AgentOptionsOverrides struct {
|
||||
// Platforms is a map from platform name to the config override.
|
||||
Platforms map[string]json.RawMessage `json:"platforms,omitempty"`
|
||||
}
|
||||
|
||||
func (o *AgentOptions) ForPlatform(platform string) json.RawMessage {
|
||||
// Return matching platform override if available.
|
||||
if opt, ok := o.Overrides.Platforms[platform]; ok {
|
||||
return opt
|
||||
}
|
||||
|
||||
// Otherwise return base config for team.
|
||||
return o.Config
|
||||
}
|
@ -167,6 +167,9 @@ type AppConfig struct {
|
||||
// AdditionalQueries is the set of additional queries that should be run
|
||||
// when collecting details from hosts.
|
||||
AdditionalQueries *json.RawMessage `db:"additional_queries"`
|
||||
|
||||
// AgentOptions is the global agent options, including overrides.
|
||||
AgentOptions json.RawMessage `db:"agent_options"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -14,7 +14,6 @@ type Datastore interface {
|
||||
AppConfigStore
|
||||
InviteStore
|
||||
ScheduledQueryStore
|
||||
OsqueryOptionsStore
|
||||
CarveStore
|
||||
TeamStore
|
||||
SoftwareStore
|
||||
|
@ -1,50 +0,0 @@
|
||||
package kolide
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type OsqueryOptionsStore interface {
|
||||
ApplyOptions(options *OptionsSpec) error
|
||||
GetOptions() (*OptionsSpec, error)
|
||||
OptionsForPlatform(platform string) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
type OsqueryOptionsService interface {
|
||||
ApplyOptionsSpec(ctx context.Context, spec *OptionsSpec) error
|
||||
GetOptionsSpec(ctx context.Context) (*OptionsSpec, error)
|
||||
}
|
||||
|
||||
type OptionsObject struct {
|
||||
ObjectMetadata
|
||||
Spec OptionsSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type OptionsSpec struct {
|
||||
Config json.RawMessage `json:"config"`
|
||||
Overrides OptionsOverrides `json:"overrides,omitempty"`
|
||||
}
|
||||
|
||||
type OptionsOverrides struct {
|
||||
Platforms map[string]json.RawMessage `json:"platforms,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
OptionsKind = "options"
|
||||
)
|
||||
|
||||
// OptionOverrideType is used to designate which override type a given set of
|
||||
// options is used for. Currently the only supported override type is by
|
||||
// platform.
|
||||
type OptionOverrideType int
|
||||
|
||||
const (
|
||||
// OptionOverrideTypeDefault indicates that this is the default config
|
||||
// (provided to hosts when there is no override set for them).
|
||||
OptionOverrideTypeDefault OptionOverrideType = iota
|
||||
// OptionOverrideTypePlatform indicates that this is a
|
||||
// platform-specific config override (with precedence over the default
|
||||
// config).
|
||||
OptionOverrideTypePlatform
|
||||
)
|
@ -1,119 +0,0 @@
|
||||
package kolide
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnmarshalYaml(t *testing.T) {
|
||||
y := []byte(`
|
||||
apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryOptions
|
||||
spec:
|
||||
config:
|
||||
options:
|
||||
distributed_interval: 3
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 10
|
||||
overrides:
|
||||
platforms:
|
||||
darwin:
|
||||
options:
|
||||
distributed_interval: 10
|
||||
distributed_tls_max_attempts: 10
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 300
|
||||
disable_tables: chrome_extensions
|
||||
docker_socket: /var/run/docker.sock
|
||||
file_paths:
|
||||
users:
|
||||
- /Users/%/Library/%%
|
||||
- /Users/%/Documents/%%
|
||||
etc:
|
||||
- /etc/%%
|
||||
linux:
|
||||
options:
|
||||
distributed_interval: 10
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 60
|
||||
schedule_timeout: 60
|
||||
docker_socket: /etc/run/docker.sock
|
||||
frobulations:
|
||||
- fire
|
||||
- ice
|
||||
`)
|
||||
|
||||
expectedConfig := `{
|
||||
"options":{
|
||||
"distributed_interval":3,
|
||||
"distributed_tls_max_attempts":3,
|
||||
"logger_plugin":"tls",
|
||||
"logger_tls_endpoint":"/api/v1/osquery/log",
|
||||
"logger_tls_period":10
|
||||
}
|
||||
}`
|
||||
|
||||
expectedDarwin := `{
|
||||
"options":{
|
||||
"disable_tables":"chrome_extensions",
|
||||
"distributed_interval":10,
|
||||
"distributed_tls_max_attempts":10,
|
||||
"docker_socket":"/var/run/docker.sock",
|
||||
"logger_plugin":"tls",
|
||||
"logger_tls_endpoint":"/api/v1/osquery/log",
|
||||
"logger_tls_period":300
|
||||
},
|
||||
"file_paths":{
|
||||
"etc":[
|
||||
"/etc/%%"
|
||||
],
|
||||
"users":[
|
||||
"/Users/%/Library/%%",
|
||||
"/Users/%/Documents/%%"
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
expectedLinux := `{
|
||||
"options":{
|
||||
"distributed_interval":10,
|
||||
"distributed_tls_max_attempts":3,
|
||||
"docker_socket":"/etc/run/docker.sock",
|
||||
"logger_plugin":"tls",
|
||||
"logger_tls_endpoint":"/api/v1/osquery/log",
|
||||
"logger_tls_period":60,
|
||||
"schedule_timeout":60
|
||||
},
|
||||
"frobulations": [
|
||||
"fire",
|
||||
"ice"
|
||||
]
|
||||
}`
|
||||
|
||||
var foo OptionsObject
|
||||
err := yaml.Unmarshal(y, &foo)
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.JSONEq(t, expectedConfig, string(foo.Spec.Config))
|
||||
|
||||
platformOverrides := foo.Spec.Overrides.Platforms
|
||||
assert.Len(t, platformOverrides, 2)
|
||||
|
||||
if assert.Contains(t, platformOverrides, "darwin") {
|
||||
assert.JSONEq(t, expectedDarwin, string(platformOverrides["darwin"]))
|
||||
}
|
||||
|
||||
if assert.Contains(t, platformOverrides, "linux") {
|
||||
assert.JSONEq(t, expectedLinux, string(platformOverrides["linux"]))
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ type Service interface {
|
||||
QueryService
|
||||
CampaignService
|
||||
OsqueryService
|
||||
OsqueryOptionsService
|
||||
AgentOptionsService
|
||||
HostService
|
||||
AppConfigService
|
||||
InviteService
|
||||
|
@ -9,14 +9,14 @@ import (
|
||||
type TeamStore interface {
|
||||
// NewTeam creates a new Team object in the store.
|
||||
NewTeam(team *Team) (*Team, error)
|
||||
// SaveTeam saves any changes to the team.
|
||||
SaveTeam(team *Team) (*Team, error)
|
||||
// Team retrieves the Team by ID.
|
||||
Team(tid uint) (*Team, error)
|
||||
// Team deletes the Team by ID.
|
||||
DeleteTeam(tid uint) error
|
||||
// TeamByName retrieves the Team by Name.
|
||||
TeamByName(name string) (*Team, error)
|
||||
// SaveTeam saves any changes to the team.
|
||||
SaveTeam(team *Team) (*Team, error)
|
||||
// ListTeams lists teams with the ordering and filters in the provided
|
||||
// options.
|
||||
ListTeams(opt ListOptions) ([]*Team, error)
|
||||
|
@ -21,12 +21,11 @@ var _ kolide.Datastore = (*Store)(nil)
|
||||
|
||||
type Store struct {
|
||||
kolide.PasswordResetStore
|
||||
kolide.TeamStore
|
||||
TeamStore
|
||||
TargetStore
|
||||
SessionStore
|
||||
CampaignStore
|
||||
ScheduledQueryStore
|
||||
OsqueryOptionsStore
|
||||
AppConfigStore
|
||||
HostStore
|
||||
InviteStore
|
||||
|
@ -1,43 +0,0 @@
|
||||
// Automatically generated by mockimpl. DO NOT EDIT!
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
)
|
||||
|
||||
var _ kolide.OsqueryOptionsStore = (*OsqueryOptionsStore)(nil)
|
||||
|
||||
type ApplyOptionsFunc func(options *kolide.OptionsSpec) error
|
||||
|
||||
type GetOptionsFunc func() (*kolide.OptionsSpec, error)
|
||||
|
||||
type OptionsForPlatformFunc func(platform string) (json.RawMessage, error)
|
||||
|
||||
type OsqueryOptionsStore struct {
|
||||
ApplyOptionsFunc ApplyOptionsFunc
|
||||
ApplyOptionsFuncInvoked bool
|
||||
|
||||
GetOptionsFunc GetOptionsFunc
|
||||
GetOptionsFuncInvoked bool
|
||||
|
||||
OptionsForPlatformFunc OptionsForPlatformFunc
|
||||
OptionsForPlatformFuncInvoked bool
|
||||
}
|
||||
|
||||
func (s *OsqueryOptionsStore) ApplyOptions(options *kolide.OptionsSpec) error {
|
||||
s.ApplyOptionsFuncInvoked = true
|
||||
return s.ApplyOptionsFunc(options)
|
||||
}
|
||||
|
||||
func (s *OsqueryOptionsStore) GetOptions() (*kolide.OptionsSpec, error) {
|
||||
s.GetOptionsFuncInvoked = true
|
||||
return s.GetOptionsFunc()
|
||||
}
|
||||
|
||||
func (s *OsqueryOptionsStore) OptionsForPlatform(platform string) (json.RawMessage, error) {
|
||||
s.OptionsForPlatformFuncInvoked = true
|
||||
return s.OptionsForPlatformFunc(platform)
|
||||
}
|
71
server/mock/datastore_teams.go
Normal file
71
server/mock/datastore_teams.go
Normal file
@ -0,0 +1,71 @@
|
||||
// Automatically generated by mockimpl. DO NOT EDIT!
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
)
|
||||
|
||||
var _ kolide.TeamStore = (*TeamStore)(nil)
|
||||
|
||||
type NewTeamFunc func(team *kolide.Team) (*kolide.Team, error)
|
||||
|
||||
type SaveTeamFunc func(team *kolide.Team) (*kolide.Team, error)
|
||||
|
||||
type DeleteTeamFunc func(tid uint) error
|
||||
|
||||
type TeamFunc func(id uint) (*kolide.Team, error)
|
||||
|
||||
type TeamByNameFunc func(name string) (*kolide.Team, error)
|
||||
|
||||
type ListTeamsFunc func(opt kolide.ListOptions) ([]*kolide.Team, error)
|
||||
|
||||
type TeamStore struct {
|
||||
NewTeamFunc NewTeamFunc
|
||||
NewTeamFuncInvoked bool
|
||||
|
||||
SaveTeamFunc SaveTeamFunc
|
||||
SaveTeamFuncInvoked bool
|
||||
|
||||
DeleteTeamFunc DeleteTeamFunc
|
||||
DeleteTeamFuncInvoked bool
|
||||
|
||||
TeamFunc TeamFunc
|
||||
TeamFuncInvoked bool
|
||||
|
||||
TeamByNameFunc TeamByNameFunc
|
||||
TeamByNameFuncInvoked bool
|
||||
|
||||
ListTeamsFunc ListTeamsFunc
|
||||
ListTeamsFuncInvoked bool
|
||||
}
|
||||
|
||||
func (s *TeamStore) NewTeam(team *kolide.Team) (*kolide.Team, error) {
|
||||
s.NewTeamFuncInvoked = true
|
||||
return s.NewTeamFunc(team)
|
||||
}
|
||||
|
||||
func (s *TeamStore) SaveTeam(team *kolide.Team) (*kolide.Team, error) {
|
||||
s.SaveTeamFuncInvoked = true
|
||||
return s.SaveTeamFunc(team)
|
||||
}
|
||||
|
||||
func (s *TeamStore) DeleteTeam(tid uint) error {
|
||||
s.DeleteTeamFuncInvoked = true
|
||||
return s.DeleteTeamFunc(tid)
|
||||
}
|
||||
|
||||
func (s *TeamStore) Team(id uint) (*kolide.Team, error) {
|
||||
s.TeamFuncInvoked = true
|
||||
return s.TeamFunc(id)
|
||||
}
|
||||
|
||||
func (s *TeamStore) TeamByName(identifier string) (*kolide.Team, error) {
|
||||
s.TeamByNameFuncInvoked = true
|
||||
return s.TeamByNameFunc(identifier)
|
||||
}
|
||||
|
||||
func (s *TeamStore) ListTeams(opt kolide.ListOptions) ([]*kolide.Team, error) {
|
||||
s.ListTeamsFuncInvoked = true
|
||||
return s.ListTeamsFunc(opt)
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ApplyOptions sends the osquery options to be applied to the Fleet instance.
|
||||
func (c *Client) ApplyOptions(spec *kolide.OptionsSpec) error {
|
||||
req := applyOsqueryOptionsSpecRequest{Spec: spec}
|
||||
response, err := c.AuthenticatedDo("POST", "/api/v1/fleet/spec/osquery_options", "", req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "POST /api/v1/fleet/spec/osquery_options")
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return errors.Errorf(
|
||||
"apply options received status %d %s",
|
||||
response.StatusCode,
|
||||
extractServerErrorText(response.Body),
|
||||
)
|
||||
}
|
||||
|
||||
var responseBody applyOsqueryOptionsSpecResponse
|
||||
err = json.NewDecoder(response.Body).Decode(&responseBody)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode apply options spec response")
|
||||
}
|
||||
|
||||
if responseBody.Err != nil {
|
||||
return errors.Errorf("apply options spec: %s", responseBody.Err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOptions retrieves the configured osquery options.
|
||||
func (c *Client) GetOptions() (*kolide.OptionsSpec, error) {
|
||||
verb, path := "GET", "/api/v1/fleet/spec/osquery_options"
|
||||
response, err := c.AuthenticatedDo(verb, path, "", nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, verb+" "+path)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
switch response.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return nil, notFoundErr{}
|
||||
}
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, errors.Errorf(
|
||||
"get options received status %d %s",
|
||||
response.StatusCode,
|
||||
extractServerErrorText(response.Body),
|
||||
)
|
||||
}
|
||||
|
||||
var responseBody getOsqueryOptionsSpecResponse
|
||||
err = json.NewDecoder(response.Body).Decode(&responseBody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode get options spec response")
|
||||
}
|
||||
|
||||
if responseBody.Err != nil {
|
||||
return nil, errors.Errorf("get options spec: %s", responseBody.Err)
|
||||
}
|
||||
|
||||
return responseBody.Spec, nil
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Apply Options Spec
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type applyOsqueryOptionsSpecRequest struct {
|
||||
Spec *kolide.OptionsSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type applyOsqueryOptionsSpecResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r applyOsqueryOptionsSpecResponse) error() error { return r.Err }
|
||||
|
||||
func makeApplyOsqueryOptionsSpecEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(applyOsqueryOptionsSpecRequest)
|
||||
err := svc.ApplyOptionsSpec(ctx, req.Spec)
|
||||
if err != nil {
|
||||
return applyOsqueryOptionsSpecResponse{Err: err}, nil
|
||||
}
|
||||
return applyOsqueryOptionsSpecResponse{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Get Options Spec
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type getOsqueryOptionsSpecResponse struct {
|
||||
Spec *kolide.OptionsSpec `json:"spec"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r getOsqueryOptionsSpecResponse) error() error { return r.Err }
|
||||
|
||||
func makeGetOsqueryOptionsSpecEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
spec, err := svc.GetOptionsSpec(ctx)
|
||||
if err != nil {
|
||||
return getOsqueryOptionsSpecResponse{Err: err}, nil
|
||||
}
|
||||
return getOsqueryOptionsSpecResponse{Spec: spec}, nil
|
||||
}
|
||||
}
|
@ -97,8 +97,6 @@ type KolideEndpoints struct {
|
||||
ListHosts endpoint.Endpoint
|
||||
GetHostSummary endpoint.Endpoint
|
||||
SearchTargets endpoint.Endpoint
|
||||
ApplyOsqueryOptionsSpec endpoint.Endpoint
|
||||
GetOsqueryOptionsSpec endpoint.Endpoint
|
||||
GetCertificate endpoint.Endpoint
|
||||
ChangeEmail endpoint.Endpoint
|
||||
InitiateSSO endpoint.Endpoint
|
||||
@ -211,8 +209,6 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey, urlPrefix string, lim
|
||||
GetLabelSpecs: authenticatedUser(jwtKey, svc, makeGetLabelSpecsEndpoint(svc)),
|
||||
GetLabelSpec: authenticatedUser(jwtKey, svc, makeGetLabelSpecEndpoint(svc)),
|
||||
SearchTargets: authenticatedUser(jwtKey, svc, makeSearchTargetsEndpoint(svc)),
|
||||
ApplyOsqueryOptionsSpec: authenticatedUser(jwtKey, svc, makeApplyOsqueryOptionsSpecEndpoint(svc)),
|
||||
GetOsqueryOptionsSpec: authenticatedUser(jwtKey, svc, makeGetOsqueryOptionsSpecEndpoint(svc)),
|
||||
GetCertificate: authenticatedUser(jwtKey, svc, makeCertificateEndpoint(svc)),
|
||||
ChangeEmail: authenticatedUser(jwtKey, svc, makeChangeEmailEndpoint(svc)),
|
||||
ListCarves: authenticatedUser(jwtKey, svc, makeListCarvesEndpoint(svc)),
|
||||
@ -323,8 +319,6 @@ type kolideHandlers struct {
|
||||
ListHosts http.Handler
|
||||
GetHostSummary http.Handler
|
||||
SearchTargets http.Handler
|
||||
ApplyOsqueryOptionsSpec http.Handler
|
||||
GetOsqueryOptionsSpec http.Handler
|
||||
GetCertificate http.Handler
|
||||
ChangeEmail http.Handler
|
||||
InitiateSSO http.Handler
|
||||
@ -426,8 +420,6 @@ func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *koli
|
||||
ListHosts: newServer(e.ListHosts, decodeListHostsRequest),
|
||||
GetHostSummary: newServer(e.GetHostSummary, decodeNoParamsRequest),
|
||||
SearchTargets: newServer(e.SearchTargets, decodeSearchTargetsRequest),
|
||||
ApplyOsqueryOptionsSpec: newServer(e.ApplyOsqueryOptionsSpec, decodeApplyOsqueryOptionsSpecRequest),
|
||||
GetOsqueryOptionsSpec: newServer(e.GetOsqueryOptionsSpec, decodeNoParamsRequest),
|
||||
GetCertificate: newServer(e.GetCertificate, decodeNoParamsRequest),
|
||||
ChangeEmail: newServer(e.ChangeEmail, decodeChangeEmailRequest),
|
||||
InitiateSSO: newServer(e.InitiateSSO, decodeInitiateSSORequest),
|
||||
@ -644,9 +636,6 @@ func attachKolideAPIRoutes(r *mux.Router, h *kolideHandlers) {
|
||||
r.Handle("/api/v1/fleet/hosts/identifier/{identifier}", h.HostByIdentifier).Methods("GET").Name("host_by_identifier")
|
||||
r.Handle("/api/v1/fleet/hosts/{id}", h.DeleteHost).Methods("DELETE").Name("delete_host")
|
||||
|
||||
r.Handle("/api/v1/fleet/spec/osquery_options", h.ApplyOsqueryOptionsSpec).Methods("POST").Name("apply_osquery_options_spec")
|
||||
r.Handle("/api/v1/fleet/spec/osquery_options", h.GetOsqueryOptionsSpec).Methods("GET").Name("get_osquery_options_spec")
|
||||
|
||||
r.Handle("/api/v1/fleet/targets", h.SearchTargets).Methods("POST").Name("search_targets")
|
||||
|
||||
r.Handle("/api/v1/fleet/version", h.Version).Methods("GET").Name("version")
|
||||
|
@ -1,42 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/server/contexts/viewer"
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
)
|
||||
|
||||
func (mw loggingMiddleware) GetOptionsSpec(ctx context.Context) (spec *kolide.OptionsSpec, err error) {
|
||||
defer func(begin time.Time) {
|
||||
_ = mw.loggerDebug(err).Log(
|
||||
"method", "GetOptionsSpec",
|
||||
"err", err,
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
spec, err = mw.Service.GetOptionsSpec(ctx)
|
||||
return spec, err
|
||||
}
|
||||
|
||||
func (mw loggingMiddleware) ApplyOptionsSpec(ctx context.Context, spec *kolide.OptionsSpec) (err error) {
|
||||
var (
|
||||
loggedInUser = "unauthenticated"
|
||||
)
|
||||
|
||||
if vc, ok := viewer.FromContext(ctx); ok {
|
||||
|
||||
loggedInUser = vc.Username()
|
||||
}
|
||||
defer func(begin time.Time) {
|
||||
_ = mw.loggerDebug(err).Log(
|
||||
"method", "ApplyOptionsSpec",
|
||||
"err", err,
|
||||
"user", loggedInUser,
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
err = mw.Service.ApplyOptionsSpec(ctx, spec)
|
||||
return err
|
||||
}
|
41
server/service/service_agent_options.go
Normal file
41
server/service/service_agent_options.go
Normal file
@ -0,0 +1,41 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) AgentOptionsForHost(ctx context.Context, host *kolide.Host) (json.RawMessage, error) {
|
||||
// If host has a team and team has non-empty options, prioritize that.
|
||||
if host.TeamID.Valid {
|
||||
team, err := svc.ds.Team(uint(host.TeamID.Int64))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load team for host")
|
||||
}
|
||||
|
||||
if team.AgentOptions != nil && len(*team.AgentOptions) > 0 {
|
||||
var options kolide.AgentOptions
|
||||
if err := json.Unmarshal(*team.AgentOptions, &options); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal team agent options")
|
||||
}
|
||||
|
||||
return options.ForPlatform(host.Platform), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise return the appropriate override for global options.
|
||||
appConfig, err := svc.ds.AppConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "load global agent options")
|
||||
}
|
||||
|
||||
var options kolide.AgentOptions
|
||||
if err := json.Unmarshal(appConfig.AgentOptions, &options); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal global agent options")
|
||||
}
|
||||
|
||||
return options.ForPlatform(host.Platform), nil
|
||||
}
|
54
server/service/service_agent_options_test.go
Normal file
54
server/service/service_agent_options_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/fleetdm/fleet/server/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/guregu/null.v3"
|
||||
)
|
||||
|
||||
func TestAgentOptionsForHost(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
svc, err := newTestService(ds, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
teamID := uint(1)
|
||||
ds.TeamFunc = func(tid uint) (*kolide.Team, error) {
|
||||
assert.Equal(t, teamID, tid)
|
||||
opt := json.RawMessage(`{"config":{"foo":"bar"},"overrides":{"platforms":{"darwin":{"foo":"override"}}}}`)
|
||||
return &kolide.Team{AgentOptions: &opt}, nil
|
||||
}
|
||||
ds.AppConfigFunc = func() (*kolide.AppConfig, error) {
|
||||
return &kolide.AppConfig{AgentOptions: json.RawMessage(`{"config":{"baz":"bar"},"overrides":{"platforms":{"darwin":{"foo":"override2"}}}}`)}, nil
|
||||
}
|
||||
|
||||
host := &kolide.Host{
|
||||
TeamID: null.IntFrom(int64(teamID)),
|
||||
Platform: "darwin",
|
||||
}
|
||||
|
||||
opt, err := svc.AgentOptionsForHost(context.Background(), host)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, `{"foo":"override"}`, string(opt))
|
||||
|
||||
host.Platform = "windows"
|
||||
opt, err = svc.AgentOptionsForHost(context.Background(), host)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, `{"foo":"bar"}`, string(opt))
|
||||
|
||||
// Should take gobal option with no team
|
||||
host.TeamID.Valid = false
|
||||
opt, err = svc.AgentOptionsForHost(context.Background(), host)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, `{"baz":"bar"}`, string(opt))
|
||||
|
||||
host.Platform = "darwin"
|
||||
opt, err = svc.AgentOptionsForHost(context.Background(), host)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, `{"foo":"override2"}`, string(opt))
|
||||
}
|
@ -197,7 +197,7 @@ func (svc service) GetClientConfig(ctx context.Context) (map[string]interface{},
|
||||
return nil, osqueryError{message: "internal error: missing host from request context"}
|
||||
}
|
||||
|
||||
baseConfig, err := svc.ds.OptionsForPlatform(host.Platform)
|
||||
baseConfig, err := svc.AgentOptionsForHost(ctx, &host)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: fetching base config: " + err.Error()}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/fleetdm/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) ApplyOptionsSpec(ctx context.Context, spec *kolide.OptionsSpec) error {
|
||||
err := svc.ds.ApplyOptions(spec)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "apply options")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc service) GetOptionsSpec(ctx context.Context) (*kolide.OptionsSpec, error) {
|
||||
spec, err := svc.ds.GetOptions()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get options from datastore")
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
@ -433,30 +433,8 @@ func TestGetClientConfig(t *testing.T) {
|
||||
return []*kolide.ScheduledQuery{}, nil
|
||||
}
|
||||
}
|
||||
ds.OptionsForPlatformFunc = func(platform string) (json.RawMessage, error) {
|
||||
return json.RawMessage(`
|
||||
{
|
||||
"options":{
|
||||
"distributed_interval":11,
|
||||
"logger_tls_period":33
|
||||
},
|
||||
"decorators":{
|
||||
"load":[
|
||||
"SELECT version FROM osquery_info;",
|
||||
"SELECT uuid AS host_uuid FROM system_info;"
|
||||
],
|
||||
"always":[
|
||||
"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;"
|
||||
],
|
||||
"interval":{
|
||||
"3600":[
|
||||
"SELECT total_seconds AS uptime FROM uptime;"
|
||||
]
|
||||
}
|
||||
},
|
||||
"foo": "bar"
|
||||
}
|
||||
`), nil
|
||||
ds.AppConfigFunc = func() (*kolide.AppConfig, error) {
|
||||
return &kolide.AppConfig{AgentOptions: json.RawMessage(`{"config":{"options":{"baz":"bar"}}}`)}, nil
|
||||
}
|
||||
ds.SaveHostFunc = func(host *kolide.Host) error {
|
||||
return nil
|
||||
@ -469,27 +447,11 @@ func TestGetClientConfig(t *testing.T) {
|
||||
ctx2 := hostctx.NewContext(context.Background(), kolide.Host{ID: 2})
|
||||
|
||||
expectedOptions := map[string]interface{}{
|
||||
"distributed_interval": float64(11),
|
||||
"logger_tls_period": float64(33),
|
||||
}
|
||||
|
||||
expectedDecorators := map[string]interface{}{
|
||||
"load": []interface{}{
|
||||
"SELECT version FROM osquery_info;",
|
||||
"SELECT uuid AS host_uuid FROM system_info;",
|
||||
},
|
||||
"always": []interface{}{
|
||||
"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;",
|
||||
},
|
||||
"interval": map[string]interface{}{
|
||||
"3600": []interface{}{"SELECT total_seconds AS uptime FROM uptime;"},
|
||||
},
|
||||
"baz": "bar",
|
||||
}
|
||||
|
||||
expectedConfig := map[string]interface{}{
|
||||
"options": expectedOptions,
|
||||
"decorators": expectedDecorators,
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
// No packs loaded yet
|
||||
@ -1517,8 +1479,8 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
t.Run("", func(t *testing.T) {
|
||||
ctx := hostctx.NewContext(context.Background(), tt.initHost)
|
||||
|
||||
ds.OptionsForPlatformFunc = func(platform string) (json.RawMessage, error) {
|
||||
return tt.configOptions, nil
|
||||
ds.AppConfigFunc = func() (*kolide.AppConfig, error) {
|
||||
return &kolide.AppConfig{AgentOptions: json.RawMessage(`{"config":` + string(tt.configOptions) + `}`)}, nil
|
||||
}
|
||||
|
||||
saveHostCalled := false
|
||||
|
@ -1,16 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func decodeApplyOsqueryOptionsSpecRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req applyOsqueryOptionsSpecRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return req, nil
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user