github.com/crowdsecurity/crowdsec@v1.6.1/pkg/csconfig/database.go (about) 1 package csconfig 2 3 import ( 4 "errors" 5 "fmt" 6 "path/filepath" 7 "time" 8 9 "entgo.io/ent/dialect" 10 log "github.com/sirupsen/logrus" 11 12 "github.com/crowdsecurity/go-cs-lib/ptr" 13 14 "github.com/crowdsecurity/crowdsec/pkg/types" 15 ) 16 17 const ( 18 DEFAULT_MAX_OPEN_CONNS = 100 19 defaultDecisionBulkSize = 1000 20 // we need an upper bound due to the sqlite limit of 32k variables in a query 21 // we have 15 variables per decision, so 32768/15 = 2184.5333 22 maxDecisionBulkSize = 2000 23 ) 24 25 type DatabaseCfg struct { 26 User string `yaml:"user"` 27 Password string `yaml:"password"` 28 DbName string `yaml:"db_name"` 29 Sslmode string `yaml:"sslmode"` 30 Host string `yaml:"host"` 31 Port int `yaml:"port"` 32 DbPath string `yaml:"db_path"` 33 Type string `yaml:"type"` 34 Flush *FlushDBCfg `yaml:"flush"` 35 LogLevel *log.Level `yaml:"log_level"` 36 MaxOpenConns *int `yaml:"max_open_conns,omitempty"` 37 UseWal *bool `yaml:"use_wal,omitempty"` 38 DecisionBulkSize int `yaml:"decision_bulk_size,omitempty"` 39 } 40 41 type AuthGCCfg struct { 42 Cert *string `yaml:"cert,omitempty"` 43 CertDuration *time.Duration 44 Api *string `yaml:"api_key,omitempty"` 45 ApiDuration *time.Duration 46 LoginPassword *string `yaml:"login_password,omitempty"` 47 LoginPasswordDuration *time.Duration 48 } 49 50 type FlushDBCfg struct { 51 MaxItems *int `yaml:"max_items,omitempty"` 52 // We could unmarshal as time.Duration, but alert filters right now are a map of strings 53 MaxAge *string `yaml:"max_age,omitempty"` 54 BouncersGC *AuthGCCfg `yaml:"bouncers_autodelete,omitempty"` 55 AgentsGC *AuthGCCfg `yaml:"agents_autodelete,omitempty"` 56 } 57 58 func (c *Config) LoadDBConfig(inCli bool) error { 59 if c.DbConfig == nil { 60 return errors.New("no database configuration provided") 61 } 62 63 if c.Cscli != nil { 64 c.Cscli.DbConfig = c.DbConfig 65 } 66 67 if c.API != nil && c.API.Server != nil { 68 c.API.Server.DbConfig = c.DbConfig 69 } 70 71 if c.DbConfig.MaxOpenConns == nil { 72 c.DbConfig.MaxOpenConns = ptr.Of(DEFAULT_MAX_OPEN_CONNS) 73 } 74 75 if !inCli && c.DbConfig.Type == "sqlite" { 76 if c.DbConfig.UseWal == nil { 77 dbDir := filepath.Dir(c.DbConfig.DbPath) 78 isNetwork, fsType, err := types.IsNetworkFS(dbDir) 79 if err != nil { 80 log.Warnf("unable to determine if database is on network filesystem: %s", err) 81 log.Warning("You are using sqlite without WAL, this can have a performance impact. If you do not store the database in a network share, set db_config.use_wal to true. Set explicitly to false to disable this warning.") 82 return nil 83 } 84 if isNetwork { 85 log.Debugf("database is on network filesystem (%s), setting useWal to false", fsType) 86 c.DbConfig.UseWal = ptr.Of(false) 87 } else { 88 log.Debugf("database is on local filesystem (%s), setting useWal to true", fsType) 89 c.DbConfig.UseWal = ptr.Of(true) 90 } 91 } else if *c.DbConfig.UseWal { 92 dbDir := filepath.Dir(c.DbConfig.DbPath) 93 isNetwork, fsType, err := types.IsNetworkFS(dbDir) 94 if err != nil { 95 log.Warnf("unable to determine if database is on network filesystem: %s", err) 96 return nil 97 } 98 if isNetwork { 99 log.Warnf("database seems to be stored on a network share (%s), but useWal is set to true. Proceed at your own risk.", fsType) 100 } 101 } 102 } 103 104 if c.DbConfig.DecisionBulkSize == 0 { 105 log.Tracef("No decision_bulk_size value provided, using default value of %d", defaultDecisionBulkSize) 106 c.DbConfig.DecisionBulkSize = defaultDecisionBulkSize 107 } 108 109 if c.DbConfig.DecisionBulkSize > maxDecisionBulkSize { 110 log.Warningf("decision_bulk_size too high (%d), setting to the maximum value of %d", c.DbConfig.DecisionBulkSize, maxDecisionBulkSize) 111 c.DbConfig.DecisionBulkSize = maxDecisionBulkSize 112 } 113 114 return nil 115 } 116 117 func (d *DatabaseCfg) ConnectionString() string { 118 connString := "" 119 120 switch d.Type { 121 case "sqlite": 122 var sqliteConnectionStringParameters string 123 if d.UseWal != nil && *d.UseWal { 124 sqliteConnectionStringParameters = "_busy_timeout=100000&_fk=1&_journal_mode=WAL" 125 } else { 126 sqliteConnectionStringParameters = "_busy_timeout=100000&_fk=1" 127 } 128 129 connString = fmt.Sprintf("file:%s?%s", d.DbPath, sqliteConnectionStringParameters) 130 case "mysql": 131 if d.isSocketConfig() { 132 connString = fmt.Sprintf("%s:%s@unix(%s)/%s?parseTime=True", d.User, d.Password, d.DbPath, d.DbName) 133 } else { 134 connString = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=True", d.User, d.Password, d.Host, d.Port, d.DbName) 135 } 136 case "postgres", "postgresql", "pgx": 137 if d.isSocketConfig() { 138 connString = fmt.Sprintf("host=%s user=%s dbname=%s password=%s", d.DbPath, d.User, d.DbName, d.Password) 139 } else { 140 connString = fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s sslmode=%s", d.Host, d.Port, d.User, d.DbName, d.Password, d.Sslmode) 141 } 142 } 143 144 return connString 145 } 146 147 func (d *DatabaseCfg) ConnectionDialect() (string, string, error) { 148 switch d.Type { 149 case "sqlite": 150 return "sqlite3", dialect.SQLite, nil 151 case "mysql": 152 return "mysql", dialect.MySQL, nil 153 case "pgx", "postgresql", "postgres": 154 if d.Type != "pgx" { 155 log.Debugf("database type '%s' is deprecated, switching to 'pgx' instead", d.Type) 156 } 157 158 return "pgx", dialect.Postgres, nil 159 } 160 161 return "", "", fmt.Errorf("unknown database type '%s'", d.Type) 162 } 163 164 func (d *DatabaseCfg) isSocketConfig() bool { 165 return d.Host == "" && d.Port == 0 && d.DbPath != "" 166 }