mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
Pattern for defining and loading application configuration (#149)
This provides a new pattern for defining and loading configuration for the Kolide server. With this PR, configuration is stored in the `config.KolideConfig` struct, and loaded through `config.Manager`. Refer to the patterns in `config/config.go` and `cli/prepare.go` to see how configuration is defined and used. A follow-up PR will work on removing further references to `viper` throughout the codebase, instead preferring to access configuration through the strongly typed `KolideConfig`.
This commit is contained in:
parent
370cc4efa8
commit
6cb05a58e6
6 changed files with 502 additions and 255 deletions
38
cli/config_dump.go
Normal file
38
cli/config_dump.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/kolide/kolide-ose/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func createConfigDumpCmd(configManager config.Manager) *cobra.Command {
|
||||
var configDumpCmd = &cobra.Command{
|
||||
Use: "config_dump",
|
||||
Short: "Dump the parsed configuration in yaml format",
|
||||
Long: `
|
||||
Dump the parsed configuration in yaml format.
|
||||
|
||||
Kolide retrieves configuration options from many locations, and it can be
|
||||
useful to see the result of merging those configs.
|
||||
|
||||
The following precedence is used when reading configs:
|
||||
1. CLI flags
|
||||
2. Environment Variables
|
||||
3. Config File
|
||||
4. Default Values
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
buf, err := yaml.Marshal(configManager.LoadConfig())
|
||||
if err != nil {
|
||||
logrus.Fatal("Error marshalling config to yaml")
|
||||
}
|
||||
fmt.Println(string(buf))
|
||||
}}
|
||||
|
||||
return configDumpCmd
|
||||
}
|
||||
140
cli/prepare.go
140
cli/prepare.go
|
|
@ -4,81 +4,87 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/kolide/kolide-ose/config"
|
||||
"github.com/kolide/kolide-ose/datastore"
|
||||
"github.com/kolide/kolide-ose/kolide"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func init() {
|
||||
prepareCmd.AddCommand(dbCmd)
|
||||
rootCmd.AddCommand(prepareCmd)
|
||||
rootCmd.AddCommand(testDataCmd)
|
||||
}
|
||||
func createPrepareCmd(configManager config.Manager) *cobra.Command {
|
||||
|
||||
var prepareCmd = &cobra.Command{
|
||||
Use: "prepare",
|
||||
Short: "Subcommands for initializing kolide infrastructure",
|
||||
Long: `
|
||||
var prepareCmd = &cobra.Command{
|
||||
Use: "prepare",
|
||||
Short: "Subcommands for initializing kolide infrastructure",
|
||||
Long: `
|
||||
Subcommands for initializing kolide infrastructure
|
||||
|
||||
To setup kolide infrastructure, use one of the available commands.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
var dbCmd = &cobra.Command{
|
||||
Use: "db",
|
||||
Short: "Given correct database configurations, prepare the databases for use",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
connString := fmt.Sprintf(
|
||||
"%s:%s@(%s)/%s?charset=utf8&parseTime=True&loc=Local",
|
||||
viper.GetString("mysql.username"),
|
||||
viper.GetString("mysql.password"),
|
||||
viper.GetString("mysql.address"),
|
||||
viper.GetString("mysql.database"),
|
||||
)
|
||||
ds, err := datastore.New("gorm-mysql", connString)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("error creating db connection")
|
||||
}
|
||||
if err := ds.Drop(); err != nil {
|
||||
logrus.WithError(err).Fatal("error dropping db tables")
|
||||
}
|
||||
|
||||
if err := ds.Migrate(); err != nil {
|
||||
logrus.WithError(err).Fatal("error setting up db schema")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var testDataCmd = &cobra.Command{
|
||||
Use: "test-data",
|
||||
Short: "Generate test data",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, arg []string) {
|
||||
connString := fmt.Sprintf(
|
||||
"%s:%s@(%s)/%s?charset=utf8&parseTime=True&loc=Local",
|
||||
viper.GetString("mysql.username"),
|
||||
viper.GetString("mysql.password"),
|
||||
viper.GetString("mysql.address"),
|
||||
viper.GetString("mysql.database"),
|
||||
)
|
||||
ds, err := datastore.New("gorm-mysql", connString)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("error creating db connection")
|
||||
}
|
||||
|
||||
admin, err := kolide.NewUser("admin", "admin", "admin@kolide.co", true, false)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Could not create new user object")
|
||||
}
|
||||
_, err = ds.NewUser(admin)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Could not create new user in the database")
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
var dbCmd = &cobra.Command{
|
||||
Use: "db",
|
||||
Short: "Given correct database configurations, prepare the databases for use",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := configManager.LoadConfig()
|
||||
connString := fmt.Sprintf(
|
||||
"%s:%s@(%s)/%s?charset=utf8&parseTime=True&loc=Local",
|
||||
config.Mysql.Username,
|
||||
config.Mysql.Password,
|
||||
config.Mysql.Address,
|
||||
config.Mysql.Database,
|
||||
)
|
||||
ds, err := datastore.New("gorm-mysql", connString)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("error creating db connection")
|
||||
}
|
||||
if err := ds.Drop(); err != nil {
|
||||
logrus.WithError(err).Fatal("error dropping db tables")
|
||||
}
|
||||
|
||||
if err := ds.Migrate(); err != nil {
|
||||
logrus.WithError(err).Fatal("error setting up db schema")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
prepareCmd.AddCommand(dbCmd)
|
||||
|
||||
var testDataCmd = &cobra.Command{
|
||||
Use: "test-data",
|
||||
Short: "Generate test data",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, arg []string) {
|
||||
config := configManager.LoadConfig()
|
||||
connString := fmt.Sprintf(
|
||||
"%s:%s@(%s)/%s?charset=utf8&parseTime=True&loc=Local",
|
||||
config.Mysql.Username,
|
||||
config.Mysql.Password,
|
||||
config.Mysql.Address,
|
||||
config.Mysql.Database,
|
||||
)
|
||||
ds, err := datastore.New("gorm-mysql", connString)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("error creating db connection")
|
||||
}
|
||||
|
||||
admin, err := kolide.NewUser("admin", "admin", "admin@kolide.co", true, false)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Could not create new user object")
|
||||
}
|
||||
_, err = ds.NewUser(admin)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Could not create new user in the database")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
prepareCmd.AddCommand(testDataCmd)
|
||||
|
||||
return prepareCmd
|
||||
|
||||
}
|
||||
|
|
|
|||
66
cli/root.go
66
cli/root.go
|
|
@ -8,22 +8,28 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&config.File, "config", "", "Path to a configuration file")
|
||||
}
|
||||
|
||||
// Launch is the entrypoint that sets up and runs the Kolide commands.
|
||||
func Launch() {
|
||||
rootCmd := createRootCmd()
|
||||
|
||||
configManager := config.NewManager(rootCmd)
|
||||
|
||||
rootCmd.AddCommand(createPrepareCmd(configManager))
|
||||
rootCmd.AddCommand(createServeCmd(configManager))
|
||||
rootCmd.AddCommand(createConfigDumpCmd(configManager))
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "kolide",
|
||||
Short: "osquery management and orchestration",
|
||||
Long: `
|
||||
func createRootCmd() *cobra.Command {
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "kolide",
|
||||
Short: "osquery management and orchestration",
|
||||
Long: `
|
||||
osquery management and orchestration
|
||||
|
||||
Configurable Options:
|
||||
|
|
@ -31,42 +37,10 @@ Configurable Options:
|
|||
Options may be supplied in a yaml configuration file or via environment
|
||||
variables. You only need to define the configuration values for which you
|
||||
wish to override the default value.
|
||||
|
||||
Available Configurations:
|
||||
|
||||
mysql:
|
||||
address (string) (KOLIDE_MYSQL_ADDRESS)
|
||||
username (string) (KOLIDE_MYSQL_USERNAME)
|
||||
password (string) (KOLIDE_MYSQL_PASSWORD)
|
||||
database (string) (KOLIDE_MYSQL_DATABASE)
|
||||
server:
|
||||
address (string) (KOLIDE_SERVER_ADDRESS)
|
||||
cert (string) (KOLIDE_SERVER_CERT)
|
||||
key (string) (KOLIDE_SERVER_KEY)
|
||||
auth:
|
||||
jwt_key (string) (KOLIDE_AUTH_JWT_KEY)
|
||||
salt_key_size (int) (KOLIDE_AUTH_SALT_KEY_SIZE)
|
||||
bcrypt_cost (int) (KOLIDE_AUTH_BCRYPT_COST)
|
||||
app:
|
||||
web_address (string) (KOLIDE_APP_WEB_ADDRESS)
|
||||
smtp:
|
||||
server (string) (KOLIDE_SMTP_SERVER)
|
||||
username (string) (KOLIDE_SMTP_USERNAME)
|
||||
password (string) (KOLIDE_SMTP_PASSWORD)
|
||||
pool_connections (int) (KOLIDE_SMTP_POOL_CONNECTIONS)
|
||||
token_key_size (int) (KOLIDE_SMTP_TOKEN_KEY_SIZE)
|
||||
session:
|
||||
key_size (int) (KOLIDE_SESSION_KEY_SIZE)
|
||||
expiration_seconds (float64) (KOLIDE_SESSION_EXPIRATION_SECONDS)
|
||||
cookie_name (string) (KOLIDE_SESSION_COOKIE_NAME)
|
||||
osquery:
|
||||
enroll_secret (string) (KOLIDE_OSQUERY_ENROLL_SECRET)
|
||||
node_key_size (int) (KOLIDE_OSQUERY_NODE_KEY_SIZE)
|
||||
status_log_file (string) (KOLIDE_OSQUERY_STATUS_LOG_FILE)
|
||||
result_log_file (string) (KOLIDE_OSQUERY_RESULT_LOG_FILE)
|
||||
label_up_interval (int) (KOLIDE_OSQUERY_LABEL_UP_INTERVAL)
|
||||
logging:
|
||||
debug (bool) (KOLIDE_LOGGING_DEBUG)
|
||||
disable_banner (bool) (KOLIDE_LOGGING_DISABLE_BANNER)
|
||||
`,
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().String("config", "", "Path to a configuration file")
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
|
|
|||
132
cli/serve.go
132
cli/serve.go
|
|
@ -9,6 +9,7 @@ import (
|
|||
"syscall"
|
||||
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
"github.com/kolide/kolide-ose/config"
|
||||
"github.com/kolide/kolide-ose/datastore"
|
||||
"github.com/kolide/kolide-ose/kolide"
|
||||
"github.com/kolide/kolide-ose/server"
|
||||
|
|
@ -16,14 +17,11 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Launch the kolide server",
|
||||
Long: `
|
||||
func createServeCmd(configManager config.Manager) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Launch the kolide server",
|
||||
Long: `
|
||||
Launch the kolide server
|
||||
|
||||
Use kolide serve to run the main HTTPS server. The Kolide server bundles
|
||||
|
|
@ -31,72 +29,74 @@ together all static assets and dependent libraries into a statically linked go
|
|||
binary (which you're executing right now). Use the options below to customize
|
||||
the way that the kolide server works.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
httpAddr = flag.String("http.addr", ":8080", "HTTP listen address")
|
||||
ctx = context.Background()
|
||||
logger kitlog.Logger
|
||||
)
|
||||
flag.Parse()
|
||||
logger = kitlog.NewLogfmtLogger(os.Stderr)
|
||||
logger = kitlog.NewContext(logger).With("ts", kitlog.DefaultTimestampUTC)
|
||||
|
||||
ds, _ := datastore.New("inmem", "")
|
||||
svcConfig := server.ServiceConfig{
|
||||
Datastore: ds,
|
||||
SessionCookieName: "KolideSession",
|
||||
BcryptCost: 12,
|
||||
SaltKeySize: 24,
|
||||
JWTKey: "foobar",
|
||||
}
|
||||
svcLogger := kitlog.NewContext(logger).With("component", "service")
|
||||
var svc kolide.Service
|
||||
{ // temp create an admin user
|
||||
svc, _ = server.NewService(svcConfig)
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
name = "admin"
|
||||
username = "admin"
|
||||
password = "secret"
|
||||
email = "admin@kolide.co"
|
||||
enabled = true
|
||||
isAdmin = true
|
||||
httpAddr = flag.String("http.addr", ":8080", "HTTP listen address")
|
||||
ctx = context.Background()
|
||||
logger kitlog.Logger
|
||||
)
|
||||
admin := kolide.UserPayload{
|
||||
Name: &name,
|
||||
Username: &username,
|
||||
Password: &password,
|
||||
Email: &email,
|
||||
Enabled: &enabled,
|
||||
Admin: &isAdmin,
|
||||
flag.Parse()
|
||||
logger = kitlog.NewLogfmtLogger(os.Stderr)
|
||||
logger = kitlog.NewContext(logger).With("ts", kitlog.DefaultTimestampUTC)
|
||||
|
||||
ds, _ := datastore.New("inmem", "")
|
||||
svcConfig := server.ServiceConfig{
|
||||
Datastore: ds,
|
||||
SessionCookieName: "KolideSession",
|
||||
BcryptCost: 12,
|
||||
SaltKeySize: 24,
|
||||
JWTKey: "foobar",
|
||||
}
|
||||
_, err := svc.NewUser(ctx, admin)
|
||||
if err != nil {
|
||||
logger.Log("err", err)
|
||||
os.Exit(1)
|
||||
svcLogger := kitlog.NewContext(logger).With("component", "service")
|
||||
var svc kolide.Service
|
||||
{ // temp create an admin user
|
||||
svc, _ = server.NewService(svcConfig)
|
||||
svc, _ = server.NewService(svcConfig)
|
||||
var (
|
||||
name = "admin"
|
||||
username = "admin"
|
||||
password = "secret"
|
||||
email = "admin@kolide.co"
|
||||
enabled = true
|
||||
isAdmin = true
|
||||
)
|
||||
admin := kolide.UserPayload{
|
||||
Name: &name,
|
||||
Username: &username,
|
||||
Password: &password,
|
||||
Email: &email,
|
||||
Enabled: &enabled,
|
||||
Admin: &isAdmin,
|
||||
}
|
||||
_, err := svc.NewUser(ctx, admin)
|
||||
if err != nil {
|
||||
logger.Log("err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
svc = server.NewLoggingService(svc, svcLogger)
|
||||
}
|
||||
svc = server.NewLoggingService(svc, svcLogger)
|
||||
}
|
||||
|
||||
httpLogger := kitlog.NewContext(logger).With("component", "http")
|
||||
httpLogger := kitlog.NewContext(logger).With("component", "http")
|
||||
|
||||
apiHandler := server.MakeHandler(ctx, svc, svcConfig.JWTKey, ds, httpLogger)
|
||||
http.Handle("/api/", accessControl(apiHandler))
|
||||
http.Handle("/assets/", server.ServeStaticAssets("/assets/"))
|
||||
http.Handle("/", server.ServeFrontend())
|
||||
apiHandler := server.MakeHandler(ctx, svc, svcConfig.JWTKey, ds, httpLogger)
|
||||
http.Handle("/api/", accessControl(apiHandler))
|
||||
http.Handle("/assets/", server.ServeStaticAssets("/assets/"))
|
||||
http.Handle("/", server.ServeFrontend())
|
||||
|
||||
errs := make(chan error, 2)
|
||||
go func() {
|
||||
logger.Log("transport", "http", "address", *httpAddr, "msg", "listening")
|
||||
errs <- http.ListenAndServe(*httpAddr, nil)
|
||||
}()
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
errs := make(chan error, 2)
|
||||
go func() {
|
||||
logger.Log("transport", "http", "address", *httpAddr, "msg", "listening")
|
||||
errs <- http.ListenAndServe(*httpAddr, nil)
|
||||
}()
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
|
||||
logger.Log("terminated", <-errs)
|
||||
},
|
||||
logger.Log("terminated", <-errs)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// cors headers
|
||||
|
|
|
|||
380
config/config.go
380
config/config.go
|
|
@ -1,100 +1,328 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
// File may or may not contain the path to the config file
|
||||
File string
|
||||
const (
|
||||
envPrefix = "KOLIDE"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
// MysqlConfig defines configs related to MySQL
|
||||
type MysqlConfig struct {
|
||||
Address string
|
||||
Username string
|
||||
Password string
|
||||
Database string
|
||||
}
|
||||
|
||||
// Due to a deficiency in viper (https://github.com/spf13/viper/issues/71), one
|
||||
// can not set the default values of nested config elements. For example, if the
|
||||
// "mysql" section of the config allows a user to define "username", "password",
|
||||
// and "database", but the only wants to override the default for "username".
|
||||
// they should be able to create a config which looks like:
|
||||
//
|
||||
// mysql:
|
||||
// username: foobar
|
||||
//
|
||||
// In viper, that would nullify the default values of all other config keys in
|
||||
// the mysql section ("mysql.*"). To get around this, instead of using the
|
||||
// provided API for setting default values, after we've read the config and env,
|
||||
// we manually check to see if the value has been set and, if it hasn't, we set
|
||||
// it manually.
|
||||
func setDefaultConfigValue(key string, value interface{}) {
|
||||
if viper.Get(key) == nil {
|
||||
viper.Set(key, value)
|
||||
// ServerConfig defines configs related to the Kolide server
|
||||
type ServerConfig struct {
|
||||
Address string
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
|
||||
// AuthConfig defines configs related to user authorization
|
||||
type AuthConfig struct {
|
||||
JwtKey string
|
||||
BcryptCost int
|
||||
SaltKeySize int
|
||||
}
|
||||
|
||||
// AppConfig defines configs related to HTTP
|
||||
type AppConfig struct {
|
||||
WebAddress string
|
||||
}
|
||||
|
||||
// SMTPConfig defines configs related to SMTP email
|
||||
type SMTPConfig struct {
|
||||
Server string
|
||||
Username string
|
||||
Password string
|
||||
PoolConnections int
|
||||
TokenKeySize int
|
||||
}
|
||||
|
||||
// SessionConfig defines configs related to user sessions
|
||||
type SessionConfig struct {
|
||||
KeySize int
|
||||
ExpirationSeconds int
|
||||
CookieName string
|
||||
}
|
||||
|
||||
// OsqueryConfig defines configs related to osquery
|
||||
type OsqueryConfig struct {
|
||||
EnrollSecret string
|
||||
NodeKeySize int
|
||||
StatusLogFile string
|
||||
ResultLogFile string
|
||||
}
|
||||
|
||||
// LoggingConfig defines configs related to logging
|
||||
type LoggingConfig struct {
|
||||
Debug bool
|
||||
DisableBanner bool
|
||||
}
|
||||
|
||||
// KolideConfig stores the application configuration. Each subcategory is
|
||||
// broken up into it's own struct, defined above. When editing any of these
|
||||
// structs, Manager.addConfigs and Manager.LoadConfig should be
|
||||
// updated to set and retrieve the configurations as appropriate.
|
||||
type KolideConfig struct {
|
||||
Mysql MysqlConfig
|
||||
Server ServerConfig
|
||||
Auth AuthConfig
|
||||
App AppConfig
|
||||
SMTP SMTPConfig
|
||||
Session SessionConfig
|
||||
Osquery OsqueryConfig
|
||||
Logging LoggingConfig
|
||||
}
|
||||
|
||||
// addConfigs adds the configuration keys and default values that will be
|
||||
// filled into the KolideConfig struct
|
||||
func (man Manager) addConfigs() {
|
||||
// MySQL
|
||||
man.addConfigString("mysql.address", "localhost:3306")
|
||||
man.addConfigString("mysql.username", "kolide")
|
||||
man.addConfigString("mysql.password", "kolide")
|
||||
man.addConfigString("mysql.database", "kolide")
|
||||
|
||||
// Server
|
||||
man.addConfigString("server.address", "0.0.0.0:8080")
|
||||
man.addConfigString("server.cert", "./tools/osquery/kolide.crt")
|
||||
man.addConfigString("server.key", "./tools/osquery/kolide.key")
|
||||
|
||||
// Auth
|
||||
man.addConfigString("auth.jwt_key", "CHANGEME")
|
||||
man.addConfigInt("auth.bcrypt_cost", 12)
|
||||
man.addConfigInt("auth.salt_key_size", 24)
|
||||
|
||||
// App
|
||||
man.addConfigString("app.web_address", "0.0.0.0:8080")
|
||||
|
||||
// SMTP
|
||||
man.addConfigString("smtp.server", "")
|
||||
man.addConfigString("smtp.username", "")
|
||||
man.addConfigString("smtp.password", "")
|
||||
man.addConfigInt("smtp.pool_connections", 4)
|
||||
man.addConfigInt("smtp.token_key_size", 24)
|
||||
|
||||
// Session
|
||||
man.addConfigInt("session.key_size", 64)
|
||||
man.addConfigInt("session.expiration_seconds", 60*60*24*90)
|
||||
man.addConfigString("session.cookie_name", "KolideSession")
|
||||
|
||||
// Osquery
|
||||
man.addConfigString("osquery.enroll_secret", "")
|
||||
man.addConfigInt("osquery.node_key_size", 24)
|
||||
man.addConfigString("osquery.status_log_file", "/tmp/osquery_status")
|
||||
man.addConfigString("osquery.result_log_file", "/tmp/osquery_result")
|
||||
|
||||
// Logging
|
||||
man.addConfigBool("logging.debug", false)
|
||||
man.addConfigBool("logging.disable_banner", false)
|
||||
}
|
||||
|
||||
// LoadConfig will load the config variables into a fully initialized
|
||||
// KolideConfig struct
|
||||
func (man Manager) LoadConfig() KolideConfig {
|
||||
man.loadConfigFile()
|
||||
|
||||
return KolideConfig{
|
||||
Mysql: MysqlConfig{
|
||||
Address: man.getConfigString("mysql.address"),
|
||||
Username: man.getConfigString("mysql.username"),
|
||||
Password: man.getConfigString("mysql.password"),
|
||||
Database: man.getConfigString("mysql.database"),
|
||||
},
|
||||
Server: ServerConfig{
|
||||
Address: man.getConfigString("server.address"),
|
||||
Cert: man.getConfigString("server.cert"),
|
||||
Key: man.getConfigString("server.key"),
|
||||
},
|
||||
Auth: AuthConfig{
|
||||
JwtKey: man.getConfigString("auth.jwt_key"),
|
||||
BcryptCost: man.getConfigInt("auth.bcrypt_cost"),
|
||||
SaltKeySize: man.getConfigInt("auth.salt_key_size"),
|
||||
},
|
||||
App: AppConfig{
|
||||
WebAddress: man.getConfigString("app.web_address"),
|
||||
},
|
||||
SMTP: SMTPConfig{
|
||||
Server: man.getConfigString("smtp.server"),
|
||||
Username: man.getConfigString("smtp.username"),
|
||||
Password: man.getConfigString("smtp.password"),
|
||||
PoolConnections: man.getConfigInt("smtp.pool_connections"),
|
||||
TokenKeySize: man.getConfigInt("smtp.token_key_size"),
|
||||
},
|
||||
Session: SessionConfig{
|
||||
KeySize: man.getConfigInt("session.key_size"),
|
||||
ExpirationSeconds: man.getConfigInt("session.expiration_seconds"),
|
||||
CookieName: man.getConfigString("session.cookie_name"),
|
||||
},
|
||||
Osquery: OsqueryConfig{
|
||||
EnrollSecret: man.getConfigString("osquery.enroll_secret"),
|
||||
NodeKeySize: man.getConfigInt("osquery.node_key_size"),
|
||||
StatusLogFile: man.getConfigString("osquery.status_log_file"),
|
||||
ResultLogFile: man.getConfigString("osquery.result_log_file"),
|
||||
},
|
||||
Logging: LoggingConfig{
|
||||
Debug: man.getConfigBool("logging.debug"),
|
||||
DisableBanner: man.getConfigBool("logging.disable_banner"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if File != "" {
|
||||
viper.SetConfigFile(File)
|
||||
// envNameFromConfigKey converts a config key into the corresponding
|
||||
// environment variable name
|
||||
func envNameFromConfigKey(key string) string {
|
||||
return envPrefix + "_" + strings.ToUpper(strings.Replace(key, ".", "_", -1))
|
||||
}
|
||||
|
||||
// flagNameFromConfigKey converts a config key into the corresponding flag name
|
||||
func flagNameFromConfigKey(key string) string {
|
||||
return strings.Replace(key, ".", "_", -1)
|
||||
}
|
||||
|
||||
// Manager manages the addition and retrieval of config values for Kolide
|
||||
// configs. It's only public API method is LoadConfig, which will return the
|
||||
// populated KolideConfig struct.
|
||||
type Manager struct {
|
||||
viper *viper.Viper
|
||||
command *cobra.Command
|
||||
defaults map[string]interface{}
|
||||
}
|
||||
|
||||
// NewManager initializes a Manager wrapping the provided cobra
|
||||
// command. All config flags will be attached to that command (and inherited by
|
||||
// the subcommands). Typically this should be called just once, with the root
|
||||
// command.
|
||||
func NewManager(command *cobra.Command) Manager {
|
||||
man := Manager{
|
||||
viper: viper.New(),
|
||||
command: command,
|
||||
defaults: map[string]interface{}{},
|
||||
}
|
||||
viper.SetConfigName("kolide")
|
||||
viper.AddConfigPath(".")
|
||||
viper.AddConfigPath("$HOME")
|
||||
viper.AddConfigPath("./tools/app")
|
||||
viper.AddConfigPath("/etc/kolide")
|
||||
man.addConfigs()
|
||||
return man
|
||||
}
|
||||
|
||||
viper.SetConfigType("yaml")
|
||||
// addDefault will check for duplication, then add a default value to the
|
||||
// defaults map
|
||||
func (man Manager) addDefault(key string, defVal interface{}) {
|
||||
if _, exists := man.defaults[key]; exists {
|
||||
panic("Trying to add duplicate config for key " + key)
|
||||
}
|
||||
|
||||
viper.SetEnvPrefix("KOLIDE")
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
man.defaults[key] = defVal
|
||||
}
|
||||
|
||||
err := viper.ReadInConfig()
|
||||
// getInterfaceVal is a helper function used by the getConfig* functions to
|
||||
// retrieve the config value as interface{}, which will then be cast to the
|
||||
// appropriate type by the getConfig* function.
|
||||
func (man Manager) getInterfaceVal(key string) interface{} {
|
||||
interfaceVal := man.viper.Get(key)
|
||||
if interfaceVal == nil {
|
||||
var ok bool
|
||||
interfaceVal, ok = man.defaults[key]
|
||||
if !ok {
|
||||
panic("Tried to look up default value for nonexistent config option: " + key)
|
||||
}
|
||||
}
|
||||
return interfaceVal
|
||||
}
|
||||
|
||||
// addConfigString adds a string config to the config options
|
||||
func (man Manager) addConfigString(key string, defVal string) {
|
||||
man.command.PersistentFlags().String(flagNameFromConfigKey(key), defVal, "Env: "+envNameFromConfigKey(key))
|
||||
man.viper.BindPFlag(key, man.command.PersistentFlags().Lookup(flagNameFromConfigKey(key)))
|
||||
man.viper.BindEnv(key, envNameFromConfigKey(key))
|
||||
|
||||
// Add default
|
||||
man.addDefault(key, defVal)
|
||||
}
|
||||
|
||||
// getConfigString retrieves a string from the loaded config
|
||||
func (man Manager) getConfigString(key string) string {
|
||||
interfaceVal := man.getInterfaceVal(key)
|
||||
stringVal, err := cast.ToStringE(interfaceVal)
|
||||
if err != nil {
|
||||
logrus.Infoln("Not reading config file. Relying on environment variables and default values.")
|
||||
panic("Unable to cast to string for key " + key + ": " + err.Error())
|
||||
}
|
||||
|
||||
return stringVal
|
||||
}
|
||||
|
||||
// addConfigInt adds a int config to the config options
|
||||
func (man Manager) addConfigInt(key string, defVal int) {
|
||||
man.command.PersistentFlags().Int(flagNameFromConfigKey(key), defVal, "Env: "+envNameFromConfigKey(key))
|
||||
man.viper.BindPFlag(key, man.command.PersistentFlags().Lookup(flagNameFromConfigKey(key)))
|
||||
man.viper.BindEnv(key, envNameFromConfigKey(key))
|
||||
|
||||
// Add default
|
||||
man.addDefault(key, defVal)
|
||||
}
|
||||
|
||||
// getConfigInt retrieves a int from the loaded config
|
||||
func (man Manager) getConfigInt(key string) int {
|
||||
interfaceVal := man.getInterfaceVal(key)
|
||||
intVal, err := cast.ToIntE(interfaceVal)
|
||||
if err != nil {
|
||||
panic("Unable to cast to int for key " + key + ": " + err.Error())
|
||||
}
|
||||
|
||||
return intVal
|
||||
}
|
||||
|
||||
// addConfigBool adds a bool config to the config options
|
||||
func (man Manager) addConfigBool(key string, defVal bool) {
|
||||
man.command.PersistentFlags().Bool(flagNameFromConfigKey(key), defVal, "Env: "+envNameFromConfigKey(key))
|
||||
man.viper.BindPFlag(key, man.command.PersistentFlags().Lookup(flagNameFromConfigKey(key)))
|
||||
man.viper.BindEnv(key, envNameFromConfigKey(key))
|
||||
|
||||
// Add default
|
||||
man.addDefault(key, defVal)
|
||||
}
|
||||
|
||||
// getConfigBool retrieves a bool from the loaded config
|
||||
func (man Manager) getConfigBool(key string) bool {
|
||||
interfaceVal := man.getInterfaceVal(key)
|
||||
boolVal, err := cast.ToBoolE(interfaceVal)
|
||||
if err != nil {
|
||||
panic("Unable to cast to bool for key " + key + ": " + err.Error())
|
||||
}
|
||||
|
||||
return boolVal
|
||||
}
|
||||
|
||||
// loadConfigFile handles the loading of the config file.
|
||||
func (man Manager) loadConfigFile() {
|
||||
configFile := man.command.PersistentFlags().Lookup("config").Value.String()
|
||||
if configFile != "" {
|
||||
man.viper.SetConfigFile(configFile)
|
||||
} else {
|
||||
man.viper.SetConfigName("kolide")
|
||||
man.viper.AddConfigPath(".")
|
||||
man.viper.AddConfigPath("$HOME")
|
||||
man.viper.AddConfigPath("./tools/app")
|
||||
man.viper.AddConfigPath("/etc/kolide")
|
||||
}
|
||||
|
||||
man.viper.SetConfigType("yaml")
|
||||
|
||||
err := man.viper.ReadInConfig()
|
||||
|
||||
fmt.Println("Using config file: ", man.viper.ConfigFileUsed())
|
||||
|
||||
if err != nil {
|
||||
panic("Error reading config: " + err.Error())
|
||||
}
|
||||
|
||||
setDefaultConfigValue("mysql.address", "localhost:3306")
|
||||
setDefaultConfigValue("mysql.username", "kolide")
|
||||
setDefaultConfigValue("mysql.password", "kolide")
|
||||
setDefaultConfigValue("mysql.database", "kolide")
|
||||
|
||||
setDefaultConfigValue("server.address", "0.0.0.0:8080")
|
||||
|
||||
setDefaultConfigValue("app.web_address", "0.0.0.0:8080")
|
||||
|
||||
setDefaultConfigValue("auth.jwt_key", "CHANGEME")
|
||||
setDefaultConfigValue("auth.bcrypt_cost", 12)
|
||||
setDefaultConfigValue("auth.salt_key_size", 24)
|
||||
|
||||
setDefaultConfigValue("smtp.token_key_size", 24)
|
||||
setDefaultConfigValue("smtp.address", "localhost:1025")
|
||||
setDefaultConfigValue("smtp.pool_connections", 4)
|
||||
|
||||
setDefaultConfigValue("session.key_size", 64)
|
||||
setDefaultConfigValue("session.expiration_seconds", 60*60*24*90)
|
||||
setDefaultConfigValue("session.cookie_name", "KolideSession")
|
||||
|
||||
setDefaultConfigValue("osquery.node_key_size", 24)
|
||||
setDefaultConfigValue("osquery.status_log_file", "/tmp/osquery_status")
|
||||
setDefaultConfigValue("osquery.result_log_file", "/tmp/osquery_result")
|
||||
setDefaultConfigValue("osquery.label_up_interval", 1*time.Minute)
|
||||
|
||||
setDefaultConfigValue("logging.debug", false)
|
||||
setDefaultConfigValue("logging.disable_banner", false)
|
||||
|
||||
if viper.GetBool("logging.debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
} else {
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
|
||||
if viper.GetBool("logs.json") {
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ import:
|
|||
version: fd703108daeb23d77c124d12978e9b6c4f28f034
|
||||
- package: github.com/spf13/cobra
|
||||
- package: github.com/spf13/viper
|
||||
- package: github.com/spf13/cast
|
||||
- package: gopkg.in/natefinch/lumberjack.v2
|
||||
version: v2.0
|
||||
- package: github.com/golang/mock
|
||||
|
|
|
|||
Loading…
Reference in a new issue