fleet/server/config/config.go
Marcos Oviedo 22bb16bf2e
Pushing initial support for MS-MDE2 Discovery message (#12387)
This PR requires the Windows MDM configuration changes - This will be
updated next week

- [x] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [x] Documented any API changes (docs/Using-Fleet/REST-API.md or
docs/Contributing/API-for-contributors.md)
- [x] Documented any permissions changes
- [X] Added/updated tests
- [X] Manual QA for all new/changed functionality
  - For Orbit and Fleet Desktop changes:
2023-06-22 17:31:17 -03:00

1671 lines
73 KiB
Go

package config
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"time"
nanodep_client "github.com/micromdm/nanodep/client"
"github.com/micromdm/nanodep/tokenpki"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
envPrefix = "FLEET"
)
// MysqlConfig defines configs related to MySQL
type MysqlConfig struct {
Protocol string `yaml:"protocol"`
Address string `yaml:"address"`
Username string `yaml:"username"`
Password string `yaml:"password"`
PasswordPath string `yaml:"password_path"`
Database string `yaml:"database"`
TLSCert string `yaml:"tls_cert"`
TLSKey string `yaml:"tls_key"`
TLSCA string `yaml:"tls_ca"`
TLSServerName string `yaml:"tls_server_name"`
TLSConfig string `yaml:"tls_config"` // tls=customValue in DSN
MaxOpenConns int `yaml:"max_open_conns"`
MaxIdleConns int `yaml:"max_idle_conns"`
ConnMaxLifetime int `yaml:"conn_max_lifetime"`
SQLMode string `yaml:"sql_mode"`
}
// RedisConfig defines configs related to Redis
type RedisConfig struct {
Address string
Username string
Password string
Database int
UseTLS bool `yaml:"use_tls"`
DuplicateResults bool `yaml:"duplicate_results"`
ConnectTimeout time.Duration `yaml:"connect_timeout"`
KeepAlive time.Duration `yaml:"keep_alive"`
ConnectRetryAttempts int `yaml:"connect_retry_attempts"`
ClusterFollowRedirections bool `yaml:"cluster_follow_redirections"`
ClusterReadFromReplica bool `yaml:"cluster_read_from_replica"`
TLSCert string `yaml:"tls_cert"`
TLSKey string `yaml:"tls_key"`
TLSCA string `yaml:"tls_ca"`
TLSServerName string `yaml:"tls_server_name"`
TLSHandshakeTimeout time.Duration `yaml:"tls_handshake_timeout"`
MaxIdleConns int `yaml:"max_idle_conns"`
MaxOpenConns int `yaml:"max_open_conns"`
// this config is an int on MysqlConfig, but it should be a time.Duration.
ConnMaxLifetime time.Duration `yaml:"conn_max_lifetime"`
IdleTimeout time.Duration `yaml:"idle_timeout"`
ConnWaitTimeout time.Duration `yaml:"conn_wait_timeout"`
WriteTimeout time.Duration `yaml:"write_timeout"`
ReadTimeout time.Duration `yaml:"read_timeout"`
}
const (
TLSProfileKey = "server.tls_compatibility"
TLSProfileModern = "modern"
TLSProfileIntermediate = "intermediate"
)
// ServerConfig defines configs related to the Fleet server
type ServerConfig struct {
Address string
Cert string
Key string
TLS bool
TLSProfile string `yaml:"tls_compatibility"`
URLPrefix string `yaml:"url_prefix"`
Keepalive bool `yaml:"keepalive"`
SandboxEnabled bool `yaml:"sandbox_enabled"`
WebsocketsAllowUnsafeOrigin bool `yaml:"websockets_allow_unsafe_origin"`
}
func (s *ServerConfig) DefaultHTTPServer(ctx context.Context, handler http.Handler) *http.Server {
return &http.Server{
Addr: s.Address,
Handler: handler,
ReadTimeout: 25 * time.Second,
WriteTimeout: 40 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
IdleTimeout: 5 * time.Minute,
MaxHeaderBytes: 1 << 18, // 0.25 MB (262144 bytes)
BaseContext: func(l net.Listener) context.Context {
return ctx
},
}
}
// AuthConfig defines configs related to user authorization
type AuthConfig struct {
BcryptCost int `yaml:"bcrypt_cost"`
SaltKeySize int `yaml:"salt_key_size"`
}
// AppConfig defines configs related to HTTP
type AppConfig struct {
TokenKeySize int `yaml:"token_key_size"`
InviteTokenValidityPeriod time.Duration `yaml:"invite_token_validity_period"`
EnableScheduledQueryStats bool `yaml:"enable_scheduled_query_stats"`
}
// SessionConfig defines configs related to user sessions
type SessionConfig struct {
KeySize int `yaml:"key_size"`
Duration time.Duration
}
// OsqueryConfig defines configs related to osquery
type OsqueryConfig struct {
NodeKeySize int `yaml:"node_key_size"`
HostIdentifier string `yaml:"host_identifier"`
EnrollCooldown time.Duration `yaml:"enroll_cooldown"`
StatusLogPlugin string `yaml:"status_log_plugin"`
ResultLogPlugin string `yaml:"result_log_plugin"`
LabelUpdateInterval time.Duration `yaml:"label_update_interval"`
PolicyUpdateInterval time.Duration `yaml:"policy_update_interval"`
DetailUpdateInterval time.Duration `yaml:"detail_update_interval"`
// StatusLogFile is deprecated. It was replaced by FilesystemConfig.StatusLogFile.
//
// TODO(lucas): We should at least add a warning if this field is populated.
StatusLogFile string `yaml:"status_log_file"`
// ResultLogFile is deprecated. It was replaced by FilesystemConfig.ResultLogFile.
//
// TODO(lucas): We should at least add a warning if this field is populated.
ResultLogFile string `yaml:"result_log_file"`
EnableLogRotation bool `yaml:"enable_log_rotation"`
MaxJitterPercent int `yaml:"max_jitter_percent"`
EnableAsyncHostProcessing string `yaml:"enable_async_host_processing"` // true/false or per-task
AsyncHostCollectInterval string `yaml:"async_host_collect_interval"` // duration or per-task
AsyncHostCollectMaxJitterPercent int `yaml:"async_host_collect_max_jitter_percent"`
AsyncHostCollectLockTimeout string `yaml:"async_host_collect_lock_timeout"` // duration or per-task
AsyncHostCollectLogStatsInterval time.Duration `yaml:"async_host_collect_log_stats_interval"`
AsyncHostInsertBatch int `yaml:"async_host_insert_batch"`
AsyncHostDeleteBatch int `yaml:"async_host_delete_batch"`
AsyncHostUpdateBatch int `yaml:"async_host_update_batch"`
AsyncHostRedisPopCount int `yaml:"async_host_redis_pop_count"`
AsyncHostRedisScanKeysCount int `yaml:"async_host_redis_scan_keys_count"`
MinSoftwareLastOpenedAtDiff time.Duration `yaml:"min_software_last_opened_at_diff"`
}
// AsyncTaskName is the type of names that identify tasks supporting
// asynchronous execution.
type AsyncTaskName string
// List of names for supported async tasks.
const (
AsyncTaskLabelMembership AsyncTaskName = "label_membership"
AsyncTaskPolicyMembership AsyncTaskName = "policy_membership"
AsyncTaskHostLastSeen AsyncTaskName = "host_last_seen"
AsyncTaskScheduledQueryStats AsyncTaskName = "scheduled_query_stats"
)
var knownAsyncTasks = map[AsyncTaskName]struct{}{
AsyncTaskLabelMembership: {},
AsyncTaskPolicyMembership: {},
AsyncTaskHostLastSeen: {},
AsyncTaskScheduledQueryStats: {},
}
// AsyncConfigForTask returns the applicable configuration for the specified
// async task.
func (o OsqueryConfig) AsyncConfigForTask(name AsyncTaskName) AsyncProcessingConfig {
strName := string(name)
return AsyncProcessingConfig{
Enabled: configForKeyOrBool("osquery.enable_async_host_processing", strName, o.EnableAsyncHostProcessing, false),
CollectInterval: configForKeyOrDuration("osquery.async_host_collect_interval", strName, o.AsyncHostCollectInterval, 30*time.Second),
CollectMaxJitterPercent: o.AsyncHostCollectMaxJitterPercent,
CollectLockTimeout: configForKeyOrDuration("osquery.async_host_collect_lock_timeout", strName, o.AsyncHostCollectLockTimeout, 1*time.Minute),
CollectLogStatsInterval: o.AsyncHostCollectLogStatsInterval,
InsertBatch: o.AsyncHostInsertBatch,
DeleteBatch: o.AsyncHostDeleteBatch,
UpdateBatch: o.AsyncHostUpdateBatch,
RedisPopCount: o.AsyncHostRedisPopCount,
RedisScanKeysCount: o.AsyncHostRedisScanKeysCount,
}
}
// AsyncProcessingConfig is the configuration for a specific async task.
type AsyncProcessingConfig struct {
Enabled bool
CollectInterval time.Duration
CollectMaxJitterPercent int
CollectLockTimeout time.Duration
CollectLogStatsInterval time.Duration
InsertBatch int
DeleteBatch int
UpdateBatch int
RedisPopCount int
RedisScanKeysCount int
}
// LoggingConfig defines configs related to logging
type LoggingConfig struct {
Debug bool
JSON bool
DisableBanner bool `yaml:"disable_banner"`
ErrorRetentionPeriod time.Duration `yaml:"error_retention_period"`
TracingEnabled bool `yaml:"tracing_enabled"`
// TracingType can either be opentelemetry or elasticapm for whichever type of tracing wanted
TracingType string `yaml:"tracing_type"`
}
// ActivityConfig defines configs related to activities.
type ActivityConfig struct {
// EnableAuditLog enables logging for audit activities.
EnableAuditLog bool `yaml:"enable_audit_log"`
// AuditLogPlugin sets the plugin to use to log activities.
AuditLogPlugin string `yaml:"audit_log_plugin"`
}
// FirehoseConfig defines configs for the AWS Firehose logging plugin
type FirehoseConfig struct {
Region string
EndpointURL string `yaml:"endpoint_url"`
AccessKeyID string `yaml:"access_key_id"`
SecretAccessKey string `yaml:"secret_access_key"`
StsAssumeRoleArn string `yaml:"sts_assume_role_arn"`
StatusStream string `yaml:"status_stream"`
ResultStream string `yaml:"result_stream"`
AuditStream string `yaml:"audit_stream"`
}
// KinesisConfig defines configs for the AWS Kinesis logging plugin
type KinesisConfig struct {
Region string
EndpointURL string `yaml:"endpoint_url"`
AccessKeyID string `yaml:"access_key_id"`
SecretAccessKey string `yaml:"secret_access_key"`
StsAssumeRoleArn string `yaml:"sts_assume_role_arn"`
StatusStream string `yaml:"status_stream"`
ResultStream string `yaml:"result_stream"`
AuditStream string `yaml:"audit_stream"`
}
// SESConfig defines configs for the AWS SES service for emailing
type SESConfig struct {
Region string
EndpointURL string `yaml:"endpoint_url"`
AccessKeyID string `yaml:"access_key_id"`
SecretAccessKey string `yaml:"secret_access_key"`
StsAssumeRoleArn string `yaml:"sts_assume_role_arn"`
SourceArn string `yaml:"source_arn"`
}
type EmailConfig struct {
EmailBackend string `yaml:"backend"`
}
// LambdaConfig defines configs for the AWS Lambda logging plugin
type LambdaConfig struct {
Region string
AccessKeyID string `yaml:"access_key_id"`
SecretAccessKey string `yaml:"secret_access_key"`
StsAssumeRoleArn string `yaml:"sts_assume_role_arn"`
StatusFunction string `yaml:"status_function"`
ResultFunction string `yaml:"result_function"`
AuditFunction string `yaml:"audit_function"`
}
// S3Config defines config to enable file carving storage to an S3 bucket
type S3Config struct {
Bucket string `yaml:"bucket"`
Prefix string `yaml:"prefix"`
Region string `yaml:"region"`
EndpointURL string `yaml:"endpoint_url"`
AccessKeyID string `yaml:"access_key_id"`
SecretAccessKey string `yaml:"secret_access_key"`
StsAssumeRoleArn string `yaml:"sts_assume_role_arn"`
DisableSSL bool `yaml:"disable_ssl"`
ForceS3PathStyle bool `yaml:"force_s3_path_style"`
}
// PubSubConfig defines configs the for Google PubSub logging plugin
type PubSubConfig struct {
Project string `json:"project"`
StatusTopic string `json:"status_topic" yaml:"status_topic"`
ResultTopic string `json:"result_topic" yaml:"result_topic"`
AuditTopic string `json:"audit_topic" yaml:"audit_topic"`
AddAttributes bool `json:"add_attributes" yaml:"add_attributes"`
}
// FilesystemConfig defines configs for the Filesystem logging plugin
type FilesystemConfig struct {
StatusLogFile string `json:"status_log_file" yaml:"status_log_file"`
ResultLogFile string `json:"result_log_file" yaml:"result_log_file"`
AuditLogFile string `json:"audit_log_file" yaml:"audit_log_file"`
EnableLogRotation bool `json:"enable_log_rotation" yaml:"enable_log_rotation"`
EnableLogCompression bool `json:"enable_log_compression" yaml:"enable_log_compression"`
MaxSize int `json:"max_size" yaml:"max_size"`
MaxAge int `json:"max_age" yaml:"max_age"`
MaxBackups int `json:"max_backups" yaml:"max_backups"`
}
// KafkaRESTConfig defines configs for the Kafka REST Proxy logging plugin.
type KafkaRESTConfig struct {
StatusTopic string `json:"status_topic" yaml:"status_topic"`
ResultTopic string `json:"result_topic" yaml:"result_topic"`
AuditTopic string `json:"audit_topic" yaml:"audit_topic"`
ProxyHost string `json:"proxyhost" yaml:"proxyhost"`
ContentTypeValue string `json:"content_type_value" yaml:"content_type_value"`
Timeout int `json:"timeout" yaml:"timeout"`
}
// LicenseConfig defines configs related to licensing Fleet.
type LicenseConfig struct {
Key string `yaml:"key"`
EnforceHostLimit bool `yaml:"enforce_host_limit"`
}
// VulnerabilitiesConfig defines configs related to vulnerability processing within Fleet.
type VulnerabilitiesConfig struct {
DatabasesPath string `json:"databases_path" yaml:"databases_path"`
Periodicity time.Duration `json:"periodicity" yaml:"periodicity"`
CPEDatabaseURL string `json:"cpe_database_url" yaml:"cpe_database_url"`
CPETranslationsURL string `json:"cpe_translations_url" yaml:"cpe_translations_url"`
CVEFeedPrefixURL string `json:"cve_feed_prefix_url" yaml:"cve_feed_prefix_url"`
CurrentInstanceChecks string `json:"current_instance_checks" yaml:"current_instance_checks"`
DisableSchedule bool `json:"disable_schedule" yaml:"disable_schedule"`
DisableDataSync bool `json:"disable_data_sync" yaml:"disable_data_sync"`
RecentVulnerabilityMaxAge time.Duration `json:"recent_vulnerability_max_age" yaml:"recent_vulnerability_max_age"`
DisableWinOSVulnerabilities bool `json:"disable_win_os_vulnerabilities" yaml:"disable_win_os_vulnerabilities"`
}
// UpgradesConfig defines configs related to fleet server upgrades.
type UpgradesConfig struct {
AllowMissingMigrations bool `json:"allow_missing_migrations" yaml:"allow_missing_migrations"`
}
type SentryConfig struct {
Dsn string `json:"dsn"`
}
type GeoIPConfig struct {
DatabasePath string `json:"database_path" yaml:"database_path"`
}
// PrometheusConfig holds the configuration for Fleet's prometheus metrics.
type PrometheusConfig struct {
// BasicAuth is the HTTP Basic BasicAuth configuration.
BasicAuth HTTPBasicAuthConfig `json:"basic_auth" yaml:"basic_auth"`
}
// HTTPBasicAuthConfig holds configuration for HTTP Basic Auth.
type HTTPBasicAuthConfig struct {
// Username is the HTTP Basic Auth username.
Username string `json:"username" yaml:"username"`
// Password is the HTTP Basic Auth password.
Password string `json:"password" yaml:"password"`
// Disable allows running the Prometheus metrics endpoint without Basic Auth.
Disable bool `json:"disable" yaml:"disable"`
}
// PackagingConfig holds configuration to build and retrieve Fleet packages
type PackagingConfig struct {
// GlobalEnrollSecret is the enroll secret that will be used to enroll
// hosts in the global scope
GlobalEnrollSecret string `yaml:"global_enroll_secret"`
// S3 configuration used to retrieve pre-built installers
S3 S3Config `yaml:"s3"`
}
// FleetConfig 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 FleetConfig struct {
Mysql MysqlConfig
MysqlReadReplica MysqlConfig `yaml:"mysql_read_replica"`
Redis RedisConfig
Server ServerConfig
Auth AuthConfig
App AppConfig
Session SessionConfig
Osquery OsqueryConfig
Activity ActivityConfig
Logging LoggingConfig
Firehose FirehoseConfig
Kinesis KinesisConfig
Lambda LambdaConfig
S3 S3Config
Email EmailConfig
SES SESConfig
PubSub PubSubConfig
Filesystem FilesystemConfig
KafkaREST KafkaRESTConfig
License LicenseConfig
Vulnerabilities VulnerabilitiesConfig
Upgrades UpgradesConfig
Sentry SentryConfig
GeoIP GeoIPConfig
Prometheus PrometheusConfig
Packaging PackagingConfig
MDM MDMConfig
}
type MDMConfig struct {
AppleAPNsCert string `yaml:"apple_apns_cert"`
AppleAPNsCertBytes string `yaml:"apple_apns_cert_bytes"`
AppleAPNsKey string `yaml:"apple_apns_key"`
AppleAPNsKeyBytes string `yaml:"apple_apns_key_bytes"`
AppleSCEPCert string `yaml:"apple_scep_cert"`
AppleSCEPCertBytes string `yaml:"apple_scep_cert_bytes"`
AppleSCEPKey string `yaml:"apple_scep_key"`
AppleSCEPKeyBytes string `yaml:"apple_scep_key_bytes"`
// the following fields hold the parsed, validated TLS certificate set the
// first time AppleAPNs or AppleSCEP is called, as well as the PEM-encoded
// bytes for the certificate and private key.
appleAPNs *tls.Certificate
appleAPNsPEMCert []byte
appleAPNsPEMKey []byte
appleSCEP *tls.Certificate
appleSCEPPEMCert []byte
appleSCEPPEMKey []byte
AppleBMServerToken string `yaml:"apple_bm_server_token"`
AppleBMServerTokenBytes string `yaml:"apple_bm_server_token_bytes"`
AppleBMCert string `yaml:"apple_bm_cert"`
AppleBMCertBytes string `yaml:"apple_bm_cert_bytes"`
AppleBMKey string `yaml:"apple_bm_key"`
AppleBMKeyBytes string `yaml:"apple_bm_key_bytes"`
// the following fields hold the decrypted, validated Apple BM token set the
// first time AppleBM is called.
appleBMToken *nanodep_client.OAuth1Tokens
// AppleEnable enables Apple MDM functionality on Fleet.
AppleEnable bool `yaml:"apple_enable"`
// AppleDEPSyncPeriodicity is the duration between DEP device syncing
// (fetching and setting of DEP profiles).
AppleDEPSyncPeriodicity time.Duration `yaml:"apple_dep_sync_periodicity"`
// AppleSCEPChallenge is the SCEP challenge for SCEP enrollment requests.
AppleSCEPChallenge string `yaml:"apple_scep_challenge"`
// AppleSCEPSignerValidityDays are the days signed client certificates will
// be valid.
AppleSCEPSignerValidityDays int `yaml:"apple_scep_signer_validity_days"`
// AppleSCEPSignerAllowRenewalDays are the allowable renewal days for
// certificates.
AppleSCEPSignerAllowRenewalDays int `yaml:"apple_scep_signer_allow_renewal_days"`
}
type x509KeyPairConfig struct {
certPath string
certBytes []byte
keyPath string
keyBytes []byte
}
func (x *x509KeyPairConfig) IsSet() bool {
// if any setting is provided, then the key pair is considered set
return x.certPath != "" || len(x.certBytes) != 0 || x.keyPath != "" || len(x.keyBytes) != 0
}
func (x *x509KeyPairConfig) Parse(keepLeaf bool) (*tls.Certificate, error) {
if x.certPath == "" && len(x.certBytes) == 0 {
return nil, errors.New("no certificate provided")
}
if x.certPath != "" && len(x.certBytes) != 0 {
return nil, errors.New("only one of the certificate path or bytes must be provided")
}
if x.keyPath == "" && len(x.keyBytes) == 0 {
return nil, errors.New("no key provided")
}
if x.keyPath != "" && len(x.keyBytes) != 0 {
return nil, errors.New("only one of the key path or bytes must be provided")
}
if len(x.certBytes) == 0 {
b, err := os.ReadFile(x.certPath)
if err != nil {
return nil, fmt.Errorf("reading certificate file: %w", err)
}
x.certBytes = b
}
if len(x.keyBytes) == 0 {
b, err := os.ReadFile(x.keyPath)
if err != nil {
return nil, fmt.Errorf("reading key file: %w", err)
}
x.keyBytes = b
}
cert, err := tls.X509KeyPair(x.certBytes, x.keyBytes)
if err != nil {
return nil, fmt.Errorf("parse key pair: %w", err)
}
if keepLeaf {
// X509KeyPair does not store the parsed certificate leaf
parsed, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("parse leaf certificate: %w", err)
}
cert.Leaf = parsed
}
return &cert, nil
}
func (m *MDMConfig) IsAppleAPNsSet() bool {
pair := x509KeyPairConfig{
m.AppleAPNsCert,
[]byte(m.AppleAPNsCertBytes),
m.AppleAPNsKey,
[]byte(m.AppleAPNsKeyBytes),
}
return pair.IsSet()
}
func (m *MDMConfig) IsAppleSCEPSet() bool {
pair := x509KeyPairConfig{
m.AppleSCEPCert,
[]byte(m.AppleSCEPCertBytes),
m.AppleSCEPKey,
[]byte(m.AppleSCEPKeyBytes),
}
return pair.IsSet()
}
func (m *MDMConfig) IsAppleBMSet() bool {
pair := x509KeyPairConfig{
m.AppleBMCert,
[]byte(m.AppleBMCertBytes),
m.AppleBMKey,
[]byte(m.AppleBMKeyBytes),
}
// the BM token options is not taken into account by pair.IsSet
return pair.IsSet() || m.AppleBMServerToken != "" || m.AppleBMServerTokenBytes != ""
}
// AppleAPNs returns the parsed and validated TLS certificate for Apple APNs.
// It parses and validates it if it hasn't been done yet.
func (m *MDMConfig) AppleAPNs() (cert *tls.Certificate, pemCert, pemKey []byte, err error) {
if m.appleAPNs == nil {
pair := x509KeyPairConfig{
m.AppleAPNsCert,
[]byte(m.AppleAPNsCertBytes),
m.AppleAPNsKey,
[]byte(m.AppleAPNsKeyBytes),
}
cert, err := pair.Parse(true)
if err != nil {
return nil, nil, nil, fmt.Errorf("Apple MDM APNs configuration: %w", err)
}
m.appleAPNs = cert
m.appleAPNsPEMCert = pair.certBytes
m.appleAPNsPEMKey = pair.keyBytes
}
return m.appleAPNs, m.appleAPNsPEMCert, m.appleAPNsPEMKey, nil
}
// AppleSCEP returns the parsed and validated TLS certificate for Apple SCEP.
// It parses and validates it if it hasn't been done yet.
func (m *MDMConfig) AppleSCEP() (cert *tls.Certificate, pemCert, pemKey []byte, err error) {
if m.appleSCEP == nil {
pair := x509KeyPairConfig{
m.AppleSCEPCert,
[]byte(m.AppleSCEPCertBytes),
m.AppleSCEPKey,
[]byte(m.AppleSCEPKeyBytes),
}
cert, err := pair.Parse(true)
if err != nil {
return nil, nil, nil, fmt.Errorf("Apple MDM SCEP configuration: %w", err)
}
m.appleSCEP = cert
m.appleSCEPPEMCert = pair.certBytes
m.appleSCEPPEMKey = pair.keyBytes
}
return m.appleSCEP, m.appleSCEPPEMCert, m.appleSCEPPEMKey, nil
}
// AppleBM returns the parsed, validated and decrypted server token for Apple
// Business Manager. It also parses and validates the Apple BM certificate and
// private key in the process, in order to decrypt the token.
func (m *MDMConfig) AppleBM() (tok *nanodep_client.OAuth1Tokens, err error) {
if m.appleBMToken == nil {
pair := x509KeyPairConfig{
m.AppleBMCert,
[]byte(m.AppleBMCertBytes),
m.AppleBMKey,
[]byte(m.AppleBMKeyBytes),
}
cert, err := pair.Parse(true)
if err != nil {
return nil, fmt.Errorf("Apple BM configuration: %w", err)
}
encToken, err := m.loadAppleBMEncryptedToken()
if err != nil {
return nil, fmt.Errorf("Apple BM configuration: %w", err)
}
bmKey, err := tokenpki.RSAKeyFromPEM(pair.keyBytes)
if err != nil {
return nil, fmt.Errorf("Apple BM configuration: parse private key: %w", err)
}
token, err := tokenpki.DecryptTokenJSON(encToken, cert.Leaf, bmKey)
if err != nil {
return nil, fmt.Errorf("Apple BM configuration: decrypt token: %w", err)
}
var jsonTok nanodep_client.OAuth1Tokens
if err := json.Unmarshal(token, &jsonTok); err != nil {
return nil, fmt.Errorf("Apple BM configuration: unmarshal JSON token: %w", err)
}
if jsonTok.AccessTokenExpiry.Before(time.Now()) {
return nil, errors.New("Apple BM configuration: token is expired")
}
m.appleBMToken = &jsonTok
}
return m.appleBMToken, nil
}
func (m *MDMConfig) loadAppleBMEncryptedToken() ([]byte, error) {
if m.AppleBMServerToken == "" && m.AppleBMServerTokenBytes == "" {
return nil, errors.New("no token provided")
}
if m.AppleBMServerToken != "" && m.AppleBMServerTokenBytes != "" {
return nil, errors.New("only one of the token path or bytes must be provided")
}
tokBytes := []byte(m.AppleBMServerTokenBytes)
if m.AppleBMServerTokenBytes == "" {
b, err := os.ReadFile(m.AppleBMServerToken)
if err != nil {
return nil, fmt.Errorf("reading token file: %w", err)
}
tokBytes = b
}
return tokBytes, nil
}
type TLS struct {
TLSCert string
TLSKey string
TLSCA string
TLSServerName string
}
func (t *TLS) ToTLSConfig() (*tls.Config, error) {
var rootCertPool *x509.CertPool
if t.TLSCA != "" {
rootCertPool = x509.NewCertPool()
pem, err := ioutil.ReadFile(t.TLSCA)
if err != nil {
return nil, fmt.Errorf("read server-ca pem: %w", err)
}
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
return nil, errors.New("failed to append PEM.")
}
}
cfg := &tls.Config{
RootCAs: rootCertPool,
}
if t.TLSCert != "" {
clientCert := make([]tls.Certificate, 0, 1)
certs, err := tls.LoadX509KeyPair(t.TLSCert, t.TLSKey)
if err != nil {
return nil, fmt.Errorf("load client cert and key: %w", err)
}
clientCert = append(clientCert, certs)
cfg.Certificates = clientCert
}
if t.TLSServerName != "" {
cfg.ServerName = t.TLSServerName
}
return cfg, nil
}
// addConfigs adds the configuration keys and default values that will be
// filled into the FleetConfig struct
func (man Manager) addConfigs() {
addMysqlConfig := func(prefix, defaultAddr, usageSuffix string) {
man.addConfigString(prefix+".protocol", "tcp",
"MySQL server communication protocol (tcp,unix,...)"+usageSuffix)
man.addConfigString(prefix+".address", defaultAddr,
"MySQL server address (host:port)"+usageSuffix)
man.addConfigString(prefix+".username", "fleet",
"MySQL server username"+usageSuffix)
man.addConfigString(prefix+".password", "",
"MySQL server password (prefer env variable for security)"+usageSuffix)
man.addConfigString(prefix+".password_path", "",
"Path to file containg MySQL server password"+usageSuffix)
man.addConfigString(prefix+".database", "fleet",
"MySQL database name"+usageSuffix)
man.addConfigString(prefix+".tls_cert", "",
"MySQL TLS client certificate path"+usageSuffix)
man.addConfigString(prefix+".tls_key", "",
"MySQL TLS client key path"+usageSuffix)
man.addConfigString(prefix+".tls_ca", "",
"MySQL TLS server CA"+usageSuffix)
man.addConfigString(prefix+".tls_server_name", "",
"MySQL TLS server name"+usageSuffix)
man.addConfigString(prefix+".tls_config", "",
"MySQL TLS config value"+usageSuffix+" Use skip-verify, true, false or custom key.")
man.addConfigInt(prefix+".max_open_conns", 50, "MySQL maximum open connection handles"+usageSuffix)
man.addConfigInt(prefix+".max_idle_conns", 50, "MySQL maximum idle connection handles"+usageSuffix)
man.addConfigInt(prefix+".conn_max_lifetime", 0, "MySQL maximum amount of time a connection may be reused"+usageSuffix)
man.addConfigString(prefix+".sql_mode", "", "MySQL sql_mode"+usageSuffix)
}
// MySQL
addMysqlConfig("mysql", "localhost:3306", ".")
addMysqlConfig("mysql_read_replica", "", " for the read replica.")
// Redis
man.addConfigString("redis.address", "localhost:6379",
"Redis server address (host:port)")
man.addConfigString("redis.username", "",
"Redis server username")
man.addConfigString("redis.password", "",
"Redis server password (prefer env variable for security)")
man.addConfigInt("redis.database", 0,
"Redis server database number")
man.addConfigBool("redis.use_tls", false, "Redis server enable TLS")
man.addConfigBool("redis.duplicate_results", false, "Duplicate Live Query results to another Redis channel")
man.addConfigDuration("redis.connect_timeout", 5*time.Second, "Timeout at connection time")
man.addConfigDuration("redis.keep_alive", 10*time.Second, "Interval between keep alive probes")
man.addConfigInt("redis.connect_retry_attempts", 0, "Number of attempts to retry a failed connection")
man.addConfigBool("redis.cluster_follow_redirections", false, "Automatically follow Redis Cluster redirections")
man.addConfigBool("redis.cluster_read_from_replica", false, "Prefer reading from a replica when possible (for Redis Cluster)")
man.addConfigString("redis.tls_cert", "", "Redis TLS client certificate path")
man.addConfigString("redis.tls_key", "", "Redis TLS client key path")
man.addConfigString("redis.tls_ca", "", "Redis TLS server CA")
man.addConfigString("redis.tls_server_name", "", "Redis TLS server name")
man.addConfigDuration("redis.tls_handshake_timeout", 10*time.Second, "Redis TLS handshake timeout")
man.addConfigInt("redis.max_idle_conns", 3, "Redis maximum idle connections")
man.addConfigInt("redis.max_open_conns", 0, "Redis maximum open connections, 0 means no limit")
man.addConfigDuration("redis.conn_max_lifetime", 0, "Redis maximum amount of time a connection may be reused, 0 means no limit")
man.addConfigDuration("redis.idle_timeout", 240*time.Second, "Redis maximum amount of time a connection may stay idle, 0 means no limit")
man.addConfigDuration("redis.conn_wait_timeout", 0, "Redis maximum amount of time to wait for a connection if the maximum is reached (0 for no wait, ignored in non-cluster Redis)")
man.addConfigDuration("redis.write_timeout", 10*time.Second, "Redis maximum amount of time to wait for a write (send) on a connection")
man.addConfigDuration("redis.read_timeout", 10*time.Second, "Redis maximum amount of time to wait for a read (receive) on a connection")
// Server
man.addConfigString("server.address", "0.0.0.0:8080",
"Fleet server address (host:port)")
man.addConfigString("server.cert", "./tools/osquery/fleet.crt",
"Fleet TLS certificate path")
man.addConfigString("server.key", "./tools/osquery/fleet.key",
"Fleet TLS key path")
man.addConfigBool("server.tls", true,
"Enable TLS (required for osqueryd communication)")
man.addConfigString(TLSProfileKey, TLSProfileIntermediate,
fmt.Sprintf("TLS security profile choose one of %s or %s",
TLSProfileModern, TLSProfileIntermediate))
man.addConfigString("server.url_prefix", "",
"URL prefix used on server and frontend endpoints")
man.addConfigBool("server.keepalive", true,
"Controls whether HTTP keep-alives are enabled.")
man.addConfigBool("server.sandbox_enabled", false,
"When enabled, Fleet limits some features for the Sandbox")
man.addConfigBool("server.websockets_allow_unsafe_origin", false, "Disable checking the origin header on websocket connections, this is sometimes necessary when proxies rewrite origin headers between the client and the Fleet webserver")
// Hide the sandbox flag as we don't want it to be discoverable for users for now
sandboxFlag := man.command.PersistentFlags().Lookup(flagNameFromConfigKey("server.sandbox_enabled"))
if sandboxFlag != nil {
sandboxFlag.Hidden = true
}
// Auth
man.addConfigInt("auth.bcrypt_cost", 12,
"Bcrypt iterations")
man.addConfigInt("auth.salt_key_size", 24,
"Size of salt for passwords")
// App
man.addConfigString("app.token_key", "CHANGEME",
"Secret key for generating invite and reset tokens")
man.addConfigDuration("app.invite_token_validity_period", 5*24*time.Hour,
"Duration invite tokens remain valid (i.e. 1h)")
man.addConfigInt("app.token_key_size", 24,
"Size of generated tokens")
man.addConfigBool("app.enable_scheduled_query_stats", true,
"If true (default) it gets scheduled query stats from hosts")
// Session
man.addConfigInt("session.key_size", 64,
"Size of generated session keys")
man.addConfigDuration("session.duration", 24*5*time.Hour,
"Duration session keys remain valid (i.e. 4h)")
// Osquery
man.addConfigInt("osquery.node_key_size", 24,
"Size of generated osqueryd node keys")
man.addConfigString("osquery.host_identifier", "provided",
"Identifier used to uniquely determine osquery clients")
man.addConfigDuration("osquery.enroll_cooldown", 0,
"Cooldown period for duplicate host enrollment (default off)")
man.addConfigString("osquery.status_log_plugin", "filesystem",
"Log plugin to use for status logs")
man.addConfigString("osquery.result_log_plugin", "filesystem",
"Log plugin to use for result logs")
man.addConfigDuration("osquery.label_update_interval", 1*time.Hour,
"Interval to update host label membership (i.e. 1h)")
man.addConfigDuration("osquery.policy_update_interval", 1*time.Hour,
"Interval to update host policy membership (i.e. 1h)")
man.addConfigDuration("osquery.detail_update_interval", 1*time.Hour,
"Interval to update host details (i.e. 1h)")
man.addConfigString("osquery.status_log_file", "",
"(DEPRECATED: Use filesystem.status_log_file) Path for osqueryd status logs")
man.addConfigString("osquery.result_log_file", "",
"(DEPRECATED: Use filesystem.result_log_file) Path for osqueryd result logs")
man.addConfigBool("osquery.enable_log_rotation", false,
"(DEPRECATED: Use filesystem.enable_log_rotation) Enable automatic rotation for osquery log files")
man.addConfigInt("osquery.max_jitter_percent", 10,
"Maximum percentage of the interval to add as jitter")
man.addConfigString("osquery.enable_async_host_processing", "false",
"Enable asynchronous processing of host-reported query results (either 'true'/'false' or set per task, e.g., 'label_membership=true&policy_membership=true')")
man.addConfigString("osquery.async_host_collect_interval", (30 * time.Second).String(),
"Interval to collect asynchronous host-reported query results (e.g. '30s' or set per task 'label_membership=10s&policy_membership=1m')")
man.addConfigInt("osquery.async_host_collect_max_jitter_percent", 10,
"Maximum percentage of the interval to collect asynchronous host results")
man.addConfigString("osquery.async_host_collect_lock_timeout", (1 * time.Minute).String(),
"Timeout of the exclusive lock held during async host collection (e.g., '30s' or set per task 'label_membership=10s&policy_membership=1m'")
man.addConfigDuration("osquery.async_host_collect_log_stats_interval", 1*time.Minute,
"Interval at which async host collection statistics are logged (0 disables logging of stats)")
man.addConfigInt("osquery.async_host_insert_batch", 2000,
"Batch size for async collection inserts in mysql")
man.addConfigInt("osquery.async_host_delete_batch", 2000,
"Batch size for async collection deletes in mysql")
man.addConfigInt("osquery.async_host_update_batch", 1000,
"Batch size for async collection updates in mysql")
man.addConfigInt("osquery.async_host_redis_pop_count", 1000,
"Batch size to pop items from redis in async collection")
man.addConfigInt("osquery.async_host_redis_scan_keys_count", 1000,
"Batch size to scan redis keys in async collection")
man.addConfigDuration("osquery.min_software_last_opened_at_diff", 1*time.Hour,
"Minimum time difference of the software's last opened timestamp (compared to the last one saved) to trigger an update to the database")
// Activities
man.addConfigBool("activity.enable_audit_log", false,
"Enable audit logs")
man.addConfigString("activity.audit_log_plugin", "filesystem",
"Log plugin to use for audit logs")
// Logging
man.addConfigBool("logging.debug", false,
"Enable debug logging")
man.addConfigBool("logging.json", false,
"Log in JSON format")
man.addConfigBool("logging.disable_banner", false,
"Disable startup banner")
man.addConfigDuration("logging.error_retention_period", 24*time.Hour,
"Amount of time to keep errors, 0 means no expiration, < 0 means disable storage of errors")
man.addConfigBool("logging.tracing_enabled", false,
"Enable Tracing, further configured via standard env variables")
man.addConfigString("logging.tracing_type", "opentelemetry",
"Select the kind of tracing, defaults to opentelemetry, can also be elasticapm")
// Email
man.addConfigString("email.backend", "", "Provide the email backend type, acceptable values are currently \"ses\" and \"default\" or empty string which will default to SMTP")
// SES
man.addConfigString("ses.region", "", "AWS Region to use")
man.addConfigString("ses.endpoint_url", "", "AWS Service Endpoint to use (leave empty for default service endpoints)")
man.addConfigString("ses.access_key_id", "", "Access Key ID for AWS authentication")
man.addConfigString("ses.secret_access_key", "", "Secret Access Key for AWS authentication")
man.addConfigString("ses.sts_assume_role_arn", "", "ARN of role to assume for AWS")
man.addConfigString("ses.source_arn", "", "ARN of the identity that is associated with the sending authorization policy that permits you to send for the email address specified in the Source parameter")
// Firehose
man.addConfigString("firehose.region", "", "AWS Region to use")
man.addConfigString("firehose.endpoint_url", "",
"AWS Service Endpoint to use (leave empty for default service endpoints)")
man.addConfigString("firehose.access_key_id", "", "Access Key ID for AWS authentication")
man.addConfigString("firehose.secret_access_key", "", "Secret Access Key for AWS authentication")
man.addConfigString("firehose.sts_assume_role_arn", "",
"ARN of role to assume for AWS")
man.addConfigString("firehose.status_stream", "",
"Firehose stream name for status logs")
man.addConfigString("firehose.result_stream", "",
"Firehose stream name for result logs")
man.addConfigString("firehose.audit_stream", "",
"Firehose stream name for audit logs")
// Kinesis
man.addConfigString("kinesis.region", "", "AWS Region to use")
man.addConfigString("kinesis.endpoint_url", "",
"AWS Service Endpoint to use (leave empty for default service endpoints)")
man.addConfigString("kinesis.access_key_id", "", "Access Key ID for AWS authentication")
man.addConfigString("kinesis.secret_access_key", "", "Secret Access Key for AWS authentication")
man.addConfigString("kinesis.sts_assume_role_arn", "",
"ARN of role to assume for AWS")
man.addConfigString("kinesis.status_stream", "",
"Kinesis stream name for status logs")
man.addConfigString("kinesis.result_stream", "",
"Kinesis stream name for result logs")
man.addConfigString("kinesis.audit_stream", "",
"Kinesis stream name for audit logs")
// Lambda
man.addConfigString("lambda.region", "", "AWS Region to use")
man.addConfigString("lambda.access_key_id", "", "Access Key ID for AWS authentication")
man.addConfigString("lambda.secret_access_key", "", "Secret Access Key for AWS authentication")
man.addConfigString("lambda.sts_assume_role_arn", "",
"ARN of role to assume for AWS")
man.addConfigString("lambda.status_function", "",
"Lambda function name for status logs")
man.addConfigString("lambda.result_function", "",
"Lambda function name for result logs")
man.addConfigString("lambda.audit_function", "",
"Lambda function name for audit logs")
// S3 for file carving
man.addConfigString("s3.bucket", "", "Bucket where to store file carves")
man.addConfigString("s3.prefix", "", "Prefix under which carves are stored")
man.addConfigString("s3.region", "", "AWS Region (if blank region is derived)")
man.addConfigString("s3.endpoint_url", "", "AWS Service Endpoint to use (leave blank for default service endpoints)")
man.addConfigString("s3.access_key_id", "", "Access Key ID for AWS authentication")
man.addConfigString("s3.secret_access_key", "", "Secret Access Key for AWS authentication")
man.addConfigString("s3.sts_assume_role_arn", "", "ARN of role to assume for AWS")
man.addConfigBool("s3.disable_ssl", false, "Disable SSL (typically for local testing)")
man.addConfigBool("s3.force_s3_path_style", false, "Set this to true to force path-style addressing, i.e., `http://s3.amazonaws.com/BUCKET/KEY`")
// PubSub
man.addConfigString("pubsub.project", "", "Google Cloud Project to use")
man.addConfigString("pubsub.status_topic", "", "PubSub topic for status logs")
man.addConfigString("pubsub.result_topic", "", "PubSub topic for result logs")
man.addConfigString("pubsub.audit_topic", "", "PubSub topic for audit logs")
man.addConfigBool("pubsub.add_attributes", false, "Add PubSub attributes in addition to the message body")
// Filesystem
man.addConfigString("filesystem.status_log_file", filepath.Join(os.TempDir(), "osquery_status"),
"Log file path to use for status logs")
man.addConfigString("filesystem.result_log_file", filepath.Join(os.TempDir(), "osquery_result"),
"Log file path to use for result logs")
man.addConfigString("filesystem.audit_log_file", filepath.Join(os.TempDir(), "audit"),
"Log file path to use for audit logs")
man.addConfigBool("filesystem.enable_log_rotation", false,
"Enable automatic rotation for osquery log files")
man.addConfigBool("filesystem.enable_log_compression", false,
"Enable compression for the rotated osquery log files")
man.addConfigInt("filesystem.max_size", 500, "Maximum size in megabytes log files will grow until rotated (only valid if enable_log_rotation is true) default is 500MB")
man.addConfigInt("filesystem.max_age", 28, "Maximum number of days to retain old log files based on the timestamp encoded in their filename. Setting to zero wil retain old log files indefinitely (only valid if enable_log_rotation is true) default is 28 days")
man.addConfigInt("filesystem.max_backups", 3, "Maximum number of old log files to retain. Setting to zero will retain all old log files (only valid if enable_log_rotation is true) default is 3")
// KafkaREST
man.addConfigString("kafkarest.status_topic", "", "Kafka REST topic for status logs")
man.addConfigString("kafkarest.result_topic", "", "Kafka REST topic for result logs")
man.addConfigString("kafkarest.audit_topic", "", "Kafka REST topic for audit logs")
man.addConfigString("kafkarest.proxyhost", "", "Kafka REST proxy host url")
man.addConfigString("kafkarest.content_type_value", "application/vnd.kafka.json.v1+json",
"Kafka REST proxy content type header (defaults to \"application/vnd.kafka.json.v1+json\"")
man.addConfigInt("kafkarest.timeout", 5, "Kafka REST proxy json post timeout")
// License
man.addConfigString("license.key", "", "Fleet license key (to enable Fleet Premium features)")
man.addConfigBool("license.enforce_host_limit", false, "Enforce license limit of enrolled hosts")
// Vulnerability processing
man.addConfigString("vulnerabilities.databases_path", "/tmp/vulndbs",
"Path where Fleet will download the data feeds to check CVEs")
man.addConfigDuration("vulnerabilities.periodicity", 1*time.Hour,
"How much time to wait between processing software for vulnerabilities.")
man.addConfigString("vulnerabilities.cpe_database_url", "",
"URL from which to get the latest CPE database. If empty, it will be downloaded from the latest release available at https://github.com/fleetdm/nvd/releases.")
man.addConfigString("vulnerabilities.cpe_translations_url", "",
"URL from which to get the latest CPE translations. If empty, it will be downloaded from the latest release available at https://github.com/fleetdm/nvd/releases.")
man.addConfigString("vulnerabilities.cve_feed_prefix_url", "",
"Prefix URL for the CVE data feed. If empty, default to https://nvd.nist.gov/")
man.addConfigString("vulnerabilities.current_instance_checks", "auto",
"Allows to manually select an instance to do the vulnerability processing.")
man.addConfigBool("vulnerabilities.disable_schedule", false,
"Set this to true when the vulnerability processing job is scheduled by an external mechanism")
man.addConfigBool("vulnerabilities.disable_data_sync", false,
"Skips synchronizing data streams and expects them to be available in the databases_path.")
man.addConfigDuration("vulnerabilities.recent_vulnerability_max_age", 30*24*time.Hour,
"Maximum age of the published date of a vulnerability (CVE) to be considered 'recent'.")
man.addConfigBool(
"vulnerabilities.disable_win_os_vulnerabilities",
false,
"Don't sync installed Windows updates nor perform Windows OS vulnerability processing.",
)
// Upgrades
man.addConfigBool("upgrades.allow_missing_migrations", false,
"Allow serve to run even if migrations are missing.")
// Sentry
man.addConfigString("sentry.dsn", "", "DSN for Sentry")
// GeoIP
man.addConfigString("geoip.database_path", "", "path to mmdb file")
// Prometheus
man.addConfigString("prometheus.basic_auth.username", "", "Prometheus username for HTTP Basic Auth")
man.addConfigString("prometheus.basic_auth.password", "", "Prometheus password for HTTP Basic Auth")
man.addConfigBool("prometheus.basic_auth.disable", false, "Disable HTTP Basic Auth for Prometheus")
// Packaging config
man.addConfigString("packaging.global_enroll_secret", "", "Enroll secret to be used for the global domain (instead of randomly generating one)")
man.addConfigString("packaging.s3.bucket", "", "Bucket where to retrieve installers")
man.addConfigString("packaging.s3.prefix", "", "Prefix under which installers are stored")
man.addConfigString("packaging.s3.region", "", "AWS Region (if blank region is derived)")
man.addConfigString("packaging.s3.endpoint_url", "", "AWS Service Endpoint to use (leave blank for default service endpoints)")
man.addConfigString("packaging.s3.access_key_id", "", "Access Key ID for AWS authentication")
man.addConfigString("packaging.s3.secret_access_key", "", "Secret Access Key for AWS authentication")
man.addConfigString("packaging.s3.sts_assume_role_arn", "", "ARN of role to assume for AWS")
man.addConfigBool("packaging.s3.disable_ssl", false, "Disable SSL (typically for local testing)")
man.addConfigBool("packaging.s3.force_s3_path_style", false, "Set this to true to force path-style addressing, i.e., `http://s3.amazonaws.com/BUCKET/KEY`")
// MDM config
man.addConfigString("mdm.apple_apns_cert", "", "Apple APNs PEM-encoded certificate path")
man.addConfigString("mdm.apple_apns_cert_bytes", "", "Apple APNs PEM-encoded certificate bytes")
man.addConfigString("mdm.apple_apns_key", "", "Apple APNs PEM-encoded private key path")
man.addConfigString("mdm.apple_apns_key_bytes", "", "Apple APNs PEM-encoded private key bytes")
man.addConfigString("mdm.apple_scep_cert", "", "Apple SCEP PEM-encoded certificate path")
man.addConfigString("mdm.apple_scep_cert_bytes", "", "Apple SCEP PEM-encoded certificate bytes")
man.addConfigString("mdm.apple_scep_key", "", "Apple SCEP PEM-encoded private key path")
man.addConfigString("mdm.apple_scep_key_bytes", "", "Apple SCEP PEM-encoded private key bytes")
man.addConfigString("mdm.apple_bm_server_token", "", "Apple Business Manager encrypted server token path (.p7m file)")
man.addConfigString("mdm.apple_bm_server_token_bytes", "", "Apple Business Manager encrypted server token bytes")
man.addConfigString("mdm.apple_bm_cert", "", "Apple Business Manager PEM-encoded certificate path")
man.addConfigString("mdm.apple_bm_cert_bytes", "", "Apple Business Manager PEM-encoded certificate bytes")
man.addConfigString("mdm.apple_bm_key", "", "Apple Business Manager PEM-encoded private key path")
man.addConfigString("mdm.apple_bm_key_bytes", "", "Apple Business Manager PEM-encoded private key bytes")
man.addConfigBool("mdm.apple_enable", false, "Enable MDM Apple functionality")
man.addConfigInt("mdm.apple_scep_signer_validity_days", 365, "Days signed client certificates will be valid")
man.addConfigInt("mdm.apple_scep_signer_allow_renewal_days", 14, "Allowable renewal days for client certificates")
man.addConfigString("mdm.apple_scep_challenge", "", "SCEP static challenge for enrollment")
man.addConfigDuration("mdm.apple_dep_sync_periodicity", 1*time.Minute, "How much time to wait for DEP profile assignment")
}
// LoadConfig will load the config variables into a fully initialized
// FleetConfig struct
func (man Manager) LoadConfig() FleetConfig {
man.loadConfigFile()
loadMysqlConfig := func(prefix string) MysqlConfig {
return MysqlConfig{
Protocol: man.getConfigString(prefix + ".protocol"),
Address: man.getConfigString(prefix + ".address"),
Username: man.getConfigString(prefix + ".username"),
Password: man.getConfigString(prefix + ".password"),
PasswordPath: man.getConfigString(prefix + ".password_path"),
Database: man.getConfigString(prefix + ".database"),
TLSCert: man.getConfigString(prefix + ".tls_cert"),
TLSKey: man.getConfigString(prefix + ".tls_key"),
TLSCA: man.getConfigString(prefix + ".tls_ca"),
TLSServerName: man.getConfigString(prefix + ".tls_server_name"),
TLSConfig: man.getConfigString(prefix + ".tls_config"),
MaxOpenConns: man.getConfigInt(prefix + ".max_open_conns"),
MaxIdleConns: man.getConfigInt(prefix + ".max_idle_conns"),
ConnMaxLifetime: man.getConfigInt(prefix + ".conn_max_lifetime"),
SQLMode: man.getConfigString(prefix + ".sql_mode"),
}
}
cfg := FleetConfig{
Mysql: loadMysqlConfig("mysql"),
MysqlReadReplica: loadMysqlConfig("mysql_read_replica"),
Redis: RedisConfig{
Address: man.getConfigString("redis.address"),
Username: man.getConfigString("redis.username"),
Password: man.getConfigString("redis.password"),
Database: man.getConfigInt("redis.database"),
UseTLS: man.getConfigBool("redis.use_tls"),
DuplicateResults: man.getConfigBool("redis.duplicate_results"),
ConnectTimeout: man.getConfigDuration("redis.connect_timeout"),
KeepAlive: man.getConfigDuration("redis.keep_alive"),
ConnectRetryAttempts: man.getConfigInt("redis.connect_retry_attempts"),
ClusterFollowRedirections: man.getConfigBool("redis.cluster_follow_redirections"),
ClusterReadFromReplica: man.getConfigBool("redis.cluster_read_from_replica"),
TLSCert: man.getConfigString("redis.tls_cert"),
TLSKey: man.getConfigString("redis.tls_key"),
TLSCA: man.getConfigString("redis.tls_ca"),
TLSServerName: man.getConfigString("redis.tls_server_name"),
TLSHandshakeTimeout: man.getConfigDuration("redis.tls_handshake_timeout"),
MaxIdleConns: man.getConfigInt("redis.max_idle_conns"),
MaxOpenConns: man.getConfigInt("redis.max_open_conns"),
ConnMaxLifetime: man.getConfigDuration("redis.conn_max_lifetime"),
IdleTimeout: man.getConfigDuration("redis.idle_timeout"),
ConnWaitTimeout: man.getConfigDuration("redis.conn_wait_timeout"),
WriteTimeout: man.getConfigDuration("redis.write_timeout"),
ReadTimeout: man.getConfigDuration("redis.read_timeout"),
},
Server: ServerConfig{
Address: man.getConfigString("server.address"),
Cert: man.getConfigString("server.cert"),
Key: man.getConfigString("server.key"),
TLS: man.getConfigBool("server.tls"),
TLSProfile: man.getConfigTLSProfile(),
URLPrefix: man.getConfigString("server.url_prefix"),
Keepalive: man.getConfigBool("server.keepalive"),
SandboxEnabled: man.getConfigBool("server.sandbox_enabled"),
WebsocketsAllowUnsafeOrigin: man.getConfigBool("server.websockets_allow_unsafe_origin"),
},
Auth: AuthConfig{
BcryptCost: man.getConfigInt("auth.bcrypt_cost"),
SaltKeySize: man.getConfigInt("auth.salt_key_size"),
},
App: AppConfig{
TokenKeySize: man.getConfigInt("app.token_key_size"),
InviteTokenValidityPeriod: man.getConfigDuration("app.invite_token_validity_period"),
EnableScheduledQueryStats: man.getConfigBool("app.enable_scheduled_query_stats"),
},
Session: SessionConfig{
KeySize: man.getConfigInt("session.key_size"),
Duration: man.getConfigDuration("session.duration"),
},
Osquery: OsqueryConfig{
NodeKeySize: man.getConfigInt("osquery.node_key_size"),
HostIdentifier: man.getConfigString("osquery.host_identifier"),
EnrollCooldown: man.getConfigDuration("osquery.enroll_cooldown"),
StatusLogPlugin: man.getConfigString("osquery.status_log_plugin"),
ResultLogPlugin: man.getConfigString("osquery.result_log_plugin"),
// StatusLogFile is deprecated. FilesystemConfig.StatusLogFile is used instead.
StatusLogFile: man.getConfigString("osquery.status_log_file"),
// ResultLogFile is deprecated. FilesystemConfig.ResultLogFile is used instead.
ResultLogFile: man.getConfigString("osquery.result_log_file"),
LabelUpdateInterval: man.getConfigDuration("osquery.label_update_interval"),
PolicyUpdateInterval: man.getConfigDuration("osquery.policy_update_interval"),
DetailUpdateInterval: man.getConfigDuration("osquery.detail_update_interval"),
EnableLogRotation: man.getConfigBool("osquery.enable_log_rotation"),
MaxJitterPercent: man.getConfigInt("osquery.max_jitter_percent"),
EnableAsyncHostProcessing: man.getConfigString("osquery.enable_async_host_processing"),
AsyncHostCollectInterval: man.getConfigString("osquery.async_host_collect_interval"),
AsyncHostCollectMaxJitterPercent: man.getConfigInt("osquery.async_host_collect_max_jitter_percent"),
AsyncHostCollectLockTimeout: man.getConfigString("osquery.async_host_collect_lock_timeout"),
AsyncHostCollectLogStatsInterval: man.getConfigDuration("osquery.async_host_collect_log_stats_interval"),
AsyncHostInsertBatch: man.getConfigInt("osquery.async_host_insert_batch"),
AsyncHostDeleteBatch: man.getConfigInt("osquery.async_host_delete_batch"),
AsyncHostUpdateBatch: man.getConfigInt("osquery.async_host_update_batch"),
AsyncHostRedisPopCount: man.getConfigInt("osquery.async_host_redis_pop_count"),
AsyncHostRedisScanKeysCount: man.getConfigInt("osquery.async_host_redis_scan_keys_count"),
MinSoftwareLastOpenedAtDiff: man.getConfigDuration("osquery.min_software_last_opened_at_diff"),
},
Activity: ActivityConfig{
EnableAuditLog: man.getConfigBool("activity.enable_audit_log"),
AuditLogPlugin: man.getConfigString("activity.audit_log_plugin"),
},
Logging: LoggingConfig{
Debug: man.getConfigBool("logging.debug"),
JSON: man.getConfigBool("logging.json"),
DisableBanner: man.getConfigBool("logging.disable_banner"),
ErrorRetentionPeriod: man.getConfigDuration("logging.error_retention_period"),
TracingEnabled: man.getConfigBool("logging.tracing_enabled"),
TracingType: man.getConfigString("logging.tracing_type"),
},
Firehose: FirehoseConfig{
Region: man.getConfigString("firehose.region"),
EndpointURL: man.getConfigString("firehose.endpoint_url"),
AccessKeyID: man.getConfigString("firehose.access_key_id"),
SecretAccessKey: man.getConfigString("firehose.secret_access_key"),
StsAssumeRoleArn: man.getConfigString("firehose.sts_assume_role_arn"),
StatusStream: man.getConfigString("firehose.status_stream"),
ResultStream: man.getConfigString("firehose.result_stream"),
AuditStream: man.getConfigString("firehose.audit_stream"),
},
Kinesis: KinesisConfig{
Region: man.getConfigString("kinesis.region"),
EndpointURL: man.getConfigString("kinesis.endpoint_url"),
AccessKeyID: man.getConfigString("kinesis.access_key_id"),
SecretAccessKey: man.getConfigString("kinesis.secret_access_key"),
StatusStream: man.getConfigString("kinesis.status_stream"),
ResultStream: man.getConfigString("kinesis.result_stream"),
AuditStream: man.getConfigString("kinesis.audit_stream"),
StsAssumeRoleArn: man.getConfigString("kinesis.sts_assume_role_arn"),
},
Lambda: LambdaConfig{
Region: man.getConfigString("lambda.region"),
AccessKeyID: man.getConfigString("lambda.access_key_id"),
SecretAccessKey: man.getConfigString("lambda.secret_access_key"),
StatusFunction: man.getConfigString("lambda.status_function"),
ResultFunction: man.getConfigString("lambda.result_function"),
AuditFunction: man.getConfigString("lambda.audit_function"),
StsAssumeRoleArn: man.getConfigString("lambda.sts_assume_role_arn"),
},
S3: S3Config{
Bucket: man.getConfigString("s3.bucket"),
Prefix: man.getConfigString("s3.prefix"),
Region: man.getConfigString("s3.region"),
EndpointURL: man.getConfigString("s3.endpoint_url"),
AccessKeyID: man.getConfigString("s3.access_key_id"),
SecretAccessKey: man.getConfigString("s3.secret_access_key"),
StsAssumeRoleArn: man.getConfigString("s3.sts_assume_role_arn"),
DisableSSL: man.getConfigBool("s3.disable_ssl"),
ForceS3PathStyle: man.getConfigBool("s3.force_s3_path_style"),
},
Email: EmailConfig{
EmailBackend: man.getConfigString("email.backend"),
},
SES: SESConfig{
Region: man.getConfigString("ses.region"),
EndpointURL: man.getConfigString("ses.endpoint_url"),
AccessKeyID: man.getConfigString("ses.access_key_id"),
SecretAccessKey: man.getConfigString("ses.secret_access_key"),
StsAssumeRoleArn: man.getConfigString("ses.sts_assume_role_arn"),
SourceArn: man.getConfigString("ses.source_arn"),
},
PubSub: PubSubConfig{
Project: man.getConfigString("pubsub.project"),
StatusTopic: man.getConfigString("pubsub.status_topic"),
ResultTopic: man.getConfigString("pubsub.result_topic"),
AuditTopic: man.getConfigString("pubsub.audit_topic"),
AddAttributes: man.getConfigBool("pubsub.add_attributes"),
},
Filesystem: FilesystemConfig{
StatusLogFile: man.getConfigString("filesystem.status_log_file"),
ResultLogFile: man.getConfigString("filesystem.result_log_file"),
AuditLogFile: man.getConfigString("filesystem.audit_log_file"),
EnableLogRotation: man.getConfigBool("filesystem.enable_log_rotation"),
EnableLogCompression: man.getConfigBool("filesystem.enable_log_compression"),
MaxSize: man.getConfigInt("filesystem.max_size"),
MaxAge: man.getConfigInt("filesystem.max_age"),
MaxBackups: man.getConfigInt("filesystem.max_backups"),
},
KafkaREST: KafkaRESTConfig{
StatusTopic: man.getConfigString("kafkarest.status_topic"),
ResultTopic: man.getConfigString("kafkarest.result_topic"),
AuditTopic: man.getConfigString("kafkarest.audit_topic"),
ProxyHost: man.getConfigString("kafkarest.proxyhost"),
ContentTypeValue: man.getConfigString("kafkarest.content_type_value"),
Timeout: man.getConfigInt("kafkarest.timeout"),
},
License: LicenseConfig{
Key: man.getConfigString("license.key"),
EnforceHostLimit: man.getConfigBool("license.enforce_host_limit"),
},
Vulnerabilities: VulnerabilitiesConfig{
DatabasesPath: man.getConfigString("vulnerabilities.databases_path"),
Periodicity: man.getConfigDuration("vulnerabilities.periodicity"),
CPEDatabaseURL: man.getConfigString("vulnerabilities.cpe_database_url"),
CPETranslationsURL: man.getConfigString("vulnerabilities.cpe_translations_url"),
CVEFeedPrefixURL: man.getConfigString("vulnerabilities.cve_feed_prefix_url"),
CurrentInstanceChecks: man.getConfigString("vulnerabilities.current_instance_checks"),
DisableSchedule: man.getConfigBool("vulnerabilities.disable_schedule"),
DisableDataSync: man.getConfigBool("vulnerabilities.disable_data_sync"),
RecentVulnerabilityMaxAge: man.getConfigDuration("vulnerabilities.recent_vulnerability_max_age"),
DisableWinOSVulnerabilities: man.getConfigBool("vulnerabilities.disable_win_os_vulnerabilities"),
},
Upgrades: UpgradesConfig{
AllowMissingMigrations: man.getConfigBool("upgrades.allow_missing_migrations"),
},
Sentry: SentryConfig{
Dsn: man.getConfigString("sentry.dsn"),
},
GeoIP: GeoIPConfig{
DatabasePath: man.getConfigString("geoip.database_path"),
},
Prometheus: PrometheusConfig{
BasicAuth: HTTPBasicAuthConfig{
Username: man.getConfigString("prometheus.basic_auth.username"),
Password: man.getConfigString("prometheus.basic_auth.password"),
Disable: man.getConfigBool("prometheus.basic_auth.disable"),
},
},
Packaging: PackagingConfig{
GlobalEnrollSecret: man.getConfigString("packaging.global_enroll_secret"),
S3: S3Config{
Bucket: man.getConfigString("packaging.s3.bucket"),
Prefix: man.getConfigString("packaging.s3.prefix"),
Region: man.getConfigString("packaging.s3.region"),
EndpointURL: man.getConfigString("packaging.s3.endpoint_url"),
AccessKeyID: man.getConfigString("packaging.s3.access_key_id"),
SecretAccessKey: man.getConfigString("packaging.s3.secret_access_key"),
StsAssumeRoleArn: man.getConfigString("packaging.s3.sts_assume_role_arn"),
DisableSSL: man.getConfigBool("packaging.s3.disable_ssl"),
ForceS3PathStyle: man.getConfigBool("packaging.s3.force_s3_path_style"),
},
},
MDM: MDMConfig{
AppleAPNsCert: man.getConfigString("mdm.apple_apns_cert"),
AppleAPNsCertBytes: man.getConfigString("mdm.apple_apns_cert_bytes"),
AppleAPNsKey: man.getConfigString("mdm.apple_apns_key"),
AppleAPNsKeyBytes: man.getConfigString("mdm.apple_apns_key_bytes"),
AppleSCEPCert: man.getConfigString("mdm.apple_scep_cert"),
AppleSCEPCertBytes: man.getConfigString("mdm.apple_scep_cert_bytes"),
AppleSCEPKey: man.getConfigString("mdm.apple_scep_key"),
AppleSCEPKeyBytes: man.getConfigString("mdm.apple_scep_key_bytes"),
AppleBMServerToken: man.getConfigString("mdm.apple_bm_server_token"),
AppleBMServerTokenBytes: man.getConfigString("mdm.apple_bm_server_token_bytes"),
AppleBMCert: man.getConfigString("mdm.apple_bm_cert"),
AppleBMCertBytes: man.getConfigString("mdm.apple_bm_cert_bytes"),
AppleBMKey: man.getConfigString("mdm.apple_bm_key"),
AppleBMKeyBytes: man.getConfigString("mdm.apple_bm_key_bytes"),
AppleEnable: man.getConfigBool("mdm.apple_enable"),
AppleSCEPSignerValidityDays: man.getConfigInt("mdm.apple_scep_signer_validity_days"),
AppleSCEPSignerAllowRenewalDays: man.getConfigInt("mdm.apple_scep_signer_allow_renewal_days"),
AppleSCEPChallenge: man.getConfigString("mdm.apple_scep_challenge"),
AppleDEPSyncPeriodicity: man.getConfigDuration("mdm.apple_dep_sync_periodicity"),
},
}
// ensure immediately that the async config is valid for all known tasks
for task := range knownAsyncTasks {
cfg.Osquery.AsyncConfigForTask(task)
}
return cfg
}
// 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 Fleet
// configs. It's only public API method is LoadConfig, which will return the
// populated FleetConfig 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
}
func getFlagUsage(key string, usage string) string {
return fmt.Sprintf("Env: %s\n\t\t%s", envNameFromConfigKey(key), usage)
}
// 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, defVal, usage string) {
man.command.PersistentFlags().String(flagNameFromConfigKey(key), defVal, getFlagUsage(key, usage))
man.viper.BindPFlag(key, man.command.PersistentFlags().Lookup(flagNameFromConfigKey(key))) //nolint:errcheck
man.viper.BindEnv(key, envNameFromConfigKey(key)) //nolint:errcheck
// 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
}
// Custom handling for TLSProfile which can only accept specific values
// for the argument
func (man Manager) getConfigTLSProfile() string {
ival := man.getInterfaceVal(TLSProfileKey)
sval, err := cast.ToStringE(ival)
if err != nil {
panic(fmt.Sprintf("%s requires a string value: %s", TLSProfileKey, err.Error()))
}
switch sval {
case TLSProfileModern, TLSProfileIntermediate:
default:
panic(fmt.Sprintf("%s must be one of %s or %s", TLSProfileKey,
TLSProfileModern, TLSProfileIntermediate))
}
return sval
}
// addConfigInt adds a int config to the config options
func (man Manager) addConfigInt(key string, defVal int, usage string) {
man.command.PersistentFlags().Int(flagNameFromConfigKey(key), defVal, getFlagUsage(key, usage))
man.viper.BindPFlag(key, man.command.PersistentFlags().Lookup(flagNameFromConfigKey(key))) //nolint:errcheck
man.viper.BindEnv(key, envNameFromConfigKey(key)) //nolint:errcheck
// 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, usage string) {
man.command.PersistentFlags().Bool(flagNameFromConfigKey(key), defVal, getFlagUsage(key, usage))
man.viper.BindPFlag(key, man.command.PersistentFlags().Lookup(flagNameFromConfigKey(key))) //nolint:errcheck
man.viper.BindEnv(key, envNameFromConfigKey(key)) //nolint:errcheck
// 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, usage string) {
man.command.PersistentFlags().Duration(flagNameFromConfigKey(key), defVal, getFlagUsage(key, usage))
man.viper.BindPFlag(key, man.command.PersistentFlags().Lookup(flagNameFromConfigKey(key))) //nolint:errcheck
man.viper.BindEnv(key, envNameFromConfigKey(key)) //nolint:errcheck
// 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
}
// panics if the config is invalid, this is handled by Viper (this is how all
// getConfigT helpers indicate errors). The default value is only applied if
// there is no task-specific config (i.e., no "task=true" config format for that
// task). If the configuration key was not set at all, it automatically
// inherited the general default configured for that key (via
// man.addConfigBool).
func configForKeyOrBool(key, task, val string, def bool) bool {
parseVal := func(v string) bool {
if v == "" {
return false
}
b, err := strconv.ParseBool(v)
if err != nil {
panic("Unable to cast to bool for key " + key + ": " + err.Error())
}
return b
}
if !strings.Contains(val, "=") {
// simple case, val is a bool
return parseVal(val)
}
q, err := url.ParseQuery(val)
if err != nil {
panic("Invalid query format for key " + key + ": " + err.Error())
}
if v := q.Get(task); v != "" {
return parseVal(v)
}
return def
}
// panics if the config is invalid, this is handled by Viper (this is how all
// getConfigT helpers indicate errors). The default value is only applied if
// there is no task-specific config (i.e. no "task=10s" config format for that
// task). If the configuration key was not set at all, it automatically
// inherited the general default configured for that key (via
// man.addConfigDuration).
func configForKeyOrDuration(key, task, val string, def time.Duration) time.Duration {
parseVal := func(v string) time.Duration {
if v == "" {
return 0
}
d, err := time.ParseDuration(v)
if err != nil {
panic("Unable to cast to time.Duration for key " + key + ": " + err.Error())
}
return d
}
if !strings.Contains(val, "=") {
// simple case, val is a duration
return parseVal(val)
}
q, err := url.ParseQuery(val)
if err != nil {
panic("Invalid query format for key " + key + ": " + err.Error())
}
if v := q.Get(task); v != "" {
return parseVal(v)
}
return def
}
// 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()
if err != nil {
fmt.Println("Error loading config file:", err)
os.Exit(1)
}
fmt.Println("Using config file:", man.viper.ConfigFileUsed())
}
// TestConfig returns a barebones configuration suitable for use in tests.
// Individual tests may want to override some of the values provided.
func TestConfig() FleetConfig {
testLogFile := "/dev/null"
if runtime.GOOS == "windows" {
testLogFile = "NUL"
}
return FleetConfig{
App: AppConfig{
TokenKeySize: 24,
InviteTokenValidityPeriod: 5 * 24 * time.Hour,
},
Auth: AuthConfig{
BcryptCost: 6, // Low cost keeps tests fast
SaltKeySize: 24,
},
Session: SessionConfig{
KeySize: 64,
Duration: 24 * 5 * time.Hour,
},
Osquery: OsqueryConfig{
NodeKeySize: 24,
HostIdentifier: "instance",
EnrollCooldown: 42 * time.Minute,
StatusLogPlugin: "filesystem",
ResultLogPlugin: "filesystem",
LabelUpdateInterval: 1 * time.Hour,
PolicyUpdateInterval: 1 * time.Hour,
DetailUpdateInterval: 1 * time.Hour,
MaxJitterPercent: 0,
},
Activity: ActivityConfig{
EnableAuditLog: true,
AuditLogPlugin: "filesystem",
},
Logging: LoggingConfig{
Debug: true,
DisableBanner: true,
},
Filesystem: FilesystemConfig{
StatusLogFile: testLogFile,
ResultLogFile: testLogFile,
AuditLogFile: testLogFile,
MaxSize: 500,
},
}
}
// SetTestMDMConfig modifies the provided cfg so that MDM is enabled and
// configured properly. The provided certificate and private key are used for
// all required pairs and the Apple BM token is used as-is, instead of
// decrypting the encrypted value that is usually provided via the fleet
// server's flags.
func SetTestMDMConfig(t testing.TB, cfg *FleetConfig, cert, key []byte, appleBMToken *nanodep_client.OAuth1Tokens) {
tlsCert, err := tls.X509KeyPair(cert, key)
if err != nil {
t.Fatal(err)
}
parsed, err := x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
t.Fatal(err)
}
tlsCert.Leaf = parsed
cfg.MDM.AppleAPNsCertBytes = string(cert)
cfg.MDM.AppleAPNsKeyBytes = string(key)
cfg.MDM.AppleSCEPCertBytes = string(cert)
cfg.MDM.AppleSCEPKeyBytes = string(key)
cfg.MDM.AppleBMCertBytes = string(cert)
cfg.MDM.AppleBMKeyBytes = string(key)
cfg.MDM.AppleBMServerTokenBytes = "whatever-will-not-be-accessed"
cfg.MDM.appleAPNs = &tlsCert
cfg.MDM.appleAPNsPEMCert = cert
cfg.MDM.appleAPNsPEMKey = key
cfg.MDM.appleSCEP = &tlsCert
cfg.MDM.appleSCEPPEMCert = cert
cfg.MDM.appleSCEPPEMKey = key
cfg.MDM.appleBMToken = appleBMToken
cfg.MDM.AppleSCEPSignerValidityDays = 365
cfg.MDM.AppleSCEPChallenge = "testchallenge"
}
// Undocumented feature flag for Microsoft MDM, used to determine if the Windows
// MDM feature is visible in the UI and can be enabled. More details here:
// https://github.com/fleetdm/fleet/issues/12257
//
// TODO: remove this flag once the Microsoft MDM feature is ready for
// release.
func IsMDMFeatureFlagEnabled() bool {
return os.Getenv("FLEET_DEV_MDM_ENABLED") == "1"
}