mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
c8e6405220
- Add appropriate configs for Redis - Use the Redis pubsub store by default, inmem in dev mode
408 lines
12 KiB
Go
408 lines
12 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/cast"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
const (
|
|
envPrefix = "KOLIDE"
|
|
)
|
|
|
|
// MysqlConfig defines configs related to MySQL
|
|
type MysqlConfig struct {
|
|
Address string
|
|
Username string
|
|
Password string
|
|
Database string
|
|
}
|
|
|
|
// RedisConfig defines configs related to Redis
|
|
type RedisConfig struct {
|
|
Address string
|
|
Password string
|
|
}
|
|
|
|
// ServerConfig defines configs related to the Kolide server
|
|
type ServerConfig struct {
|
|
Address string
|
|
Cert string
|
|
Key string
|
|
TLS bool
|
|
}
|
|
|
|
// 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 {
|
|
TokenKeySize int
|
|
TokenKey string
|
|
InviteTokenValidityPeriod time.Duration
|
|
}
|
|
|
|
// SMTPConfig defines configs related to SMTP email
|
|
type SMTPConfig struct {
|
|
Server string
|
|
Username string
|
|
Password string
|
|
PoolConnections int
|
|
}
|
|
|
|
// SessionConfig defines configs related to user sessions
|
|
type SessionConfig struct {
|
|
KeySize int
|
|
Duration time.Duration
|
|
}
|
|
|
|
// OsqueryConfig defines configs related to osquery
|
|
type OsqueryConfig struct {
|
|
EnrollSecret string
|
|
NodeKeySize int
|
|
StatusLogFile string
|
|
ResultLogFile string
|
|
LabelUpdateInterval time.Duration
|
|
}
|
|
|
|
// 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
|
|
Redis RedisConfig
|
|
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")
|
|
|
|
// Redis
|
|
man.addConfigString("redis.address", "localhost:6379")
|
|
man.addConfigString("redis.password", "")
|
|
|
|
// 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")
|
|
man.addConfigBool("server.tls", true)
|
|
|
|
// 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")
|
|
man.addConfigString("app.token_key", "CHANGEME")
|
|
man.addConfigDuration("app.invite_token_validity_period", 5*24*time.Hour)
|
|
man.addConfigInt("app.token_key_size", 24)
|
|
|
|
// SMTP
|
|
man.addConfigString("smtp.server", "0.0.0.0:1025")
|
|
man.addConfigString("smtp.username", "")
|
|
man.addConfigString("smtp.password", "")
|
|
man.addConfigInt("smtp.pool_connections", 4)
|
|
|
|
// Session
|
|
man.addConfigInt("session.key_size", 64)
|
|
man.addConfigDuration("session.duration", 24*90*time.Hour)
|
|
|
|
// 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")
|
|
man.addConfigDuration("osquery.label_update_interval", 1*time.Hour)
|
|
|
|
// 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"),
|
|
},
|
|
Redis: RedisConfig{
|
|
Address: man.getConfigString("redis.address"),
|
|
Password: man.getConfigString("redis.password"),
|
|
},
|
|
Server: ServerConfig{
|
|
Address: man.getConfigString("server.address"),
|
|
Cert: man.getConfigString("server.cert"),
|
|
Key: man.getConfigString("server.key"),
|
|
TLS: man.getConfigBool("server.tls"),
|
|
},
|
|
Auth: AuthConfig{
|
|
JwtKey: man.getConfigString("auth.jwt_key"),
|
|
BcryptCost: man.getConfigInt("auth.bcrypt_cost"),
|
|
SaltKeySize: man.getConfigInt("auth.salt_key_size"),
|
|
},
|
|
App: AppConfig{
|
|
TokenKeySize: man.getConfigInt("app.token_key_size"),
|
|
TokenKey: man.getConfigString("app.token_key"),
|
|
InviteTokenValidityPeriod: man.getConfigDuration("app.invite_token_validity_period"),
|
|
},
|
|
SMTP: SMTPConfig{
|
|
Server: man.getConfigString("smtp.server"),
|
|
Username: man.getConfigString("smtp.username"),
|
|
Password: man.getConfigString("smtp.password"),
|
|
PoolConnections: man.getConfigInt("smtp.pool_connections"),
|
|
},
|
|
Session: SessionConfig{
|
|
KeySize: man.getConfigInt("session.key_size"),
|
|
Duration: man.getConfigDuration("session.duration"),
|
|
},
|
|
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"),
|
|
LabelUpdateInterval: man.getConfigDuration("osquery.label_update_interval"),
|
|
},
|
|
Logging: LoggingConfig{
|
|
Debug: man.getConfigBool("logging.debug"),
|
|
DisableBanner: man.getConfigBool("logging.disable_banner"),
|
|
},
|
|
}
|
|
}
|
|
|
|
// IsSet determines whether a given config key has been explicitly set by any
|
|
// of the configuration sources. If false, the default value is being used.
|
|
func (man Manager) IsSet(key string) bool {
|
|
return man.viper.IsSet(key)
|
|
}
|
|
|
|
// 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{}{},
|
|
}
|
|
man.addConfigs()
|
|
return man
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
man.defaults[key] = defVal
|
|
}
|
|
|
|
// 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 {
|
|
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
|
|
}
|
|
|
|
// addConfigDuration adds a duration config to the config options
|
|
func (man Manager) addConfigDuration(key string, defVal time.Duration) {
|
|
man.command.PersistentFlags().Duration(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)
|
|
}
|
|
|
|
// getConfigDuration retrieves a duration from the loaded config
|
|
func (man Manager) getConfigDuration(key string) time.Duration {
|
|
interfaceVal := man.getInterfaceVal(key)
|
|
durationVal, err := cast.ToDurationE(interfaceVal)
|
|
if err != nil {
|
|
panic("Unable to cast to duration for key " + key + ": " + err.Error())
|
|
}
|
|
|
|
return durationVal
|
|
}
|
|
|
|
// loadConfigFile handles the loading of the config file.
|
|
func (man Manager) loadConfigFile() {
|
|
man.viper.SetConfigType("yaml")
|
|
|
|
configFile := man.command.PersistentFlags().Lookup("config").Value.String()
|
|
|
|
if configFile == "" {
|
|
// No config file set, only use configs from env
|
|
// vars/flags/defaults
|
|
return
|
|
}
|
|
|
|
man.viper.SetConfigFile(configFile)
|
|
err := man.viper.ReadInConfig()
|
|
|
|
fmt.Println("Using config file: ", man.viper.ConfigFileUsed())
|
|
|
|
if err != nil {
|
|
panic("Error reading config: " + err.Error())
|
|
}
|
|
}
|
|
|
|
// TestConfig returns a barebones configuration suitable for use in tests.
|
|
// Individual tests may want to override some of the values provided.
|
|
func TestConfig() KolideConfig {
|
|
return KolideConfig{
|
|
App: AppConfig{
|
|
TokenKey: "CHANGEME",
|
|
InviteTokenValidityPeriod: 5 * 24 * time.Hour,
|
|
},
|
|
Auth: AuthConfig{
|
|
JwtKey: "CHANGEME",
|
|
BcryptCost: 6, // Low cost keeps tests fast
|
|
SaltKeySize: 24,
|
|
},
|
|
Session: SessionConfig{
|
|
KeySize: 64,
|
|
Duration: 24 * 90 * time.Hour,
|
|
},
|
|
Osquery: OsqueryConfig{
|
|
EnrollSecret: "",
|
|
NodeKeySize: 24,
|
|
StatusLogFile: "",
|
|
ResultLogFile: "",
|
|
LabelUpdateInterval: 1 * time.Hour,
|
|
},
|
|
Logging: LoggingConfig{
|
|
Debug: true,
|
|
DisableBanner: true,
|
|
},
|
|
SMTP: SMTPConfig{},
|
|
}
|
|
}
|