mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
Transition osquery options interfaces for compatibility with fleetctl (#1649)
- Refinements to options yaml definition - Datastore and service implementations - Migration to bring existing options into new table format
This commit is contained in:
parent
45165aa29a
commit
5e9fe9d5a1
16
Gopkg.lock
generated
16
Gopkg.lock
generated
@ -66,6 +66,12 @@
|
||||
revision = "47dc60e71eed504e3ef8e77ee3c6fe720f3be57f"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ghodss/yaml"
|
||||
packages = ["."]
|
||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-kit/kit"
|
||||
packages = ["endpoint","log","log/level","metrics","metrics/internal/lv","metrics/prometheus","transport/grpc","transport/http"]
|
||||
@ -166,7 +172,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/kolide/launcher"
|
||||
packages = ["service","service/internal/launcherproto","service/uuid"]
|
||||
revision = "4b93ec1bd7ab0eaa302ddcfa8ed554950de8f38e"
|
||||
revision = "9f53adbf07cbf4222885118c3aa599edcbc37dab"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@ -322,7 +328,7 @@
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "75cc3cad82b5f47d3fb229ddda8c5167da14f294"
|
||||
revision = "57961680700a5336d15015c8c50686ca5ba362a4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@ -331,10 +337,10 @@
|
||||
revision = "7f0da29060c682909f650ad8ed4e515bd74fa12a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [".","balancer","balancer/roundrobin","codes","connectivity","credentials","encoding","grpclb/grpc_lb_v1/messages","grpclog","internal","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/manual","resolver/passthrough","stats","status","tap","transport"]
|
||||
revision = "a4bf341022f076582fc2e9a802ce170a8938f81d"
|
||||
revision = "be077907e29fdb945d351e4284eb5361e7f8924e"
|
||||
version = "v1.8.1"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/natefinch/lumberjack.v2"
|
||||
@ -351,6 +357,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "8bca5e6fdea71d7fa8f4251ba35e18142490728a17e4aa5de66cf8fd23d9c798"
|
||||
inputs-digest = "eb90a37f82621ce0e2e040bd1f3ade7ef48913bb559c95f9c34f4929d7324990"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@ -113,31 +113,55 @@ apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryOptions
|
||||
spec:
|
||||
config:
|
||||
distributed_interval: 3
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 10
|
||||
options:
|
||||
distributed_interval: 3
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 10
|
||||
overrides:
|
||||
# Note configs in overrides take precedence over base configs
|
||||
# Note configs in overrides take precedence over the default config defined
|
||||
# under the config key above. With this config file, the base config would
|
||||
# only be used for Windows hosts, while Mac and Linux hosts would pull
|
||||
# these overrides.
|
||||
platforms:
|
||||
darwin:
|
||||
disable_tables: chrome_extensions
|
||||
docker_socket: /var/run/docker.sock
|
||||
logger_tls_period: 60
|
||||
fim:
|
||||
interval: 500
|
||||
groups:
|
||||
- name: etc
|
||||
paths:
|
||||
- /etc/%%
|
||||
- name: users
|
||||
paths:
|
||||
- /Users/%/Library/%%
|
||||
- /Users/%/Documents/%%
|
||||
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:
|
||||
schedule_timeout: 60
|
||||
docker_socket: /etc/run/docker.sock
|
||||
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
|
||||
file_paths:
|
||||
homes:
|
||||
- /root/.ssh/%%
|
||||
- /home/%/.ssh/%%
|
||||
etc:
|
||||
- /etc/%%
|
||||
tmp:
|
||||
- /tmp/%%
|
||||
exclude_paths:
|
||||
homes:
|
||||
- /home/not_to_monitor/.ssh/%%
|
||||
tmp:
|
||||
- /tmp/too_many_events/
|
||||
```
|
||||
|
||||
### Osquery Logging Decorators
|
||||
@ -145,7 +169,6 @@ spec:
|
||||
The following file describes logging decorators that should be applied on osquery instances. A decorator should reference an osquery query by name. Both of these resources can be included in the same file as such:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryDecorator
|
||||
spec:
|
||||
@ -165,7 +188,6 @@ spec:
|
||||
The following file describes the labels which hosts should be automatically grouped into. The label resource should reference the query by name. Both of these resources can be included in the same file as such:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryLabel
|
||||
spec:
|
||||
@ -206,7 +228,6 @@ spec:
|
||||
To define multiple queries in a file, concatenate multiple `OsqueryQuery` resources together in a single file with `---`. For example, consider a file that you might store at `queries/osquery_monitoring.yml`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryQuery
|
||||
spec:
|
||||
|
@ -3,28 +3,52 @@ apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryOptions
|
||||
spec:
|
||||
config:
|
||||
distributed_interval: 3
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 10
|
||||
options:
|
||||
distributed_interval: 3
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 10
|
||||
overrides:
|
||||
# Note configs in overrides take precedence over base configs
|
||||
# Note configs in overrides take precedence over the default config defined
|
||||
# under the config key above. With this config file, the base config would
|
||||
# only be used for Windows hosts, while Mac and Linux hosts would pull
|
||||
# these overrides.
|
||||
platforms:
|
||||
darwin:
|
||||
disable_tables: chrome_extensions
|
||||
docker_socket: /var/run/docker.sock
|
||||
logger_tls_period: 60
|
||||
fim:
|
||||
interval: 500
|
||||
groups:
|
||||
- name: etc
|
||||
paths:
|
||||
- /etc/%%
|
||||
- name: users
|
||||
paths:
|
||||
- /Users/%/Library/%%
|
||||
- /Users/%/Documents/%%
|
||||
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:
|
||||
schedule_timeout: 60
|
||||
docker_socket: /etc/run/docker.sock
|
||||
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
|
||||
file_paths:
|
||||
homes:
|
||||
- /root/.ssh/%%
|
||||
- /home/%/.ssh/%%
|
||||
etc:
|
||||
- /etc/%%
|
||||
tmp:
|
||||
- /tmp/%%
|
||||
exclude_paths:
|
||||
homes:
|
||||
- /home/not_to_monitor/.ssh/%%
|
||||
tmp:
|
||||
- /tmp/too_many_events/
|
||||
|
@ -3,31 +3,55 @@ apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryOptions
|
||||
spec:
|
||||
config:
|
||||
distributed_interval: 3
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 10
|
||||
options:
|
||||
distributed_interval: 3
|
||||
distributed_tls_max_attempts: 3
|
||||
logger_plugin: tls
|
||||
logger_tls_endpoint: /api/v1/osquery/log
|
||||
logger_tls_period: 10
|
||||
overrides:
|
||||
# Note configs in overrides take precedence over base configs
|
||||
# Note configs in overrides take precedence over the default config defined
|
||||
# under the config key above. With this config file, the base config would
|
||||
# only be used for Windows hosts, while Mac and Linux hosts would pull
|
||||
# these overrides.
|
||||
platforms:
|
||||
darwin:
|
||||
disable_tables: chrome_extensions
|
||||
docker_socket: /var/run/docker.sock
|
||||
logger_tls_period: 60
|
||||
fim:
|
||||
interval: 500
|
||||
groups:
|
||||
- name: etc
|
||||
paths:
|
||||
- /etc/%%
|
||||
- name: users
|
||||
paths:
|
||||
- /Users/%/Library/%%
|
||||
- /Users/%/Documents/%%
|
||||
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:
|
||||
schedule_timeout: 60
|
||||
docker_socket: /etc/run/docker.sock
|
||||
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
|
||||
file_paths:
|
||||
homes:
|
||||
- /root/.ssh/%%
|
||||
- /home/%/.ssh/%%
|
||||
etc:
|
||||
- /etc/%%
|
||||
tmp:
|
||||
- /tmp/%%
|
||||
exclude_paths:
|
||||
homes:
|
||||
- /home/not_to_monitor/.ssh/%%
|
||||
tmp:
|
||||
- /tmp/too_many_events/
|
||||
---
|
||||
apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryDecorator
|
||||
|
95
server/datastore/datastore_osquery_options_test.go
Normal file
95
server/datastore/datastore_osquery_options_test.go
Normal file
@ -0,0 +1,95 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/kolide/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)
|
||||
})
|
||||
}
|
||||
}
|
@ -79,4 +79,7 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){
|
||||
testCountHostsInTargets,
|
||||
testHostStatus,
|
||||
testResetOptions,
|
||||
testApplyOsqueryOptions,
|
||||
testApplyOsqueryOptionsNoOverrides,
|
||||
testOsqueryOptionsForHost,
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ type Datastore struct {
|
||||
appConfig *kolide.AppConfig
|
||||
config *config.KolideConfig
|
||||
kolide.TargetStore
|
||||
kolide.OsqueryOptionsStore
|
||||
}
|
||||
|
||||
func New(config config.KolideConfig) (*Datastore, error) {
|
||||
|
@ -0,0 +1,99 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/jmoiron/sqlx/reflectx"
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func init() {
|
||||
MigrationClient.AddMigration(Up_20171212182458, Down_20171212182458)
|
||||
}
|
||||
|
||||
type configForExport struct {
|
||||
Options map[string]interface{} `json:"options"`
|
||||
FilePaths map[string][]string `json:"file_paths,omitempty"`
|
||||
}
|
||||
|
||||
type yamlObjForExport struct {
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
Spec specForExport `json:"spec"`
|
||||
}
|
||||
|
||||
type specForExport struct {
|
||||
Config json.RawMessage `json:"config"`
|
||||
}
|
||||
|
||||
func Up_20171212182458(tx *sql.Tx) error {
|
||||
txx := sqlx.Tx{Tx: tx, Mapper: reflectx.NewMapperFunc("db", sqlx.NameMapper)}
|
||||
|
||||
// Get basic osquery options
|
||||
query := `
|
||||
SELECT *
|
||||
FROM options
|
||||
WHERE value != "null"
|
||||
`
|
||||
// Intentionally initialize empty instead of nil so that we generate a
|
||||
// config with empty options rather than a null value.
|
||||
var opts []kolide.Option
|
||||
if err := txx.Select(&opts, query); err != nil && err != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "getting options")
|
||||
}
|
||||
optConfig := map[string]interface{}{}
|
||||
for _, opt := range opts {
|
||||
optConfig[opt.Name] = opt.GetValue()
|
||||
}
|
||||
|
||||
// Get FIM paths from fim table
|
||||
query = `
|
||||
SELECT fim.section_name, mf.file
|
||||
FROM file_integrity_monitorings AS fim
|
||||
INNER JOIN file_integrity_monitoring_files AS mf
|
||||
ON (fim.id = mf.file_integrity_monitoring_id)
|
||||
`
|
||||
rows, err := txx.Query(query)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "retrieving fim paths")
|
||||
}
|
||||
fimConfig := kolide.FIMSections{}
|
||||
for rows.Next() {
|
||||
var sectionName, fileName string
|
||||
err = rows.Scan(§ionName, &fileName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "retrieving path for fim section")
|
||||
}
|
||||
fimConfig[sectionName] = append(fimConfig[sectionName], fileName)
|
||||
}
|
||||
|
||||
// Create config JSON
|
||||
config := configForExport{
|
||||
Options: optConfig,
|
||||
FilePaths: fimConfig,
|
||||
}
|
||||
confJSON, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal config JSON")
|
||||
}
|
||||
|
||||
// Save config JSON
|
||||
query = `
|
||||
INSERT INTO osquery_options (
|
||||
override_type, override_identifier, options
|
||||
) VALUES (?, ?, ?)
|
||||
`
|
||||
if _, err = txx.Exec(query, kolide.OptionOverrideTypeDefault, "", string(confJSON)); err != nil {
|
||||
return errors.Wrap(err, "saving converted options")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Down_20171212182458(tx *sql.Tx) error {
|
||||
// No down migration
|
||||
return nil
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package tables
|
||||
|
||||
import "database/sql"
|
||||
|
||||
func init() {
|
||||
MigrationClient.AddMigration(Up_20171116163618, Down_20171116163618)
|
||||
}
|
||||
|
||||
func Up_20171116163618(tx *sql.Tx) error {
|
||||
sqlStatement := "CREATE TABLE `osquery_options` (" +
|
||||
"`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT," +
|
||||
"`override_type` INT(1) NOT NULL, " +
|
||||
"`override_identifier` VARCHAR(255) NOT NULL DEFAULT ''," +
|
||||
"`options` JSON NOT NULL," +
|
||||
"PRIMARY KEY (`id`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8;"
|
||||
_, err := tx.Exec(sqlStatement)
|
||||
return err
|
||||
}
|
||||
|
||||
func Down_20171116163618(tx *sql.Tx) error {
|
||||
_, err := tx.Exec("DROP TABLE IF EXISTS `osquery_options`;")
|
||||
return err
|
||||
}
|
132
server/datastore/mysql/osquery_options.go
Normal file
132
server/datastore/mysql/osquery_options.go
Normal file
@ -0,0 +1,132 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-kit/kit/log/level"
|
||||
"github.com/kolide/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
|
||||
}
|
@ -18,6 +18,7 @@ type Datastore interface {
|
||||
DecoratorStore
|
||||
FileIntegrityMonitoringStore
|
||||
YARAStore
|
||||
OsqueryOptionsStore
|
||||
Name() string
|
||||
Drop() error
|
||||
// MigrateTables creates and migrates the table schemas
|
||||
|
3
server/kolide/fleetctl.go
Normal file
3
server/kolide/fleetctl.go
Normal file
@ -0,0 +1,3 @@
|
||||
package kolide
|
||||
|
||||
const ApiVersion = "k8s.kolide.com/v1alpha1"
|
@ -8,7 +8,7 @@ import (
|
||||
type OsqueryService interface {
|
||||
EnrollAgent(ctx context.Context, enrollSecret, hostIdentifier string) (nodeKey string, err error)
|
||||
AuthenticateHost(ctx context.Context, nodeKey string) (host *Host, err error)
|
||||
GetClientConfig(ctx context.Context) (config *OsqueryConfig, err error)
|
||||
GetClientConfig(ctx context.Context) (config map[string]interface{}, err error)
|
||||
// GetDistributedQueries retrieves the distributed queries to run for
|
||||
// the host in the provided context. These may be detail queries, label
|
||||
// queries, or user-initiated distributed queries. A map from query
|
||||
@ -60,18 +60,6 @@ type Decorators struct {
|
||||
Interval map[string][]string `json:"interval,omitempty"`
|
||||
}
|
||||
|
||||
// OsqueryConfig is a struct that can be serialized into a valid osquery config
|
||||
// using Go's JSON tooling.
|
||||
type OsqueryConfig struct {
|
||||
Schedule map[string]QueryContent `json:"schedule,omitempty"`
|
||||
Options map[string]interface{} `json:"options"`
|
||||
Decorators Decorators `json:"decorators,omitempty"`
|
||||
Packs Packs `json:"packs,omitempty"`
|
||||
// FilePaths contains named collections of file paths used for
|
||||
// FIM (File Integrity Monitoring)
|
||||
FilePaths FIMSections `json:"file_paths,omitempty"`
|
||||
}
|
||||
|
||||
// OsqueryStatusLog is the format of an osquery status log.
|
||||
type OsqueryStatusLog struct {
|
||||
Severity string `json:"severity"`
|
||||
|
48
server/kolide/osquery_options.go
Normal file
48
server/kolide/osquery_options.go
Normal file
@ -0,0 +1,48 @@
|
||||
package kolide
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type OsqueryOptionsStore interface {
|
||||
ApplyOptions(options *OptionsSpec) error
|
||||
GetOptions() (*OptionsSpec, error)
|
||||
OptionsForPlatform(platform string) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
type OsqueryOptionsService interface {
|
||||
ApplyOptionsYaml(yml string) error
|
||||
GetOptionsYaml() (string, error)
|
||||
}
|
||||
|
||||
type OptionsYaml struct {
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
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 (
|
||||
OptionsSpecKind = "OsqueryOptions"
|
||||
)
|
||||
|
||||
// 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
|
||||
)
|
119
server/kolide/osquery_options_test.go
Normal file
119
server/kolide/osquery_options_test.go
Normal file
@ -0,0 +1,119 @@
|
||||
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 OptionsYaml
|
||||
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"]))
|
||||
}
|
||||
}
|
@ -45,10 +45,10 @@ func (svc *launcherWrapper) RequestConfig(ctx context.Context, nodeKey string) (
|
||||
return "", false, errors.Wrap(err, "get config for launcher")
|
||||
}
|
||||
|
||||
// Launcher manages plugins so remove them from configuration if they exist.
|
||||
for _, optionName := range []string{"distributed_plugin", "logger_plugin"} {
|
||||
if _, ok := config.Options[optionName]; ok {
|
||||
delete(config.Options, optionName)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ func TestLauncherRequestConfig(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
assert.True(t, tls.AuthenticateHostFuncInvoked)
|
||||
assert.False(t, invalid)
|
||||
assert.Equal(t, `{"options":{"key":"value"},"decorators":{}}`, config)
|
||||
assert.JSONEq(t, `{"options":{"key":"value"},"decorators":{"deco":"foobar"}}`, config)
|
||||
}
|
||||
|
||||
func TestLauncherRequestQueries(t *testing.T) {
|
||||
@ -122,11 +122,14 @@ func newTLSService(t *testing.T) *mock.TLSService {
|
||||
},
|
||||
GetClientConfigFunc: func(
|
||||
ctx context.Context,
|
||||
) (config *kolide.OsqueryConfig, err error) {
|
||||
return &kolide.OsqueryConfig{
|
||||
Options: map[string]interface{}{
|
||||
) (config map[string]interface{}, err error) {
|
||||
return map[string]interface{}{
|
||||
"options": map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
"decorators": map[string]interface{}{
|
||||
"deco": "foobar",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
||||
|
@ -9,6 +9,8 @@ package mock
|
||||
//go:generate mockimpl -o datastore_packs.go "s *PackStore" "kolide.PackStore"
|
||||
//go:generate mockimpl -o datastore_hosts.go "s *HostStore" "kolide.HostStore"
|
||||
//go:generate mockimpl -o datastore_fim.go "s *FileIntegrityMonitoringStore" "kolide.FileIntegrityMonitoringStore"
|
||||
//go:generate mockimpl -o datastore_osquery_options.go "s *OsqueryOptionsStore" "kolide.OsqueryOptionsStore"
|
||||
//go:generate mockimpl -o datastore_scheduled_queries.go "s *ScheduledQueryStore" "kolide.ScheduledQueryStore"
|
||||
|
||||
import "github.com/kolide/fleet/server/kolide"
|
||||
|
||||
@ -19,9 +21,10 @@ type Store struct {
|
||||
kolide.SessionStore
|
||||
kolide.PasswordResetStore
|
||||
kolide.QueryStore
|
||||
kolide.ScheduledQueryStore
|
||||
kolide.YARAStore
|
||||
kolide.TargetStore
|
||||
ScheduledQueryStore
|
||||
OsqueryOptionsStore
|
||||
FileIntegrityMonitoringStore
|
||||
AppConfigStore
|
||||
DecoratorStore
|
||||
|
43
server/mock/datastore_osquery_options.go
Normal file
43
server/mock/datastore_osquery_options.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Automatically generated by mockimpl. DO NOT EDIT!
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/kolide/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)
|
||||
}
|
59
server/mock/datastore_scheduled_queries.go
Normal file
59
server/mock/datastore_scheduled_queries.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Automatically generated by mockimpl. DO NOT EDIT!
|
||||
|
||||
package mock
|
||||
|
||||
import "github.com/kolide/fleet/server/kolide"
|
||||
|
||||
var _ kolide.ScheduledQueryStore = (*ScheduledQueryStore)(nil)
|
||||
|
||||
type NewScheduledQueryFunc func(sq *kolide.ScheduledQuery, opts ...kolide.OptionalArg) (*kolide.ScheduledQuery, error)
|
||||
|
||||
type SaveScheduledQueryFunc func(sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error)
|
||||
|
||||
type DeleteScheduledQueryFunc func(id uint) error
|
||||
|
||||
type ScheduledQueryFunc func(id uint) (*kolide.ScheduledQuery, error)
|
||||
|
||||
type ListScheduledQueriesInPackFunc func(id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error)
|
||||
|
||||
type ScheduledQueryStore struct {
|
||||
NewScheduledQueryFunc NewScheduledQueryFunc
|
||||
NewScheduledQueryFuncInvoked bool
|
||||
|
||||
SaveScheduledQueryFunc SaveScheduledQueryFunc
|
||||
SaveScheduledQueryFuncInvoked bool
|
||||
|
||||
DeleteScheduledQueryFunc DeleteScheduledQueryFunc
|
||||
DeleteScheduledQueryFuncInvoked bool
|
||||
|
||||
ScheduledQueryFunc ScheduledQueryFunc
|
||||
ScheduledQueryFuncInvoked bool
|
||||
|
||||
ListScheduledQueriesInPackFunc ListScheduledQueriesInPackFunc
|
||||
ListScheduledQueriesInPackFuncInvoked bool
|
||||
}
|
||||
|
||||
func (s *ScheduledQueryStore) NewScheduledQuery(sq *kolide.ScheduledQuery, opts ...kolide.OptionalArg) (*kolide.ScheduledQuery, error) {
|
||||
s.NewScheduledQueryFuncInvoked = true
|
||||
return s.NewScheduledQueryFunc(sq, opts...)
|
||||
}
|
||||
|
||||
func (s *ScheduledQueryStore) SaveScheduledQuery(sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error) {
|
||||
s.SaveScheduledQueryFuncInvoked = true
|
||||
return s.SaveScheduledQueryFunc(sq)
|
||||
}
|
||||
|
||||
func (s *ScheduledQueryStore) DeleteScheduledQuery(id uint) error {
|
||||
s.DeleteScheduledQueryFuncInvoked = true
|
||||
return s.DeleteScheduledQueryFunc(id)
|
||||
}
|
||||
|
||||
func (s *ScheduledQueryStore) ScheduledQuery(id uint) (*kolide.ScheduledQuery, error) {
|
||||
s.ScheduledQueryFuncInvoked = true
|
||||
return s.ScheduledQueryFunc(id)
|
||||
}
|
||||
|
||||
func (s *ScheduledQueryStore) ListScheduledQueriesInPack(id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error) {
|
||||
s.ListScheduledQueriesInPackFuncInvoked = true
|
||||
return s.ListScheduledQueriesInPackFunc(id, opts)
|
||||
}
|
@ -15,7 +15,7 @@ type EnrollAgentFunc func(ctx context.Context, enrollSecret string, hostIdentifi
|
||||
|
||||
type AuthenticateHostFuncI func(ctx context.Context, nodeKey string) (host *kolide.Host, err error)
|
||||
|
||||
type GetClientConfigFunc func(ctx context.Context) (config *kolide.OsqueryConfig, err error)
|
||||
type GetClientConfigFunc func(ctx context.Context) (config map[string]interface{}, err error)
|
||||
|
||||
type GetDistributedQueriesFunc func(ctx context.Context) (queries map[string]string, accelerate uint, err error)
|
||||
|
||||
@ -58,7 +58,7 @@ func (s *TLSService) AuthenticateHost(ctx context.Context, nodeKey string) (host
|
||||
return s.AuthenticateHostFunc(ctx, nodeKey)
|
||||
}
|
||||
|
||||
func (s *TLSService) GetClientConfig(ctx context.Context) (config *kolide.OsqueryConfig, err error) {
|
||||
func (s *TLSService) GetClientConfig(ctx context.Context) (config map[string]interface{}, err error) {
|
||||
s.GetClientConfigFuncInvoked = true
|
||||
return s.GetClientConfigFunc(ctx)
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ type getClientConfigRequest struct {
|
||||
}
|
||||
|
||||
type getClientConfigResponse struct {
|
||||
kolide.OsqueryConfig
|
||||
Err error `json:"error,omitempty"`
|
||||
Config map[string]interface{}
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r getClientConfigResponse) error() error { return r.Err }
|
||||
@ -56,7 +56,7 @@ func makeGetClientConfigEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
if err != nil {
|
||||
return getClientConfigResponse{Err: err}, nil
|
||||
}
|
||||
return getClientConfigResponse{OsqueryConfig: *config}, nil
|
||||
return getClientConfigResponse{Config: config}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,9 +48,9 @@ func (mw loggingMiddleware) AuthenticateHost(ctx context.Context, nodeKey string
|
||||
return host, err
|
||||
}
|
||||
|
||||
func (mw loggingMiddleware) GetClientConfig(ctx context.Context) (*kolide.OsqueryConfig, error) {
|
||||
func (mw loggingMiddleware) GetClientConfig(ctx context.Context) (map[string]interface{}, error) {
|
||||
var (
|
||||
config *kolide.OsqueryConfig
|
||||
config map[string]interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
|
@ -89,39 +89,26 @@ func (svc service) EnrollAgent(ctx context.Context, enrollSecret, hostIdentifier
|
||||
return host.NodeKey, nil
|
||||
}
|
||||
|
||||
func (svc service) getFIMConfig(ctx context.Context, cfg *kolide.OsqueryConfig) (*kolide.OsqueryConfig, error) {
|
||||
fimConfig, err := svc.GetFIM(ctx)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: unable to fetch FIM configuration"}
|
||||
}
|
||||
if cfg.Schedule == nil {
|
||||
cfg.Schedule = make(map[string]kolide.QueryContent)
|
||||
}
|
||||
removed := false
|
||||
// file events scheduled query is required to run file integrity monitors
|
||||
cfg.Schedule["file_events"] = kolide.QueryContent{
|
||||
Query: "SELECT * FROM file_events;",
|
||||
Interval: fimConfig.Interval,
|
||||
Removed: &removed,
|
||||
}
|
||||
cfg.FilePaths = fimConfig.FilePaths
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (svc service) GetClientConfig(ctx context.Context) (*kolide.OsqueryConfig, error) {
|
||||
func (svc service) GetClientConfig(ctx context.Context) (map[string]interface{}, error) {
|
||||
host, ok := hostctx.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, osqueryError{message: "internal error: missing host from request context"}
|
||||
}
|
||||
|
||||
options, err := svc.ds.GetOsqueryConfigOptions()
|
||||
baseConfig, err := svc.ds.OptionsForPlatform(host.Platform)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: unable to fetch configuration options"}
|
||||
return nil, osqueryError{message: "internal error: fetching base config: " + err.Error()}
|
||||
}
|
||||
|
||||
var config map[string]interface{}
|
||||
err = json.Unmarshal(baseConfig, &config)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: parsing base configuration: " + err.Error()}
|
||||
}
|
||||
|
||||
decorators, err := svc.ds.ListDecorators()
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: unable to fetch decorators"}
|
||||
return nil, osqueryError{message: "internal error: unable to fetch decorators: " + err.Error()}
|
||||
}
|
||||
decConfig := kolide.Decorators{
|
||||
Interval: make(map[string][]string),
|
||||
@ -141,10 +128,12 @@ func (svc service) GetClientConfig(ctx context.Context) (*kolide.OsqueryConfig,
|
||||
}
|
||||
}
|
||||
|
||||
config := &kolide.OsqueryConfig{
|
||||
Options: options,
|
||||
Decorators: decConfig,
|
||||
Packs: kolide.Packs{},
|
||||
if len(decConfig.Interval) > 0 || len(decConfig.Always) > 0 || len(decConfig.Load) > 0 {
|
||||
decJSON, err := json.Marshal(decConfig)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: marshal decorator JSON: " + err.Error()}
|
||||
}
|
||||
config["decorators"] = json.RawMessage(decJSON)
|
||||
}
|
||||
|
||||
packs, err := svc.ListPacksForHost(ctx, host.ID)
|
||||
@ -152,6 +141,7 @@ func (svc service) GetClientConfig(ctx context.Context) (*kolide.OsqueryConfig,
|
||||
return nil, osqueryError{message: "database error: " + err.Error()}
|
||||
}
|
||||
|
||||
packConfig := kolide.Packs{}
|
||||
for _, pack := range packs {
|
||||
// first, we must figure out what queries are in this pack
|
||||
queries, err := svc.ds.ListScheduledQueriesInPack(pack.ID, kolide.ListOptions{})
|
||||
@ -178,30 +168,40 @@ func (svc service) GetClientConfig(ctx context.Context) (*kolide.OsqueryConfig,
|
||||
}
|
||||
|
||||
// finally, we add the pack to the client config struct with all of
|
||||
// the packs queries
|
||||
config.Packs[pack.Name] = kolide.PackContent{
|
||||
// the pack's queries
|
||||
packConfig[pack.Name] = kolide.PackContent{
|
||||
Platform: pack.Platform,
|
||||
Queries: configQueries,
|
||||
}
|
||||
}
|
||||
|
||||
if len(packConfig) > 0 {
|
||||
packJSON, err := json.Marshal(packConfig)
|
||||
if err != nil {
|
||||
return nil, osqueryError{message: "internal error: marshal pack JSON: " + err.Error()}
|
||||
}
|
||||
config["packs"] = json.RawMessage(packJSON)
|
||||
}
|
||||
|
||||
// Save interval values if they have been updated. Note
|
||||
// config_tls_refresh can only be set in the osquery flags so is
|
||||
// ignored here.
|
||||
saveHost := false
|
||||
|
||||
distributedIntervalVal, ok := config.Options["distributed_interval"]
|
||||
distributedInterval, err := cast.ToUintE(distributedIntervalVal)
|
||||
if ok && err == nil && host.DistributedInterval != distributedInterval {
|
||||
host.DistributedInterval = distributedInterval
|
||||
saveHost = true
|
||||
}
|
||||
if options, ok := config["options"].(map[string]interface{}); ok {
|
||||
distributedIntervalVal, ok := options["distributed_interval"]
|
||||
distributedInterval, err := cast.ToUintE(distributedIntervalVal)
|
||||
if ok && err == nil && host.DistributedInterval != distributedInterval {
|
||||
host.DistributedInterval = distributedInterval
|
||||
saveHost = true
|
||||
}
|
||||
|
||||
loggerTLSPeriodVal, ok := config.Options["logger_tls_period"]
|
||||
loggerTLSPeriod, err := cast.ToUintE(loggerTLSPeriodVal)
|
||||
if ok && err == nil && host.LoggerTLSPeriod != loggerTLSPeriod {
|
||||
host.LoggerTLSPeriod = loggerTLSPeriod
|
||||
saveHost = true
|
||||
loggerTLSPeriodVal, ok := options["logger_tls_period"]
|
||||
loggerTLSPeriod, err := cast.ToUintE(loggerTLSPeriodVal)
|
||||
if ok && err == nil && host.LoggerTLSPeriod != loggerTLSPeriod {
|
||||
host.LoggerTLSPeriod = loggerTLSPeriod
|
||||
saveHost = true
|
||||
}
|
||||
}
|
||||
|
||||
if saveHost {
|
||||
@ -211,7 +211,7 @@ func (svc service) GetClientConfig(ctx context.Context) (*kolide.OsqueryConfig,
|
||||
}
|
||||
}
|
||||
|
||||
return svc.getFIMConfig(ctx, config)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// If osqueryWriters are based on bufio we want to flush after a batch of
|
||||
|
41
server/service/service_osquery_options.go
Normal file
41
server/service/service_osquery_options.go
Normal file
@ -0,0 +1,41 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc service) ApplyOptionsYaml(yml string) error {
|
||||
var conf kolide.OptionsYaml
|
||||
err := yaml.Unmarshal([]byte(yml), &conf)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unmarshal options YAML")
|
||||
}
|
||||
|
||||
if conf.Kind != kolide.OptionsSpecKind {
|
||||
return errors.Errorf("expected kind '%s', got '%s'", kolide.OptionsSpecKind, conf.Kind)
|
||||
}
|
||||
|
||||
err = svc.ds.ApplyOptions(&conf.Spec)
|
||||
return errors.Wrap(err, "apply options")
|
||||
}
|
||||
|
||||
func (svc service) GetOptionsYaml() (string, error) {
|
||||
spec, err := svc.ds.GetOptions()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "get options from datastore")
|
||||
}
|
||||
|
||||
ymlObj := kolide.OptionsYaml{
|
||||
ApiVersion: kolide.ApiVersion,
|
||||
Kind: kolide.OptionsSpecKind,
|
||||
Spec: *spec,
|
||||
}
|
||||
|
||||
yml, err := yaml.Marshal(ymlObj)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "marshal options yaml")
|
||||
}
|
||||
return string(yml), nil
|
||||
}
|
138
server/service/service_osquery_options_test.go
Normal file
138
server/service/service_osquery_options_test.go
Normal file
@ -0,0 +1,138 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
"github.com/kolide/fleet/server/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestApplyOptionsYaml(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
yml string
|
||||
options *kolide.OptionsSpec
|
||||
shouldErr bool
|
||||
}{
|
||||
{"notyaml", nil, true},
|
||||
{
|
||||
yml: `
|
||||
apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryQuery
|
||||
spec:
|
||||
name: osquery_schedule
|
||||
description: Report performance stats
|
||||
query: select * from osquery_schedule
|
||||
`, // Wrong kind of yaml
|
||||
options: nil,
|
||||
shouldErr: true,
|
||||
},
|
||||
{
|
||||
yml: `
|
||||
apiVersion: k8s.kolide.com/v1alpha1
|
||||
kind: OsqueryOptions
|
||||
spec:
|
||||
config:
|
||||
foo: bar
|
||||
overrides:
|
||||
# Note configs in overrides take precedence over base configs
|
||||
platforms:
|
||||
darwin:
|
||||
bing: bang
|
||||
`,
|
||||
options: &kolide.OptionsSpec{
|
||||
Config: json.RawMessage(`{"foo":"bar"}`),
|
||||
Overrides: kolide.OptionsOverrides{
|
||||
Platforms: map[string]json.RawMessage{
|
||||
"darwin": json.RawMessage(`{"bing":"bang"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
var gotOptions *kolide.OptionsSpec
|
||||
ds := &mock.Store{
|
||||
OsqueryOptionsStore: mock.OsqueryOptionsStore{
|
||||
ApplyOptionsFunc: func(options *kolide.OptionsSpec) error {
|
||||
gotOptions = options
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
svc := service{
|
||||
ds: ds,
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
gotOptions = nil
|
||||
t.Run("", func(t *testing.T) {
|
||||
err := svc.ApplyOptionsYaml(tt.yml)
|
||||
if tt.shouldErr {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tt.options, gotOptions)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionsYamlRoundtrip(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
spec kolide.OptionsSpec
|
||||
}{
|
||||
{
|
||||
kolide.OptionsSpec{
|
||||
json.RawMessage(`{"foo":"bar"}`),
|
||||
kolide.OptionsOverrides{},
|
||||
},
|
||||
},
|
||||
{
|
||||
kolide.OptionsSpec{
|
||||
json.RawMessage(`{"bing":"bang","boom":2}`),
|
||||
kolide.OptionsOverrides{
|
||||
map[string]json.RawMessage{
|
||||
"foobar": json.RawMessage(`{"manzanita":"scratch"}`),
|
||||
"froobling": json.RawMessage(`{"doornail":"mumble"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var returnOptions, gotOptions *kolide.OptionsSpec
|
||||
ds := &mock.Store{
|
||||
OsqueryOptionsStore: mock.OsqueryOptionsStore{
|
||||
GetOptionsFunc: func() (*kolide.OptionsSpec, error) {
|
||||
return returnOptions, nil
|
||||
},
|
||||
ApplyOptionsFunc: func(options *kolide.OptionsSpec) error {
|
||||
gotOptions = options
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
svc := service{
|
||||
ds: ds,
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run("", func(t *testing.T) {
|
||||
returnOptions = &tt.spec
|
||||
gotOptions = nil
|
||||
|
||||
yml, err := svc.GetOptionsYaml()
|
||||
require.Nil(t, err)
|
||||
|
||||
err = svc.ApplyOptionsYaml(yml)
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.Equal(t, returnOptions, gotOptions)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
@ -20,7 +20,6 @@ import (
|
||||
"github.com/kolide/fleet/server/kolide"
|
||||
"github.com/kolide/fleet/server/mock"
|
||||
"github.com/kolide/fleet/server/pubsub"
|
||||
"github.com/kolide/fleet/server/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -351,92 +350,105 @@ func TestLabelQueries(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetClientConfig(t *testing.T) {
|
||||
ds, err := inmem.New(config.TestConfig())
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, ds.MigrateData())
|
||||
|
||||
mockClock := clock.NewMockClock()
|
||||
|
||||
svc, err := newTestServiceWithClock(ds, nil, mockClock)
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
hosts, err := ds.ListHosts(kolide.ListOptions{})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, hosts, 0)
|
||||
|
||||
_, err = svc.EnrollAgent(ctx, "", "user.local")
|
||||
assert.Nil(t, err)
|
||||
|
||||
hosts, err = ds.ListHosts(kolide.ListOptions{})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, hosts, 1)
|
||||
host := hosts[0]
|
||||
|
||||
ctx = hostctx.NewContext(ctx, *host)
|
||||
|
||||
// with no queries, packs, labels, etc. verify the state of a fresh host
|
||||
// asking for a config
|
||||
config, err := svc.GetClientConfig(ctx)
|
||||
require.Nil(t, err)
|
||||
assert.NotNil(t, config)
|
||||
val, ok := config.Options["disable_distributed"]
|
||||
require.True(t, ok)
|
||||
disabled, ok := val.(bool)
|
||||
require.True(t, ok)
|
||||
assert.False(t, disabled)
|
||||
val, ok = config.Options["pack_delimiter"]
|
||||
require.True(t, ok)
|
||||
delim, ok := val.(string)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "/", delim)
|
||||
|
||||
// this will be greater than 0 if we ever start inserting an administration
|
||||
// pack
|
||||
assert.Len(t, config.Packs, 0)
|
||||
|
||||
// let's populate the database with some info
|
||||
|
||||
infoQuery := &kolide.Query{
|
||||
Name: "Info",
|
||||
Query: "select * from osquery_info;",
|
||||
ds := new(mock.Store)
|
||||
ds.ListDecoratorsFunc = func(opt ...kolide.OptionalArg) ([]*kolide.Decorator, error) {
|
||||
return []*kolide.Decorator{}, nil
|
||||
}
|
||||
infoQueryInterval := uint(60)
|
||||
infoQuery, err = ds.NewQuery(infoQuery)
|
||||
assert.Nil(t, err)
|
||||
|
||||
monitoringPack := &kolide.Pack{
|
||||
Name: "monitoring",
|
||||
ds.ListPacksFunc = func(opt kolide.ListOptions) ([]*kolide.Pack, error) {
|
||||
return []*kolide.Pack{}, nil
|
||||
}
|
||||
_, err = ds.NewPack(monitoringPack)
|
||||
assert.Nil(t, err)
|
||||
|
||||
test.NewScheduledQuery(t, ds, monitoringPack.ID, infoQuery.ID, infoQueryInterval, false, false)
|
||||
|
||||
mysqlLabel := &kolide.Label{
|
||||
Name: "MySQL Monitoring",
|
||||
Query: "select pid from processes where name = 'mysqld';",
|
||||
ds.ListLabelsForHostFunc = func(hid uint) ([]kolide.Label, error) {
|
||||
return []kolide.Label{
|
||||
{ID: 1, Name: "foo_label"},
|
||||
}, nil
|
||||
}
|
||||
ds.ListLabelsForPackFunc = func(pid uint) ([]*kolide.Label, error) {
|
||||
switch pid {
|
||||
case 1, 2:
|
||||
return []*kolide.Label{
|
||||
{ID: 1, Name: "foo_label"},
|
||||
}, nil
|
||||
default:
|
||||
return []*kolide.Label{}, nil
|
||||
}
|
||||
}
|
||||
ds.ListExplicitHostsInPackFunc = func(pid uint, opt kolide.ListOptions) ([]uint, error) {
|
||||
switch pid {
|
||||
case 4:
|
||||
return []uint{1}, nil
|
||||
default:
|
||||
return []uint{}, nil
|
||||
}
|
||||
}
|
||||
ds.ListScheduledQueriesInPackFunc = func(pid uint, opt kolide.ListOptions) ([]*kolide.ScheduledQuery, error) {
|
||||
tru := true
|
||||
switch pid {
|
||||
case 1:
|
||||
return []*kolide.ScheduledQuery{
|
||||
{Name: "time", Query: "select * from time", Interval: 30},
|
||||
}, nil
|
||||
case 4:
|
||||
return []*kolide.ScheduledQuery{
|
||||
{Name: "foobar", Query: "select 3", Interval: 20},
|
||||
{Name: "froobing", Query: "select 'guacamole'", Interval: 60, Snapshot: &tru},
|
||||
}, nil
|
||||
default:
|
||||
return []*kolide.ScheduledQuery{}, nil
|
||||
}
|
||||
}
|
||||
ds.OptionsForPlatformFunc = func(platform string) (json.RawMessage, error) {
|
||||
return json.RawMessage(`{"options":{
|
||||
"distributed_interval": 11,
|
||||
"logger_tls_period": 33
|
||||
}}`), nil
|
||||
}
|
||||
ds.SaveHostFunc = func(host *kolide.Host) error {
|
||||
return nil
|
||||
}
|
||||
mysqlLabel, err = ds.NewLabel(mysqlLabel)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = ds.AddLabelToPack(mysqlLabel.ID, monitoringPack.ID)
|
||||
assert.Nil(t, err)
|
||||
svc, err := newTestService(ds, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ds.RecordLabelQueryExecutions(
|
||||
host,
|
||||
map[uint]bool{mysqlLabel.ID: true},
|
||||
mockClock.Now(),
|
||||
ctx := hostctx.NewContext(context.Background(), kolide.Host{ID: 1})
|
||||
|
||||
expectedOptions := map[string]interface{}{
|
||||
"distributed_interval": float64(11),
|
||||
"logger_tls_period": float64(33),
|
||||
}
|
||||
|
||||
// No packs loaded yet
|
||||
conf, err := svc.GetClientConfig(ctx)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, map[string]interface{}{"options": expectedOptions}, conf)
|
||||
|
||||
// Now add packs
|
||||
ds.ListPacksFunc = func(opt kolide.ListOptions) ([]*kolide.Pack, error) {
|
||||
return []*kolide.Pack{
|
||||
{ID: 1, Name: "pack_by_label"},
|
||||
{ID: 2, Name: "disabled_pack", Disabled: true},
|
||||
{ID: 3, Name: "not_matching_pack"},
|
||||
{ID: 4, Name: "pack_by_explicit_host"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
conf, err = svc.GetClientConfig(ctx)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, expectedOptions, conf["options"])
|
||||
assert.JSONEq(t, `{
|
||||
"pack_by_explicit_host": {
|
||||
"queries": {
|
||||
"foobar":{"query":"select 3","interval":20},
|
||||
"froobing":{"query":"select 'guacamole'","interval":60,"snapshot":true}
|
||||
}
|
||||
},
|
||||
"pack_by_label": {
|
||||
"queries":{
|
||||
"time":{"query":"select * from time","interval":30}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
string(conf["packs"].(json.RawMessage)),
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// with a minimal setup of packs, labels, and queries, will our host get the
|
||||
// pack
|
||||
config, err = svc.GetClientConfig(ctx)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, config.Packs, 1)
|
||||
assert.Len(t, config.Packs["monitoring"].Queries, 1)
|
||||
}
|
||||
|
||||
func TestDetailQueriesWithEmptyStrings(t *testing.T) {
|
||||
@ -975,22 +987,11 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
ds.ListLabelsForHostFunc = func(hid uint) ([]kolide.Label, error) {
|
||||
return []kolide.Label{}, nil
|
||||
}
|
||||
ds.AppConfigFunc = func() (*kolide.AppConfig, error) {
|
||||
return &kolide.AppConfig{FIMInterval: 400}, nil
|
||||
}
|
||||
ds.FIMSectionsFunc = func() (kolide.FIMSections, error) {
|
||||
sections := kolide.FIMSections{
|
||||
"etc": []string{
|
||||
"/etc/%%",
|
||||
},
|
||||
}
|
||||
return sections, nil
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
initHost kolide.Host
|
||||
finalHost kolide.Host
|
||||
configOptions map[string]interface{}
|
||||
configOptions json.RawMessage
|
||||
saveHostCalled bool
|
||||
}{
|
||||
// Both updated
|
||||
@ -1003,11 +1004,11 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
LoggerTLSPeriod: 33,
|
||||
ConfigTLSRefresh: 60,
|
||||
},
|
||||
map[string]interface{}{
|
||||
json.RawMessage(`{"options": {
|
||||
"distributed_interval": 11,
|
||||
"logger_tls_period": 33,
|
||||
"logger_plugin": "tls",
|
||||
},
|
||||
"logger_plugin": "tls"
|
||||
}}`),
|
||||
true,
|
||||
},
|
||||
// Only logger_tls_period updated
|
||||
@ -1021,10 +1022,10 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
LoggerTLSPeriod: 33,
|
||||
ConfigTLSRefresh: 60,
|
||||
},
|
||||
map[string]interface{}{
|
||||
json.RawMessage(`{"options": {
|
||||
"distributed_interval": 11,
|
||||
"logger_tls_period": 33,
|
||||
},
|
||||
"logger_tls_period": 33
|
||||
}}`),
|
||||
true,
|
||||
},
|
||||
// Only distributed_interval updated
|
||||
@ -1038,10 +1039,10 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
LoggerTLSPeriod: 33,
|
||||
ConfigTLSRefresh: 60,
|
||||
},
|
||||
map[string]interface{}{
|
||||
json.RawMessage(`{"options": {
|
||||
"distributed_interval": 11,
|
||||
"logger_tls_period": 33,
|
||||
},
|
||||
"logger_tls_period": 33
|
||||
}}`),
|
||||
true,
|
||||
},
|
||||
// Kolide not managing distributed_interval
|
||||
@ -1055,9 +1056,9 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
LoggerTLSPeriod: 33,
|
||||
ConfigTLSRefresh: 60,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"logger_tls_period": 33,
|
||||
},
|
||||
json.RawMessage(`{"options":{
|
||||
"logger_tls_period": 33
|
||||
}}`),
|
||||
true,
|
||||
},
|
||||
// SaveHost should not be called with no changes
|
||||
@ -1072,21 +1073,19 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
LoggerTLSPeriod: 33,
|
||||
ConfigTLSRefresh: 60,
|
||||
},
|
||||
map[string]interface{}{
|
||||
json.RawMessage(`{"options":{
|
||||
"distributed_interval": 11,
|
||||
"logger_tls_period": 33,
|
||||
},
|
||||
"logger_tls_period": 33
|
||||
}}`),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
ds.FIMSectionsFuncInvoked = false
|
||||
|
||||
t.Run("", func(t *testing.T) {
|
||||
ctx := hostctx.NewContext(context.Background(), tt.initHost)
|
||||
|
||||
ds.GetOsqueryConfigOptionsFunc = func() (map[string]interface{}, error) {
|
||||
ds.OptionsForPlatformFunc = func(platform string) (json.RawMessage, error) {
|
||||
return tt.configOptions, nil
|
||||
}
|
||||
|
||||
@ -1097,22 +1096,9 @@ func TestUpdateHostIntervals(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg, err := svc.GetClientConfig(ctx)
|
||||
_, err := svc.GetClientConfig(ctx)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, tt.saveHostCalled, saveHostCalled)
|
||||
require.True(t, ds.FIMSectionsFuncInvoked)
|
||||
require.Condition(t, func() bool {
|
||||
_, ok := cfg.Schedule["file_events"]
|
||||
return ok
|
||||
})
|
||||
assert.Equal(t, 400, int(cfg.Schedule["file_events"].Interval))
|
||||
assert.Equal(t, "SELECT * FROM file_events;", cfg.Schedule["file_events"].Query)
|
||||
require.NotNil(t, cfg.FilePaths)
|
||||
require.Condition(t, func() bool {
|
||||
_, ok := cfg.FilePaths["etc"]
|
||||
return ok
|
||||
})
|
||||
assert.Len(t, cfg.FilePaths["etc"], 1)
|
||||
})
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user