Refactor configuration management (#698)

This commit is contained in:
AlteredCoder 2021-03-24 18:16:17 +01:00 committed by GitHub
parent bf192593b6
commit 1e899c2211
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 2014 additions and 721 deletions

View file

@ -199,6 +199,9 @@ func NewAlertsCmd() *cobra.Command {
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
var err error var err error
if err := csConfig.LoadAPIClient(); err != nil {
log.Fatalf("loading api client: %s", err.Error())
}
if csConfig.API.Client == nil { if csConfig.API.Client == nil {
log.Fatalln("There is no configuration on 'api_client:'") log.Fatalln("There is no configuration on 'api_client:'")
} }

View file

@ -31,6 +31,9 @@ To list/add/delete bouncers
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
var err error var err error
if err := csConfig.LoadDBConfig(); err != nil {
log.Fatalf(err.Error())
}
dbClient, err = database.NewClient(csConfig.DbConfig) dbClient, err = database.NewClient(csConfig.DbConfig)
if err != nil { if err != nil {
log.Fatalf("unable to create new database client: %s", err) log.Fatalf("unable to create new database client: %s", err)

View file

@ -28,11 +28,14 @@ func NewCapiCmd() *cobra.Command {
Short: "Manage interaction with Central API (CAPI)", Short: "Manage interaction with Central API (CAPI)",
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadAPIServer(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.API.Server == nil { if csConfig.API.Server == nil {
log.Fatalln("There is no API->server configuration") log.Fatalln("There is no API->server configuration")
} }
if csConfig.API.Server.OnlineClient == nil { if csConfig.API.Server.OnlineClient == nil {
log.Fatalf("no configuration for crowdsec API in '%s'", *csConfig.Self) log.Fatalf("no configuration for crowdsec API in '%s'", *csConfig.FilePath)
} }
return nil return nil
@ -124,7 +127,12 @@ func NewCapiCmd() *cobra.Command {
if err != nil { if err != nil {
log.Fatalf("parsing api url ('%s'): %s", csConfig.API.Server.OnlineClient.Credentials.URL, err) log.Fatalf("parsing api url ('%s'): %s", csConfig.API.Server.OnlineClient.Credentials.URL, err)
} }
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to load hub index : %s", err) log.Fatalf("Failed to load hub index : %s", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index") log.Infoln("Run 'sudo cscli hub update' to get the hub index")
} }

View file

@ -18,13 +18,22 @@ func NewCollectionsCmd() *cobra.Command {
/*TBD fix help*/ /*TBD fix help*/
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if csConfig.Cscli == nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.Hub == nil {
return fmt.Errorf("you must configure cli before interacting with hub") return fmt.Errorf("you must configure cli before interacting with hub")
} }
if err := setHubBranch(); err != nil { if err := setHubBranch(); err != nil {
return fmt.Errorf("error while setting hub branch: %s", err) return fmt.Errorf("error while setting hub branch: %s", err)
} }
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
return nil return nil
}, },
PersistentPostRun: func(cmd *cobra.Command, args []string) { PersistentPostRun: func(cmd *cobra.Command, args []string) {
@ -42,10 +51,6 @@ func NewCollectionsCmd() *cobra.Command {
Example: `cscli collections install crowdsec/xxx crowdsec/xyz`, Example: `cscli collections install crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
for _, name := range args { for _, name := range args {
InstallItem(name, cwhub.COLLECTIONS, forceAction) InstallItem(name, cwhub.COLLECTIONS, forceAction)
} }
@ -62,11 +67,6 @@ func NewCollectionsCmd() *cobra.Command {
Example: `cscli collections remove crowdsec/xxx crowdsec/xyz`, Example: `cscli collections remove crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
RemoveMany(cwhub.COLLECTIONS, "") RemoveMany(cwhub.COLLECTIONS, "")
} else { } else {
@ -95,10 +95,6 @@ func NewCollectionsCmd() *cobra.Command {
Long: `Fetch and upgrade given collection(s) from hub`, Long: `Fetch and upgrade given collection(s) from hub`,
Example: `cscli collections upgrade crowdsec/xxx crowdsec/xyz`, Example: `cscli collections upgrade crowdsec/xxx crowdsec/xyz`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
UpgradeConfig(cwhub.COLLECTIONS, "", forceAction) UpgradeConfig(cwhub.COLLECTIONS, "", forceAction)
} else { } else {
@ -119,10 +115,6 @@ func NewCollectionsCmd() *cobra.Command {
Example: `cscli collections inspect crowdsec/xxx crowdsec/xyz`, Example: `cscli collections inspect crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
for _, name := range args { for _, name := range args {
InspectItem(name, cwhub.COLLECTIONS) InspectItem(name, cwhub.COLLECTIONS)
} }
@ -138,10 +130,6 @@ func NewCollectionsCmd() *cobra.Command {
Example: `cscli collections list`, Example: `cscli collections list`,
Args: cobra.ExactArgs(0), Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
ListItem(cwhub.COLLECTIONS, args) ListItem(cwhub.COLLECTIONS, args)
}, },
} }

View file

@ -44,7 +44,7 @@ func backupConfigToDirectory(dirPath string) error {
return errors.Wrapf(err, "while checking parent directory %s existence", parentDir) return errors.Wrapf(err, "while checking parent directory %s existence", parentDir)
} }
if err = os.Mkdir(dirPath, 0600); err != nil { if err = os.Mkdir(dirPath, 0700); err != nil {
return fmt.Errorf("error while creating %s : %s", dirPath, err) return fmt.Errorf("error while creating %s : %s", dirPath, err)
} }
@ -68,7 +68,7 @@ func backupConfigToDirectory(dirPath string) error {
} }
acquisBackupDir := dirPath + "/acquis/" acquisBackupDir := dirPath + "/acquis/"
if err = os.Mkdir(acquisBackupDir, 0600); err != nil { if err = os.Mkdir(acquisBackupDir, 0700); err != nil {
return fmt.Errorf("error while creating %s : %s", acquisBackupDir, err) return fmt.Errorf("error while creating %s : %s", acquisBackupDir, err)
} }
@ -215,7 +215,7 @@ func restoreConfigFromDirectory(dirPath string) error {
/*if there is a acquisition dir, restore its content*/ /*if there is a acquisition dir, restore its content*/
if csConfig.Crowdsec.AcquisitionDirPath != "" { if csConfig.Crowdsec.AcquisitionDirPath != "" {
if err = os.Mkdir(csConfig.Crowdsec.AcquisitionDirPath, 0600); err != nil { if err = os.Mkdir(csConfig.Crowdsec.AcquisitionDirPath, 0700); err != nil {
return fmt.Errorf("error while creating %s : %s", csConfig.Crowdsec.AcquisitionDirPath, err) return fmt.Errorf("error while creating %s : %s", csConfig.Crowdsec.AcquisitionDirPath, err)
} }
@ -389,7 +389,10 @@ func NewConfigCmd() *cobra.Command {
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var err error var err error
if err = cwhub.GetHubIdx(csConfig.Cscli); err != nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err = cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err) log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index") log.Infoln("Run 'sudo cscli hub update' to get the hub index")
} }
@ -414,7 +417,7 @@ func NewConfigCmd() *cobra.Command {
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var err error var err error
if err = cwhub.GetHubIdx(csConfig.Cscli); err != nil { if err = cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err) log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index") log.Infoln("Run 'sudo cscli hub update' to get the hub index")
} }

View file

@ -105,6 +105,9 @@ func NewDecisionsCmd() *cobra.Command {
/*TBD example*/ /*TBD example*/
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
if err := csConfig.LoadAPIClient(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.API.Client == nil { if csConfig.API.Client == nil {
log.Fatalln("There is no configuration on 'api_client:'") log.Fatalln("There is no configuration on 'api_client:'")
} }

View file

@ -40,7 +40,10 @@ cscli hub update # Download list of available configurations from the hub
Short: "List installed configs", Short: "List installed configs",
Args: cobra.ExactArgs(0), Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err) log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index") log.Infoln("Run 'sudo cscli hub update' to get the hub index")
} }
@ -77,7 +80,10 @@ Fetches the [.index.json](https://github.com/crowdsecurity/hub/blob/master/.inde
return nil return nil
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.UpdateHubIdx(csConfig.Cscli); err != nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err := cwhub.UpdateHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err) log.Fatalf("Failed to get Hub index : %v", err)
} }
}, },
@ -102,7 +108,10 @@ Upgrade all configs installed from Crowdsec Hub. Run 'sudo cscli hub update' if
return nil return nil
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err) log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index") log.Infoln("Run 'sudo cscli hub update' to get the hub index")
} }

View file

@ -28,11 +28,14 @@ func NewLapiCmd() *cobra.Command {
Short: "Manage interaction with Local API (LAPI)", Short: "Manage interaction with Local API (LAPI)",
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadAPIClient(); err != nil {
return fmt.Errorf("loading api client: %s", err.Error())
}
if csConfig.API.Client == nil { if csConfig.API.Client == nil {
log.Fatalln("There is no API->client configuration") log.Fatalln("There is no API->client configuration")
} }
if csConfig.API.Client.Credentials == nil { if csConfig.API.Client.Credentials == nil {
log.Fatalf("no configuration for crowdsec API in '%s'", *csConfig.Self) log.Fatalf("no configuration for crowdsec API in '%s'", *csConfig.FilePath)
} }
return nil return nil
}, },
@ -133,7 +136,11 @@ Keep in mind the machine needs to be validated by an administrator on LAPI side
if err != nil { if err != nil {
log.Fatalf("parsing api url ('%s'): %s", apiurl, err) log.Fatalf("parsing api url ('%s'): %s", apiurl, err)
} }
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to load hub index : %s", err) log.Fatalf("Failed to load hub index : %s", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index") log.Infoln("Run 'sudo cscli hub update' to get the hub index")
} }

View file

@ -84,9 +84,15 @@ func NewMachinesCmd() *cobra.Command {
Long: ` Long: `
Machines Management. Machines Management.
To list/add/delete/register/validate machines To list/add/delete/validate machines
`, `,
Example: `cscli machines [action]`, Example: `cscli machines [action]`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadDBConfig(); err != nil {
log.Fatalf(err.Error())
}
return nil
},
} }
var cmdMachinesList = &cobra.Command{ var cmdMachinesList = &cobra.Command{
@ -97,6 +103,7 @@ To list/add/delete/register/validate machines
Args: cobra.MaximumNArgs(1), Args: cobra.MaximumNArgs(1),
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
var err error var err error
dbClient, err = database.NewClient(csConfig.DbConfig) dbClient, err = database.NewClient(csConfig.DbConfig)
if err != nil { if err != nil {
log.Fatalf("unable to create new database client: %s", err) log.Fatalf("unable to create new database client: %s", err)

View file

@ -14,7 +14,7 @@ import (
var trace_lvl, dbg_lvl, nfo_lvl, wrn_lvl, err_lvl bool var trace_lvl, dbg_lvl, nfo_lvl, wrn_lvl, err_lvl bool
var ConfigFilePath string var ConfigFilePath string
var csConfig *csconfig.GlobalConfig var csConfig *csconfig.Config
var dbClient *database.Client var dbClient *database.Client
var OutputFormat string var OutputFormat string
@ -28,7 +28,7 @@ var restoreOldBackup bool
var prometheusURL string var prometheusURL string
func initConfig() { func initConfig() {
var err error
if trace_lvl { if trace_lvl {
log.SetLevel(log.TraceLevel) log.SetLevel(log.TraceLevel)
} else if dbg_lvl { } else if dbg_lvl {
@ -42,12 +42,15 @@ func initConfig() {
} }
logFormatter := &log.TextFormatter{TimestampFormat: "02-01-2006 03:04:05 PM", FullTimestamp: true} logFormatter := &log.TextFormatter{TimestampFormat: "02-01-2006 03:04:05 PM", FullTimestamp: true}
log.SetFormatter(logFormatter) log.SetFormatter(logFormatter)
csConfig = csconfig.NewConfig() csConfig, err = csconfig.NewConfig(ConfigFilePath, false, false)
if err != nil {
log.Debugf("Using %s as configuration file", ConfigFilePath)
if err := csConfig.LoadConfigurationFile(ConfigFilePath, csConfig.DisableAPI, csConfig.DisableAgent); err != nil {
log.Fatalf(err.Error()) log.Fatalf(err.Error())
} }
log.Debugf("Using %s as configuration file", ConfigFilePath)
if err := csConfig.LoadCSCLI(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.Cscli == nil { if csConfig.Cscli == nil {
log.Fatalf("missing 'cscli' configuration in '%s', exiting", ConfigFilePath) log.Fatalf("missing 'cscli' configuration in '%s', exiting", ConfigFilePath)
} }

View file

@ -377,6 +377,9 @@ func NewMetricsCmd() *cobra.Command {
Long: `Fetch metrics from the prometheus server and display them in a human-friendly way`, Long: `Fetch metrics from the prometheus server and display them in a human-friendly way`,
Args: cobra.ExactArgs(0), Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := csConfig.LoadPrometheus(); err != nil {
log.Fatalf(err.Error())
}
if !csConfig.Prometheus.Enabled { if !csConfig.Prometheus.Enabled {
log.Warningf("Prometheus is not enabled, can't show metrics") log.Warningf("Prometheus is not enabled, can't show metrics")
os.Exit(1) os.Exit(1)
@ -387,7 +390,7 @@ func NewMetricsCmd() *cobra.Command {
} }
if prometheusURL == "" { if prometheusURL == "" {
log.Errorf("No prometheus url, please specify in %s or via -u", *csConfig.Self) log.Errorf("No prometheus url, please specify in %s or via -u", *csConfig.FilePath)
os.Exit(1) os.Exit(1)
} }

View file

@ -22,13 +22,21 @@ cscli parsers remove crowdsecurity/sshd-logs
`, `,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if csConfig.Cscli == nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.Hub == nil {
return fmt.Errorf("you must configure cli before interacting with hub") return fmt.Errorf("you must configure cli before interacting with hub")
} }
if err := setHubBranch(); err != nil { if err := setHubBranch(); err != nil {
return fmt.Errorf("error while setting hub branch: %s", err) return fmt.Errorf("error while setting hub branch: %s", err)
} }
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
return nil return nil
}, },
PersistentPostRun: func(cmd *cobra.Command, args []string) { PersistentPostRun: func(cmd *cobra.Command, args []string) {
@ -46,10 +54,6 @@ cscli parsers remove crowdsecurity/sshd-logs
Example: `cscli parsers install crowdsec/xxx crowdsec/xyz`, Example: `cscli parsers install crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
for _, name := range args { for _, name := range args {
InstallItem(name, cwhub.PARSERS, forceAction) InstallItem(name, cwhub.PARSERS, forceAction)
} }
@ -66,11 +70,6 @@ cscli parsers remove crowdsecurity/sshd-logs
Example: `cscli parsers remove crowdsec/xxx crowdsec/xyz`, Example: `cscli parsers remove crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
RemoveMany(cwhub.PARSERS, "") RemoveMany(cwhub.PARSERS, "")
} else { } else {
@ -91,10 +90,6 @@ cscli parsers remove crowdsecurity/sshd-logs
Long: `Fetch and upgrade given parser(s) from hub`, Long: `Fetch and upgrade given parser(s) from hub`,
Example: `cscli parsers upgrade crowdsec/xxx crowdsec/xyz`, Example: `cscli parsers upgrade crowdsec/xxx crowdsec/xyz`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
UpgradeConfig(cwhub.PARSERS, "", forceAction) UpgradeConfig(cwhub.PARSERS, "", forceAction)
} else { } else {
@ -115,11 +110,6 @@ cscli parsers remove crowdsecurity/sshd-logs
Example: `cscli parsers inspect crowdsec/xxx`, Example: `cscli parsers inspect crowdsec/xxx`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
InspectItem(args[0], cwhub.PARSERS) InspectItem(args[0], cwhub.PARSERS)
}, },
} }
@ -133,10 +123,6 @@ cscli parsers remove crowdsecurity/sshd-logs
Example: `cscli parsers list Example: `cscli parsers list
cscli parser list crowdsecurity/xxx`, cscli parser list crowdsecurity/xxx`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
ListItem(cwhub.PARSERS, args) ListItem(cwhub.PARSERS, args)
}, },
} }

View file

@ -21,13 +21,21 @@ func NewPostOverflowsCmd() *cobra.Command {
cscli postoverflows remove crowdsecurity/cdn-whitelist`, cscli postoverflows remove crowdsecurity/cdn-whitelist`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if csConfig.Cscli == nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.Hub == nil {
return fmt.Errorf("you must configure cli before interacting with hub") return fmt.Errorf("you must configure cli before interacting with hub")
} }
if err := setHubBranch(); err != nil { if err := setHubBranch(); err != nil {
return fmt.Errorf("error while setting hub branch: %s", err) return fmt.Errorf("error while setting hub branch: %s", err)
} }
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
return nil return nil
}, },
PersistentPostRun: func(cmd *cobra.Command, args []string) { PersistentPostRun: func(cmd *cobra.Command, args []string) {
@ -45,10 +53,6 @@ func NewPostOverflowsCmd() *cobra.Command {
Example: `cscli postoverflows install crowdsec/xxx crowdsec/xyz`, Example: `cscli postoverflows install crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
for _, name := range args { for _, name := range args {
InstallItem(name, cwhub.PARSERS_OVFLW, forceAction) InstallItem(name, cwhub.PARSERS_OVFLW, forceAction)
} }
@ -65,11 +69,6 @@ func NewPostOverflowsCmd() *cobra.Command {
Example: `cscli postoverflows remove crowdsec/xxx crowdsec/xyz`, Example: `cscli postoverflows remove crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
RemoveMany(cwhub.PARSERS_OVFLW, "") RemoveMany(cwhub.PARSERS_OVFLW, "")
} else { } else {
@ -90,10 +89,6 @@ func NewPostOverflowsCmd() *cobra.Command {
Long: `Fetch and Upgrade given postoverflow(s) from hub`, Long: `Fetch and Upgrade given postoverflow(s) from hub`,
Example: `cscli postoverflows upgrade crowdsec/xxx crowdsec/xyz`, Example: `cscli postoverflows upgrade crowdsec/xxx crowdsec/xyz`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
UpgradeConfig(cwhub.PARSERS_OVFLW, "", forceAction) UpgradeConfig(cwhub.PARSERS_OVFLW, "", forceAction)
} else { } else {
@ -114,10 +109,6 @@ func NewPostOverflowsCmd() *cobra.Command {
Example: `cscli postoverflows inspect crowdsec/xxx crowdsec/xyz`, Example: `cscli postoverflows inspect crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
InspectItem(args[0], cwhub.PARSERS_OVFLW) InspectItem(args[0], cwhub.PARSERS_OVFLW)
}, },
} }
@ -130,10 +121,6 @@ func NewPostOverflowsCmd() *cobra.Command {
Example: `cscli postoverflows list Example: `cscli postoverflows list
cscli postoverflows list crowdsecurity/xxx`, cscli postoverflows list crowdsecurity/xxx`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
ListItem(cwhub.PARSERS_OVFLW, args) ListItem(cwhub.PARSERS_OVFLW, args)
}, },
} }

View file

@ -22,13 +22,21 @@ cscli scenarios remove crowdsecurity/ssh-bf
`, `,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if csConfig.Cscli == nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.Hub == nil {
return fmt.Errorf("you must configure cli before interacting with hub") return fmt.Errorf("you must configure cli before interacting with hub")
} }
if err := setHubBranch(); err != nil { if err := setHubBranch(); err != nil {
return fmt.Errorf("error while setting hub branch: %s", err) return fmt.Errorf("error while setting hub branch: %s", err)
} }
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
return nil return nil
}, },
PersistentPostRun: func(cmd *cobra.Command, args []string) { PersistentPostRun: func(cmd *cobra.Command, args []string) {
@ -46,10 +54,6 @@ cscli scenarios remove crowdsecurity/ssh-bf
Example: `cscli scenarios install crowdsec/xxx crowdsec/xyz`, Example: `cscli scenarios install crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
for _, name := range args { for _, name := range args {
InstallItem(name, cwhub.SCENARIOS, forceAction) InstallItem(name, cwhub.SCENARIOS, forceAction)
} }
@ -66,11 +70,6 @@ cscli scenarios remove crowdsecurity/ssh-bf
Example: `cscli scenarios remove crowdsec/xxx crowdsec/xyz`, Example: `cscli scenarios remove crowdsec/xxx crowdsec/xyz`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
RemoveMany(cwhub.SCENARIOS, "") RemoveMany(cwhub.SCENARIOS, "")
} else { } else {
@ -91,10 +90,6 @@ cscli scenarios remove crowdsecurity/ssh-bf
Long: `Fetch and Upgrade given scenario(s) from hub`, Long: `Fetch and Upgrade given scenario(s) from hub`,
Example: `cscli scenarios upgrade crowdsec/xxx crowdsec/xyz`, Example: `cscli scenarios upgrade crowdsec/xxx crowdsec/xyz`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
if all { if all {
UpgradeConfig(cwhub.SCENARIOS, "", forceAction) UpgradeConfig(cwhub.SCENARIOS, "", forceAction)
} else { } else {
@ -115,10 +110,6 @@ cscli scenarios remove crowdsecurity/ssh-bf
Example: `cscli scenarios inspect crowdsec/xxx`, Example: `cscli scenarios inspect crowdsec/xxx`,
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
InspectItem(args[0], cwhub.SCENARIOS) InspectItem(args[0], cwhub.SCENARIOS)
}, },
} }
@ -132,10 +123,6 @@ cscli scenarios remove crowdsecurity/ssh-bf
Example: `cscli scenarios list Example: `cscli scenarios list
cscli scenarios list crowdsecurity/xxx`, cscli scenarios list crowdsecurity/xxx`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil {
log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index")
}
ListItem(cwhub.SCENARIOS, args) ListItem(cwhub.SCENARIOS, args)
}, },
} }

View file

@ -11,25 +11,25 @@ import (
) )
func addToExclusion(name string) error { func addToExclusion(name string) error {
csConfig.Crowdsec.SimulationConfig.Exclusions = append(csConfig.Crowdsec.SimulationConfig.Exclusions, name) csConfig.Cscli.SimulationConfig.Exclusions = append(csConfig.Cscli.SimulationConfig.Exclusions, name)
return nil return nil
} }
func removeFromExclusion(name string) error { func removeFromExclusion(name string) error {
index := indexOf(name, csConfig.Crowdsec.SimulationConfig.Exclusions) index := indexOf(name, csConfig.Cscli.SimulationConfig.Exclusions)
// Remove element from the slice // Remove element from the slice
csConfig.Crowdsec.SimulationConfig.Exclusions[index] = csConfig.Crowdsec.SimulationConfig.Exclusions[len(csConfig.Crowdsec.SimulationConfig.Exclusions)-1] csConfig.Cscli.SimulationConfig.Exclusions[index] = csConfig.Cscli.SimulationConfig.Exclusions[len(csConfig.Cscli.SimulationConfig.Exclusions)-1]
csConfig.Crowdsec.SimulationConfig.Exclusions[len(csConfig.Crowdsec.SimulationConfig.Exclusions)-1] = "" csConfig.Cscli.SimulationConfig.Exclusions[len(csConfig.Cscli.SimulationConfig.Exclusions)-1] = ""
csConfig.Crowdsec.SimulationConfig.Exclusions = csConfig.Crowdsec.SimulationConfig.Exclusions[:len(csConfig.Crowdsec.SimulationConfig.Exclusions)-1] csConfig.Cscli.SimulationConfig.Exclusions = csConfig.Cscli.SimulationConfig.Exclusions[:len(csConfig.Cscli.SimulationConfig.Exclusions)-1]
return nil return nil
} }
func enableGlobalSimulation() error { func enableGlobalSimulation() error {
csConfig.Crowdsec.SimulationConfig.Simulation = new(bool) csConfig.Cscli.SimulationConfig.Simulation = new(bool)
*csConfig.Crowdsec.SimulationConfig.Simulation = true *csConfig.Cscli.SimulationConfig.Simulation = true
csConfig.Crowdsec.SimulationConfig.Exclusions = []string{} csConfig.Cscli.SimulationConfig.Exclusions = []string{}
if err := dumpSimulationFile(); err != nil { if err := dumpSimulationFile(); err != nil {
log.Fatalf("unable to dump simulation file: %s", err.Error()) log.Fatalf("unable to dump simulation file: %s", err.Error())
@ -41,7 +41,7 @@ func enableGlobalSimulation() error {
} }
func dumpSimulationFile() error { func dumpSimulationFile() error {
newConfigSim, err := yaml.Marshal(csConfig.Crowdsec.SimulationConfig) newConfigSim, err := yaml.Marshal(csConfig.Cscli.SimulationConfig)
if err != nil { if err != nil {
return fmt.Errorf("unable to marshal simulation configuration: %s", err) return fmt.Errorf("unable to marshal simulation configuration: %s", err)
} }
@ -55,11 +55,11 @@ func dumpSimulationFile() error {
} }
func disableGlobalSimulation() error { func disableGlobalSimulation() error {
csConfig.Crowdsec.SimulationConfig.Simulation = new(bool) csConfig.Cscli.SimulationConfig.Simulation = new(bool)
*csConfig.Crowdsec.SimulationConfig.Simulation = false *csConfig.Cscli.SimulationConfig.Simulation = false
csConfig.Crowdsec.SimulationConfig.Exclusions = []string{} csConfig.Cscli.SimulationConfig.Exclusions = []string{}
newConfigSim, err := yaml.Marshal(csConfig.Crowdsec.SimulationConfig) newConfigSim, err := yaml.Marshal(csConfig.Cscli.SimulationConfig)
if err != nil { if err != nil {
return fmt.Errorf("unable to marshal new simulation configuration: %s", err) return fmt.Errorf("unable to marshal new simulation configuration: %s", err)
} }
@ -73,23 +73,23 @@ func disableGlobalSimulation() error {
} }
func simulationStatus() error { func simulationStatus() error {
if csConfig.Crowdsec.SimulationConfig == nil { if csConfig.Cscli.SimulationConfig == nil {
log.Printf("global simulation: disabled (configuration file is missing)") log.Printf("global simulation: disabled (configuration file is missing)")
return nil return nil
} }
if *csConfig.Crowdsec.SimulationConfig.Simulation { if *csConfig.Cscli.SimulationConfig.Simulation {
log.Println("global simulation: enabled") log.Println("global simulation: enabled")
if len(csConfig.Crowdsec.SimulationConfig.Exclusions) > 0 { if len(csConfig.Cscli.SimulationConfig.Exclusions) > 0 {
log.Println("Scenarios not in simulation mode :") log.Println("Scenarios not in simulation mode :")
for _, scenario := range csConfig.Crowdsec.SimulationConfig.Exclusions { for _, scenario := range csConfig.Cscli.SimulationConfig.Exclusions {
log.Printf(" - %s", scenario) log.Printf(" - %s", scenario)
} }
} }
} else { } else {
log.Println("global simulation: disabled") log.Println("global simulation: disabled")
if len(csConfig.Crowdsec.SimulationConfig.Exclusions) > 0 { if len(csConfig.Cscli.SimulationConfig.Exclusions) > 0 {
log.Println("Scenarios in simulation mode :") log.Println("Scenarios in simulation mode :")
for _, scenario := range csConfig.Crowdsec.SimulationConfig.Exclusions { for _, scenario := range csConfig.Cscli.SimulationConfig.Exclusions {
log.Printf(" - %s", scenario) log.Printf(" - %s", scenario)
} }
} }
@ -105,9 +105,15 @@ func NewSimulationCmds() *cobra.Command {
cscli simulation enable crowdsecurity/ssh-bf cscli simulation enable crowdsecurity/ssh-bf
cscli simulation disable crowdsecurity/ssh-bf`, cscli simulation disable crowdsecurity/ssh-bf`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := csConfig.LoadSimulation(); err != nil {
log.Fatalf(err.Error())
}
if csConfig.Cscli == nil { if csConfig.Cscli == nil {
return fmt.Errorf("you must configure cli before using simulation") return fmt.Errorf("you must configure cli before using simulation")
} }
if csConfig.Cscli.SimulationConfig == nil {
return fmt.Errorf("no simulation configured")
}
return nil return nil
}, },
PersistentPostRun: func(cmd *cobra.Command, args []string) { PersistentPostRun: func(cmd *cobra.Command, args []string) {
@ -125,7 +131,10 @@ cscli simulation disable crowdsecurity/ssh-bf`,
Short: "Enable the simulation, globally or on specified scenarios", Short: "Enable the simulation, globally or on specified scenarios",
Example: `cscli simulation enable`, Example: `cscli simulation enable`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := cwhub.GetHubIdx(csConfig.Cscli); err != nil { if err := csConfig.LoadHub(); err != nil {
log.Fatalf(err.Error())
}
if err := cwhub.GetHubIdx(csConfig.Hub); err != nil {
log.Fatalf("Failed to get Hub index : %v", err) log.Fatalf("Failed to get Hub index : %v", err)
log.Infoln("Run 'sudo cscli hub update' to get the hub index") log.Infoln("Run 'sudo cscli hub update' to get the hub index")
} }
@ -143,16 +152,16 @@ cscli simulation disable crowdsecurity/ssh-bf`,
if !item.Installed { if !item.Installed {
log.Warningf("'%s' isn't enabled", scenario) log.Warningf("'%s' isn't enabled", scenario)
} }
isExcluded := inSlice(scenario, csConfig.Crowdsec.SimulationConfig.Exclusions) isExcluded := inSlice(scenario, csConfig.Cscli.SimulationConfig.Exclusions)
if *csConfig.Crowdsec.SimulationConfig.Simulation && !isExcluded { if *csConfig.Cscli.SimulationConfig.Simulation && !isExcluded {
log.Warningf("global simulation is already enabled") log.Warningf("global simulation is already enabled")
continue continue
} }
if !*csConfig.Crowdsec.SimulationConfig.Simulation && isExcluded { if !*csConfig.Cscli.SimulationConfig.Simulation && isExcluded {
log.Warningf("simulation for '%s' already enabled", scenario) log.Warningf("simulation for '%s' already enabled", scenario)
continue continue
} }
if *csConfig.Crowdsec.SimulationConfig.Simulation && isExcluded { if *csConfig.Cscli.SimulationConfig.Simulation && isExcluded {
if err := removeFromExclusion(scenario); err != nil { if err := removeFromExclusion(scenario); err != nil {
log.Fatalf(err.Error()) log.Fatalf(err.Error())
} }
@ -186,12 +195,12 @@ cscli simulation disable crowdsecurity/ssh-bf`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 { if len(args) > 0 {
for _, scenario := range args { for _, scenario := range args {
isExcluded := inSlice(scenario, csConfig.Crowdsec.SimulationConfig.Exclusions) isExcluded := inSlice(scenario, csConfig.Cscli.SimulationConfig.Exclusions)
if !*csConfig.Crowdsec.SimulationConfig.Simulation && !isExcluded { if !*csConfig.Cscli.SimulationConfig.Simulation && !isExcluded {
log.Warningf("%s isn't in simulation mode", scenario) log.Warningf("%s isn't in simulation mode", scenario)
continue continue
} }
if !*csConfig.Crowdsec.SimulationConfig.Simulation && isExcluded { if !*csConfig.Cscli.SimulationConfig.Simulation && isExcluded {
if err := removeFromExclusion(scenario); err != nil { if err := removeFromExclusion(scenario); err != nil {
log.Fatalf(err.Error()) log.Fatalf(err.Error())
} }

View file

@ -141,16 +141,16 @@ func InstallItem(name string, obtype string, force bool) {
return return
} }
} }
item, err := cwhub.DownloadLatest(csConfig.Cscli, item, force) item, err := cwhub.DownloadLatest(csConfig.Hub, item, force)
if err != nil { if err != nil {
log.Fatalf("error while downloading %s : %v", item.Name, err) log.Fatalf("error while downloading %s : %v", item.Name, err)
} }
cwhub.AddItem(obtype, item) cwhub.AddItem(obtype, item)
if downloadOnly { if downloadOnly {
log.Infof("Downloaded %s to %s", item.Name, csConfig.Cscli.HubDir+"/"+item.RemotePath) log.Infof("Downloaded %s to %s", item.Name, csConfig.Hub.HubDir+"/"+item.RemotePath)
return return
} }
item, err = cwhub.EnableItem(csConfig.Cscli, item) item, err = cwhub.EnableItem(csConfig.Hub, item)
if err != nil { if err != nil {
log.Fatalf("error while enabled %s : %v.", item.Name, err) log.Fatalf("error while enabled %s : %v.", item.Name, err)
} }
@ -168,7 +168,7 @@ func RemoveMany(itemType string, name string) {
log.Fatalf("unable to retrieve: %s", name) log.Fatalf("unable to retrieve: %s", name)
} }
item := *it item := *it
item, err = cwhub.DisableItem(csConfig.Cscli, item, purge, forceAction) item, err = cwhub.DisableItem(csConfig.Hub, item, purge, forceAction)
if err != nil { if err != nil {
log.Fatalf("unable to disable %s : %v", item.Name, err) log.Fatalf("unable to disable %s : %v", item.Name, err)
} }
@ -176,7 +176,7 @@ func RemoveMany(itemType string, name string) {
return return
} else if name == "" && all { } else if name == "" && all {
for _, v := range cwhub.GetItemMap(itemType) { for _, v := range cwhub.GetItemMap(itemType) {
v, err = cwhub.DisableItem(csConfig.Cscli, v, purge, forceAction) v, err = cwhub.DisableItem(csConfig.Hub, v, purge, forceAction)
if err != nil { if err != nil {
log.Fatalf("unable to disable %s : %v", v.Name, err) log.Fatalf("unable to disable %s : %v", v.Name, err)
} }
@ -219,7 +219,7 @@ func UpgradeConfig(itemType string, name string, force bool) {
continue continue
} }
} }
v, err = cwhub.DownloadLatest(csConfig.Cscli, v, force) v, err = cwhub.DownloadLatest(csConfig.Hub, v, force)
if err != nil { if err != nil {
log.Fatalf("%s : download failed : %v", v.Name, err) log.Fatalf("%s : download failed : %v", v.Name, err)
} }
@ -264,7 +264,7 @@ func InspectItem(name string, objecitemType string) {
fmt.Printf("%s", string(buff)) fmt.Printf("%s", string(buff))
if csConfig.Prometheus.Enabled { if csConfig.Prometheus.Enabled {
if csConfig.Prometheus.ListenAddr == "" || csConfig.Prometheus.ListenPort == 0 { if csConfig.Prometheus.ListenAddr == "" || csConfig.Prometheus.ListenPort == 0 {
log.Warningf("No prometheus address or port specified in '%s', can't show metrics", *csConfig.Self) log.Warningf("No prometheus address or port specified in '%s', can't show metrics", *csConfig.FilePath)
return return
} }
if prometheusURL == "" { if prometheusURL == "" {
@ -500,7 +500,7 @@ func silenceInstallItem(name string, obtype string) (string, error) {
if downloadOnly && it.Downloaded && it.UpToDate { if downloadOnly && it.Downloaded && it.UpToDate {
return fmt.Sprintf("%s is already downloaded and up-to-date", it.Name), nil return fmt.Sprintf("%s is already downloaded and up-to-date", it.Name), nil
} }
it, err := cwhub.DownloadLatest(csConfig.Cscli, it, forceAction) it, err := cwhub.DownloadLatest(csConfig.Hub, it, forceAction)
if err != nil { if err != nil {
return "", fmt.Errorf("error while downloading %s : %v", it.Name, err) return "", fmt.Errorf("error while downloading %s : %v", it.Name, err)
} }
@ -511,7 +511,7 @@ func silenceInstallItem(name string, obtype string) (string, error) {
if downloadOnly { if downloadOnly {
return fmt.Sprintf("Downloaded %s to %s", it.Name, csConfig.Cscli.HubDir+"/"+it.RemotePath), nil return fmt.Sprintf("Downloaded %s to %s", it.Name, csConfig.Cscli.HubDir+"/"+it.RemotePath), nil
} }
it, err = cwhub.EnableItem(csConfig.Cscli, it) it, err = cwhub.EnableItem(csConfig.Hub, it)
if err != nil { if err != nil {
return "", fmt.Errorf("error while enabled %s : %v", it.Name, err) return "", fmt.Errorf("error while enabled %s : %v", it.Name, err)
} }

View file

@ -9,7 +9,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func initAPIServer(cConfig *csconfig.GlobalConfig) (*apiserver.APIServer, error) { func initAPIServer(cConfig *csconfig.Config) (*apiserver.APIServer, error) {
apiServer, err := apiserver.NewServer(cConfig.API.Server) apiServer, err := apiserver.NewServer(cConfig.API.Server)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to run local API: %s", err) return nil, fmt.Errorf("unable to run local API: %s", err)

View file

@ -14,14 +14,14 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func initCrowdsec(cConfig *csconfig.GlobalConfig) (*parser.Parsers, error) { func initCrowdsec(cConfig *csconfig.Config) (*parser.Parsers, error) {
err := exprhelpers.Init() err := exprhelpers.Init()
if err != nil { if err != nil {
return &parser.Parsers{}, fmt.Errorf("Failed to init expr helpers : %s", err) return &parser.Parsers{}, fmt.Errorf("Failed to init expr helpers : %s", err)
} }
// Populate cwhub package tools // Populate cwhub package tools
if err := cwhub.GetHubIdx(cConfig.Cscli); err != nil { if err := cwhub.GetHubIdx(cConfig.Hub); err != nil {
return &parser.Parsers{}, fmt.Errorf("Failed to load hub index : %s", err) return &parser.Parsers{}, fmt.Errorf("Failed to load hub index : %s", err)
} }
@ -41,7 +41,7 @@ func initCrowdsec(cConfig *csconfig.GlobalConfig) (*parser.Parsers, error) {
return csParsers, nil return csParsers, nil
} }
func runCrowdsec(cConfig *csconfig.GlobalConfig, parsers *parser.Parsers) error { func runCrowdsec(cConfig *csconfig.Config, parsers *parser.Parsers) error {
inputLineChan := make(chan types.Event) inputLineChan := make(chan types.Event)
inputEventChan := make(chan types.Event) inputEventChan := make(chan types.Event)
@ -117,7 +117,7 @@ func runCrowdsec(cConfig *csconfig.GlobalConfig, parsers *parser.Parsers) error
return nil return nil
} }
func serveCrowdsec(parsers *parser.Parsers, cConfig *csconfig.GlobalConfig) { func serveCrowdsec(parsers *parser.Parsers, cConfig *csconfig.Config) {
crowdsecTomb.Go(func() error { crowdsecTomb.Go(func() error {
defer types.CatchPanic("crowdsec/serveCrowdsec") defer types.CatchPanic("crowdsec/serveCrowdsec")
go func() { go func() {

View file

@ -33,9 +33,6 @@ var (
apiTomb tomb.Tomb apiTomb tomb.Tomb
crowdsecTomb tomb.Tomb crowdsecTomb tomb.Tomb
disableAPI bool
disableAgent bool
flags *Flags flags *Flags
/*the state of acquisition*/ /*the state of acquisition*/
@ -112,7 +109,7 @@ func newParsers() *parser.Parsers {
return parsers return parsers
} }
func LoadBuckets(cConfig *csconfig.GlobalConfig) error { func LoadBuckets(cConfig *csconfig.Config) error {
var ( var (
err error err error
@ -140,7 +137,7 @@ func LoadBuckets(cConfig *csconfig.GlobalConfig) error {
return nil return nil
} }
func LoadAcquisition(cConfig *csconfig.GlobalConfig) error { func LoadAcquisition(cConfig *csconfig.Config) error {
var err error var err error
if flags.SingleFilePath != "" || flags.SingleJournalctlFilter != "" { if flags.SingleFilePath != "" || flags.SingleJournalctlFilter != "" {
@ -191,31 +188,25 @@ func (f *Flags) Parse() {
} }
// LoadConfig return configuration parsed from configuration file // LoadConfig return configuration parsed from configuration file
func LoadConfig(cConfig *csconfig.GlobalConfig) error { func LoadConfig(cConfig *csconfig.Config) error {
disableAPI = flags.DisableAPI
disableAgent = flags.DisableAgent if !flags.DisableAgent {
if flags.ConfigFile != "" { if err := cConfig.LoadCrowdsec(); err != nil {
if err := cConfig.LoadConfigurationFile(flags.ConfigFile, disableAPI, disableAgent); err != nil { return err
return fmt.Errorf("while loading configuration : %s", err)
} }
} else {
log.Warningf("no configuration file provided")
}
if !disableAPI && (cConfig.API == nil || cConfig.API.Server == nil) {
log.Errorf("no API server configuration found, will not start the local API")
disableAPI = true
} }
if !disableAgent && cConfig.Crowdsec == nil { if !flags.DisableAPI {
log.Errorf("no configuration found crowdsec agent, will not start the agent") if err := cConfig.LoadAPIServer(); err != nil {
disableAgent = true return err
}
} }
if !disableAgent && (cConfig.API == nil || cConfig.API.Client == nil || cConfig.API.Client.Credentials == nil) { if !cConfig.DisableAgent && (cConfig.API == nil || cConfig.API.Client == nil || cConfig.API.Client.Credentials == nil) {
log.Fatalf("missing local API credentials for crowdsec agent, abort") log.Fatalf("missing local API credentials for crowdsec agent, abort")
} }
if disableAPI && disableAgent { if cConfig.DisableAPI && cConfig.DisableAgent {
log.Fatalf("You must run at least the API Server or crowdsec") log.Fatalf("You must run at least the API Server or crowdsec")
} }
@ -244,14 +235,14 @@ func LoadConfig(cConfig *csconfig.GlobalConfig) error {
cConfig.Common.LogLevel = &logLevel cConfig.Common.LogLevel = &logLevel
} }
if flags.TestMode && !disableAgent { if flags.TestMode && !cConfig.DisableAgent {
cConfig.Crowdsec.LintOnly = true cConfig.Crowdsec.LintOnly = true
} }
if flags.SingleFilePath != "" || flags.SingleJournalctlFilter != "" { if flags.SingleFilePath != "" || flags.SingleJournalctlFilter != "" {
cConfig.API.Server.OnlineClient = nil cConfig.API.Server.OnlineClient = nil
/*if the api is disabled as well, just read file and exit, don't daemonize*/ /*if the api is disabled as well, just read file and exit, don't daemonize*/
if disableAPI { if flags.DisableAPI {
cConfig.Common.Daemonize = false cConfig.Common.Daemonize = false
} }
cConfig.Common.LogMedia = "stdout" cConfig.Common.LogMedia = "stdout"
@ -263,13 +254,12 @@ func LoadConfig(cConfig *csconfig.GlobalConfig) error {
func main() { func main() {
var ( var (
cConfig *csconfig.GlobalConfig cConfig *csconfig.Config
err error err error
) )
defer types.CatchPanic("crowdsec/main") defer types.CatchPanic("crowdsec/main")
cConfig = csconfig.NewConfig()
// Handle command line arguments // Handle command line arguments
flags = &Flags{} flags = &Flags{}
flags.Parse() flags.Parse()
@ -278,6 +268,10 @@ func main() {
os.Exit(0) os.Exit(0)
} }
cConfig, err = csconfig.NewConfig(flags.ConfigFile, flags.DisableAgent, flags.DisableAPI)
if err != nil {
log.Fatalf(err.Error())
}
if err := LoadConfig(cConfig); err != nil { if err := LoadConfig(cConfig); err != nil {
log.Fatalf(err.Error()) log.Fatalf(err.Error())
} }
@ -288,19 +282,6 @@ func main() {
log.Infof("Crowdsec %s", cwversion.VersionStr()) log.Infof("Crowdsec %s", cwversion.VersionStr())
if !flags.DisableAPI && (cConfig.API == nil || cConfig.API.Server == nil) {
log.Errorf("no API server configuration found, will not start the local API")
flags.DisableAPI = true
}
if !flags.DisableAgent && cConfig.Crowdsec == nil {
log.Errorf("no configuration found crowdsec agent, will not start the agent")
flags.DisableAgent = true
}
if !flags.DisableAgent && (cConfig.API == nil || cConfig.API.Client == nil || cConfig.API.Client.Credentials == nil) {
log.Fatalf("missing local API credentials for crowdsec agent, abort")
}
// Enable profiling early // Enable profiling early
if cConfig.Prometheus != nil { if cConfig.Prometheus != nil {
go registerPrometheus(cConfig.Prometheus) go registerPrometheus(cConfig.Prometheus)

View file

@ -10,7 +10,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func runPour(input chan types.Event, holders []leaky.BucketFactory, buckets *leaky.Buckets, cConfig *csconfig.GlobalConfig) error { func runPour(input chan types.Event, holders []leaky.BucketFactory, buckets *leaky.Buckets, cConfig *csconfig.Config) error {
var ( var (
count int count int
) )

View file

@ -19,7 +19,7 @@ import (
) )
//debugHandler is kept as a dev convenience : it shuts down and serialize internal state //debugHandler is kept as a dev convenience : it shuts down and serialize internal state
func debugHandler(sig os.Signal, cConfig *csconfig.GlobalConfig) error { func debugHandler(sig os.Signal, cConfig *csconfig.Config) error {
var tmpFile string var tmpFile string
var err error var err error
//stop go routines //stop go routines
@ -37,12 +37,12 @@ func debugHandler(sig os.Signal, cConfig *csconfig.GlobalConfig) error {
return nil return nil
} }
func reloadHandler(sig os.Signal, cConfig *csconfig.GlobalConfig) error { func reloadHandler(sig os.Signal, cConfig *csconfig.Config) error {
var tmpFile string var tmpFile string
var err error var err error
//stop go routines //stop go routines
if !disableAgent { if !cConfig.DisableAgent {
if err := shutdownCrowdsec(); err != nil { if err := shutdownCrowdsec(); err != nil {
log.Fatalf("Failed to shut down crowdsec routines: %s", err) log.Fatalf("Failed to shut down crowdsec routines: %s", err)
} }
@ -57,7 +57,7 @@ func reloadHandler(sig os.Signal, cConfig *csconfig.GlobalConfig) error {
} }
} }
if !disableAPI { if !cConfig.DisableAPI {
if err := shutdownAPI(); err != nil { if err := shutdownAPI(); err != nil {
log.Fatalf("Failed to shut down api routines: %s", err) log.Fatalf("Failed to shut down api routines: %s", err)
} }
@ -81,7 +81,7 @@ func reloadHandler(sig os.Signal, cConfig *csconfig.GlobalConfig) error {
log.Fatal(err.Error()) log.Fatal(err.Error())
} }
if !disableAPI { if !cConfig.DisableAPI {
apiServer, err := initAPIServer(cConfig) apiServer, err := initAPIServer(cConfig)
if err != nil { if err != nil {
return fmt.Errorf("unable to init api server: %s", err) return fmt.Errorf("unable to init api server: %s", err)
@ -90,7 +90,7 @@ func reloadHandler(sig os.Signal, cConfig *csconfig.GlobalConfig) error {
serveAPIServer(apiServer) serveAPIServer(apiServer)
} }
if !disableAgent { if !cConfig.DisableAgent {
csParsers, err := initCrowdsec(cConfig) csParsers, err := initCrowdsec(cConfig)
if err != nil { if err != nil {
return fmt.Errorf("unable to init crowdsec: %s", err) return fmt.Errorf("unable to init crowdsec: %s", err)
@ -186,7 +186,7 @@ func termHandler(sig os.Signal) error {
return nil return nil
} }
func HandleSignals(cConfig *csconfig.GlobalConfig) { func HandleSignals(cConfig *csconfig.Config) {
signalChan := make(chan os.Signal, 1) signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, signal.Notify(signalChan,
syscall.SIGHUP, syscall.SIGHUP,
@ -220,7 +220,7 @@ func HandleSignals(cConfig *csconfig.GlobalConfig) {
os.Exit(code) os.Exit(code)
} }
func Serve(cConfig *csconfig.GlobalConfig) error { func Serve(cConfig *csconfig.Config) error {
acquisTomb = tomb.Tomb{} acquisTomb = tomb.Tomb{}
parsersTomb = tomb.Tomb{} parsersTomb = tomb.Tomb{}
bucketsTomb = tomb.Tomb{} bucketsTomb = tomb.Tomb{}
@ -228,7 +228,7 @@ func Serve(cConfig *csconfig.GlobalConfig) error {
apiTomb = tomb.Tomb{} apiTomb = tomb.Tomb{}
crowdsecTomb = tomb.Tomb{} crowdsecTomb = tomb.Tomb{}
if !disableAPI { if !cConfig.DisableAPI {
apiServer, err := initAPIServer(cConfig) apiServer, err := initAPIServer(cConfig)
if err != nil { if err != nil {
return errors.Wrap(err, "api server init") return errors.Wrap(err, "api server init")
@ -238,7 +238,7 @@ func Serve(cConfig *csconfig.GlobalConfig) error {
} }
} }
if !disableAgent { if !cConfig.DisableAgent {
csParsers, err := initCrowdsec(cConfig) csParsers, err := initCrowdsec(cConfig)
if err != nil { if err != nil {
return errors.Wrap(err, "crowdsec init") return errors.Wrap(err, "crowdsec init")

View file

@ -31,8 +31,8 @@ var MachineTest = models.WatcherAuthRequest{
var UserAgent = fmt.Sprintf("crowdsec-test/%s", cwversion.Version) var UserAgent = fmt.Sprintf("crowdsec-test/%s", cwversion.Version)
func LoadTestConfig() csconfig.GlobalConfig { func LoadTestConfig() csconfig.Config {
config := csconfig.GlobalConfig{} config := csconfig.Config{}
maxAge := "1h" maxAge := "1h"
flushConfig := csconfig.FlushDBCfg{ flushConfig := csconfig.FlushDBCfg{
MaxAge: &maxAge, MaxAge: &maxAge,
@ -57,8 +57,8 @@ func LoadTestConfig() csconfig.GlobalConfig {
return config return config
} }
func LoadTestConfigForwardedFor() csconfig.GlobalConfig { func LoadTestConfigForwardedFor() csconfig.Config {
config := csconfig.GlobalConfig{} config := csconfig.Config{}
maxAge := "1h" maxAge := "1h"
flushConfig := csconfig.FlushDBCfg{ flushConfig := csconfig.FlushDBCfg{
MaxAge: &maxAge, MaxAge: &maxAge,

View file

@ -1,6 +1,15 @@
package csconfig package csconfig
import log "github.com/sirupsen/logrus" import (
"fmt"
"io/ioutil"
"strings"
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)
type APICfg struct { type APICfg struct {
Client *LocalApiClientCfg `yaml:"client"` Client *LocalApiClientCfg `yaml:"client"`
@ -26,6 +35,47 @@ type LocalApiClientCfg struct {
InsecureSkipVerify *bool `yaml:"insecure_skip_verify"` // check if api certificate is bad or not InsecureSkipVerify *bool `yaml:"insecure_skip_verify"` // check if api certificate is bad or not
} }
func (o *OnlineApiClientCfg) Load() error {
o.Credentials = new(ApiCredentialsCfg)
fcontent, err := ioutil.ReadFile(o.CredentialsFilePath)
if err != nil {
return errors.Wrapf(err, "failed to read api server credentials configuration file '%s'", o.CredentialsFilePath)
}
err = yaml.UnmarshalStrict(fcontent, o.Credentials)
if err != nil {
return errors.Wrapf(err, "failed unmarshaling api server credentials configuration file '%s'", o.CredentialsFilePath)
}
if o.Credentials.Login == "" || o.Credentials.Password == "" || o.Credentials.URL == "" {
log.Warningf("can't load CAPI credentials from '%s' (missing field)", o.CredentialsFilePath)
o.Credentials = nil
}
return nil
}
func (l *LocalApiClientCfg) Load() error {
fcontent, err := ioutil.ReadFile(l.CredentialsFilePath)
if err != nil {
return errors.Wrapf(err, "failed to read api client credential configuration file '%s'", l.CredentialsFilePath)
}
err = yaml.UnmarshalStrict(fcontent, &l.Credentials)
if err != nil {
return errors.Wrapf(err, "failed unmarshaling api client credential configuration file '%s'", l.CredentialsFilePath)
}
if l.Credentials != nil && l.Credentials.URL != "" {
if !strings.HasSuffix(l.Credentials.URL, "/") {
l.Credentials.URL = l.Credentials.URL + "/"
}
} else {
log.Warningf("no credentials or URL found in api client configuration '%s'", l.CredentialsFilePath)
}
if l.InsecureSkipVerify == nil {
apiclient.InsecureSkipVerify = false
} else {
apiclient.InsecureSkipVerify = *l.InsecureSkipVerify
}
return nil
}
/*local api service configuration*/ /*local api service configuration*/
type LocalApiServerCfg struct { type LocalApiServerCfg struct {
ListenURI string `yaml:"listen_uri,omitempty"` //127.0.0.1:8080 ListenURI string `yaml:"listen_uri,omitempty"` //127.0.0.1:8080
@ -44,3 +94,44 @@ type TLSCfg struct {
CertFilePath string `yaml:"cert_file"` CertFilePath string `yaml:"cert_file"`
KeyFilePath string `yaml:"key_file"` KeyFilePath string `yaml:"key_file"`
} }
func (c *Config) LoadAPIServer() error {
if c.API.Server != nil && !c.DisableAPI {
if err := c.LoadCommon(); err != nil {
return fmt.Errorf("loading common configuration: %s", err.Error())
}
c.API.Server.LogDir = c.Common.LogDir
c.API.Server.LogMedia = c.Common.LogMedia
if err := c.API.Server.LoadProfiles(); err != nil {
return errors.Wrap(err, "while loading profiles for LAPI")
}
if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
if err := c.API.Server.OnlineClient.Load(); err != nil {
return errors.Wrap(err, "loading online client credentials")
}
}
if c.API.Server.OnlineClient == nil || c.API.Server.OnlineClient.Credentials == nil {
log.Printf("push and pull to crowdsec API disabled")
}
if err := c.LoadDBConfig(); err != nil {
return err
}
} else {
log.Warningf("crowdsec local API is disabled")
c.DisableAPI = true
}
return nil
}
func (c *Config) LoadAPIClient() error {
if c.API != nil && c.API.Client != nil && c.API.Client.CredentialsFilePath != "" && !c.DisableAgent {
if err := c.API.Client.Load(); err != nil {
return err
}
} else {
return fmt.Errorf("no API client section in configuration")
}
return nil
}

268
pkg/csconfig/api_test.go Normal file
View file

@ -0,0 +1,268 @@
package csconfig
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
)
func TestLoadLocalApiClientCfg(t *testing.T) {
True := true
tests := []struct {
name string
Input *LocalApiClientCfg
expectedResult *ApiCredentialsCfg
err string
}{
{
name: "basic valid configuration",
Input: &LocalApiClientCfg{
CredentialsFilePath: "./tests/lapi-secrets.yaml",
},
expectedResult: &ApiCredentialsCfg{
URL: "http://localhost:8080/",
Login: "test",
Password: "testpassword",
},
},
{
name: "invalid configuration",
Input: &LocalApiClientCfg{
CredentialsFilePath: "./tests/bad_lapi-secrets.yaml",
},
expectedResult: &ApiCredentialsCfg{},
},
{
name: "invalid configuration filepath",
Input: &LocalApiClientCfg{
CredentialsFilePath: "./tests/nonexist_lapi-secrets.yaml",
},
expectedResult: nil,
},
{
name: "valid configuration with insecure skip verify",
Input: &LocalApiClientCfg{
CredentialsFilePath: "./tests/lapi-secrets.yaml",
InsecureSkipVerify: &True,
},
expectedResult: &ApiCredentialsCfg{
URL: "http://localhost:8080/",
Login: "test",
Password: "testpassword",
},
},
}
for idx, test := range tests {
fmt.Printf("TEST '%s'\n", test.name)
err := test.Input.Load()
if err == nil && test.err != "" {
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Credentials)
if !isOk {
t.Fatalf("test '%s' failed", test.name)
}
}
}
func TestLoadOnlineApiClientCfg(t *testing.T) {
tests := []struct {
name string
Input *OnlineApiClientCfg
expectedResult *ApiCredentialsCfg
err string
}{
{
name: "basic valid configuration",
Input: &OnlineApiClientCfg{
CredentialsFilePath: "./tests/online-api-secrets.yaml",
},
expectedResult: &ApiCredentialsCfg{
URL: "http://crowdsec.api",
Login: "test",
Password: "testpassword",
},
},
{
name: "invalid configuration",
Input: &OnlineApiClientCfg{
CredentialsFilePath: "./tests/bad_lapi-secrets.yaml",
},
expectedResult: &ApiCredentialsCfg{},
err: "failed unmarshaling api server credentials",
},
{
name: "missing field configuration",
Input: &OnlineApiClientCfg{
CredentialsFilePath: "./tests/bad_online-api-secrets.yaml",
},
expectedResult: nil,
},
{
name: "invalid configuration filepath",
Input: &OnlineApiClientCfg{
CredentialsFilePath: "./tests/nonexist_online-api-secrets.yaml",
},
expectedResult: &ApiCredentialsCfg{},
err: "failed to read api server credentials",
},
}
for idx, test := range tests {
err := test.Input.Load()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Credentials)
if !isOk {
t.Fatalf("TEST '%s': NOK", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}
func TestLoadAPIServer(t *testing.T) {
tmpLAPI := &LocalApiServerCfg{
ProfilesPath: "./tests/profiles.yaml",
}
if err := tmpLAPI.LoadProfiles(); err != nil {
t.Fatalf("loading tmp profiles: %+v", err)
}
LogDirFullPath, err := filepath.Abs("./tests")
if err != nil {
t.Fatalf(err.Error())
}
config := &Config{}
fcontent, err := ioutil.ReadFile("./tests/config.yaml")
if err != nil {
t.Fatalf(err.Error())
}
configData := os.ExpandEnv(string(fcontent))
err = yaml.UnmarshalStrict([]byte(configData), &config)
if err != nil {
t.Fatalf(err.Error())
}
tests := []struct {
name string
Input *Config
expectedResult *LocalApiServerCfg
err string
}{
{
name: "basic valid configuration",
Input: &Config{
Self: []byte(configData),
API: &APICfg{
Server: &LocalApiServerCfg{
ListenURI: "http://crowdsec.api",
OnlineClient: &OnlineApiClientCfg{
CredentialsFilePath: "./tests/online-api-secrets.yaml",
},
ProfilesPath: "./tests/profiles.yaml",
},
},
DbConfig: &DatabaseCfg{
Type: "sqlite",
DbPath: "./tests/test.db",
},
Common: &CommonCfg{
LogDir: "./tests/",
LogMedia: "stdout",
},
DisableAPI: false,
},
expectedResult: &LocalApiServerCfg{
ListenURI: "http://crowdsec.api",
TLS: nil,
DbConfig: &DatabaseCfg{
DbPath: "./tests/test.db",
Type: "sqlite",
},
LogDir: LogDirFullPath,
LogMedia: "stdout",
OnlineClient: &OnlineApiClientCfg{
CredentialsFilePath: "./tests/online-api-secrets.yaml",
Credentials: &ApiCredentialsCfg{
URL: "http://crowdsec.api",
Login: "test",
Password: "testpassword",
},
},
Profiles: tmpLAPI.Profiles,
ProfilesPath: "./tests/profiles.yaml",
UseForwardedForHeaders: false,
},
err: "",
},
{
name: "basic valid configuration",
Input: &Config{
Self: []byte(configData),
API: &APICfg{
Server: &LocalApiServerCfg{},
},
Common: &CommonCfg{
LogDir: "./tests/",
LogMedia: "stdout",
},
DisableAPI: false,
},
expectedResult: &LocalApiServerCfg{
LogDir: LogDirFullPath,
LogMedia: "stdout",
},
err: "while loading profiles for LAPI",
},
}
for idx, test := range tests {
err := test.Input.LoadAPIServer()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.API.Server)
if !isOk {
t.Fatalf("TEST '%s': NOK", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}

View file

@ -1,6 +1,12 @@
package csconfig package csconfig
import log "github.com/sirupsen/logrus" import (
"fmt"
"path/filepath"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
/*daemonization/service related stuff*/ /*daemonization/service related stuff*/
type CommonCfg struct { type CommonCfg struct {
@ -11,3 +17,27 @@ type CommonCfg struct {
LogLevel *log.Level `yaml:"log_level"` LogLevel *log.Level `yaml:"log_level"`
WorkingDir string `yaml:"working_dir,omitempty"` ///var/run WorkingDir string `yaml:"working_dir,omitempty"` ///var/run
} }
func (c *Config) LoadCommon() error {
var err error
if c.Common == nil {
return fmt.Errorf("no common block provided in configuration file")
}
var CommonCleanup = []*string{
&c.Common.PidDir,
&c.Common.LogDir,
&c.Common.WorkingDir,
}
for _, k := range CommonCleanup {
if *k == "" {
continue
}
*k, err = filepath.Abs(*k)
if err != nil {
return errors.Wrapf(err, "failed to get absolute path of '%s'", *k)
}
}
return nil
}

View file

@ -0,0 +1,98 @@
package csconfig
import (
"fmt"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadCommon(t *testing.T) {
PidDirFullPath, err := filepath.Abs("./tests/")
if err != nil {
t.Fatalf(err.Error())
}
LogDirFullPath, err := filepath.Abs("./tests/log/")
if err != nil {
t.Fatalf(err.Error())
}
WorkingDirFullPath, err := filepath.Abs("./tests")
if err != nil {
t.Fatalf(err.Error())
}
tests := []struct {
name string
Input *Config
expectedResult *CommonCfg
err string
}{
{
name: "basic valid configuration",
Input: &Config{
Common: &CommonCfg{
Daemonize: true,
PidDir: "./tests",
LogMedia: "file",
LogDir: "./tests/log/",
WorkingDir: "./tests/",
},
},
expectedResult: &CommonCfg{
Daemonize: true,
PidDir: PidDirFullPath,
LogMedia: "file",
LogDir: LogDirFullPath,
WorkingDir: WorkingDirFullPath,
},
},
{
name: "empty working dir",
Input: &Config{
Common: &CommonCfg{
Daemonize: true,
PidDir: "./tests",
LogMedia: "file",
LogDir: "./tests/log/",
},
},
expectedResult: &CommonCfg{
Daemonize: true,
PidDir: PidDirFullPath,
LogMedia: "file",
LogDir: LogDirFullPath,
},
},
{
name: "no common",
Input: &Config{},
expectedResult: nil,
},
}
for idx, test := range tests {
err := test.Input.LoadCommon()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Common)
if !isOk {
t.Fatalf("TEST '%s': NOK", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}

View file

@ -4,19 +4,17 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings"
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
/*top-level config : defaults,overriden by cfg file,overriden by cli*/ /*top-level config : defaults,overriden by cfg file,overriden by cli*/
type GlobalConfig struct { type Config struct {
//just a path to ourself :p //just a path to ourself :p
Self *string `yaml:"-"` FilePath *string `yaml:"-"`
Self []byte `yaml:"-"`
Common *CommonCfg `yaml:"common,omitempty"` Common *CommonCfg `yaml:"common,omitempty"`
Prometheus *PrometheusCfg `yaml:"prometheus,omitempty"` Prometheus *PrometheusCfg `yaml:"prometheus,omitempty"`
Crowdsec *CrowdsecServiceCfg `yaml:"crowdsec_service,omitempty"` Crowdsec *CrowdsecServiceCfg `yaml:"crowdsec_service,omitempty"`
@ -26,9 +24,10 @@ type GlobalConfig struct {
ConfigPaths *ConfigurationPaths `yaml:"config_paths,omitempty"` ConfigPaths *ConfigurationPaths `yaml:"config_paths,omitempty"`
DisableAPI bool `yaml:"-"` DisableAPI bool `yaml:"-"`
DisableAgent bool `yaml:"-"` DisableAgent bool `yaml:"-"`
Hub *Hub `yaml:"-"`
} }
func (c *GlobalConfig) Dump() error { func (c *Config) Dump() error {
out, err := yaml.Marshal(c) out, err := yaml.Marshal(c)
if err != nil { if err != nil {
return errors.Wrap(err, "failed marshaling config") return errors.Wrap(err, "failed marshaling config")
@ -37,192 +36,26 @@ func (c *GlobalConfig) Dump() error {
return nil return nil
} }
func (c *GlobalConfig) LoadConfigurationFile(path string, disableAPI bool, disableAgent bool) error { func NewConfig(configFile string, disableAgent bool, disableAPI bool) (*Config, error) {
c.DisableAPI = disableAPI fcontent, err := ioutil.ReadFile(configFile)
c.DisableAgent = disableAgent
fcontent, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to read config file") return nil, errors.Wrap(err, "failed to read config file")
} }
configData := os.ExpandEnv(string(fcontent)) configData := os.ExpandEnv(string(fcontent))
err = yaml.UnmarshalStrict([]byte(configData), c) cfg := Config{
FilePath: &configFile,
DisableAgent: disableAgent,
DisableAPI: disableAPI,
}
err = yaml.UnmarshalStrict([]byte(configData), &cfg)
if err != nil { if err != nil {
return errors.Wrap(err, "failed unmarshaling config") return nil, err
} }
path, err = filepath.Abs(path) return &cfg, nil
if err != nil {
return errors.Wrap(err, "failed to load absolute path")
}
c.Self = &path
if err := c.LoadConfiguration(); err != nil {
return errors.Wrap(err, "failed to load sub configurations")
}
return nil
} }
func (c *GlobalConfig) LoadConfiguration() error { func NewDefaultConfig() *Config {
if c.ConfigPaths.ConfigDir == "" {
return fmt.Errorf("please provide a configuration directory with the 'config_dir' directive in the 'config_paths' section")
}
if c.ConfigPaths.DataDir == "" {
return fmt.Errorf("please provide a data directory with the 'data_dir' directive in the 'config_paths' section")
}
if c.ConfigPaths.HubDir == "" {
c.ConfigPaths.HubDir = filepath.Clean(c.ConfigPaths.ConfigDir + "/hub")
}
if c.ConfigPaths.HubIndexFile == "" {
c.ConfigPaths.HubIndexFile = filepath.Clean(c.ConfigPaths.HubDir + "/.index.json")
}
if err := c.LoadSimulation(); err != nil {
return err
}
if c.Crowdsec != nil {
if c.Crowdsec.AcquisitionFilePath != "" {
log.Debugf("non-empty acquisition file path %s", c.Crowdsec.AcquisitionFilePath)
if _, err := os.Stat(c.Crowdsec.AcquisitionFilePath); err != nil {
return errors.Wrapf(err, "while checking acquisition path %s", c.Crowdsec.AcquisitionFilePath)
}
c.Crowdsec.AcquisitionFiles = append(c.Crowdsec.AcquisitionFiles, c.Crowdsec.AcquisitionFilePath)
}
if c.Crowdsec.AcquisitionDirPath != "" {
files, err := filepath.Glob(c.Crowdsec.AcquisitionDirPath + "/*.yaml")
c.Crowdsec.AcquisitionFiles = append(c.Crowdsec.AcquisitionFiles, files...)
if err != nil {
return errors.Wrap(err, "while globing acquis_dir")
}
}
if c.Crowdsec.AcquisitionDirPath == "" && c.Crowdsec.AcquisitionFilePath == "" {
return fmt.Errorf("no acquisition_path nor acquisition_dir")
}
c.Crowdsec.ConfigDir = c.ConfigPaths.ConfigDir
c.Crowdsec.DataDir = c.ConfigPaths.DataDir
c.Crowdsec.HubDir = c.ConfigPaths.HubDir
c.Crowdsec.HubIndexFile = c.ConfigPaths.HubIndexFile
if c.Crowdsec.ParserRoutinesCount <= 0 {
c.Crowdsec.ParserRoutinesCount = 1
}
if c.Crowdsec.BucketsRoutinesCount <= 0 {
c.Crowdsec.BucketsRoutinesCount = 1
}
if c.Crowdsec.OutputRoutinesCount <= 0 {
c.Crowdsec.OutputRoutinesCount = 1
}
}
if err := c.CleanupPaths(); err != nil {
return errors.Wrap(err, "invalid config")
}
if c.Cscli != nil {
c.Cscli.DbConfig = c.DbConfig
c.Cscli.ConfigDir = c.ConfigPaths.ConfigDir
c.Cscli.DataDir = c.ConfigPaths.DataDir
c.Cscli.HubDir = c.ConfigPaths.HubDir
c.Cscli.HubIndexFile = c.ConfigPaths.HubIndexFile
if c.Cscli.PrometheusUrl == "" {
port := 6060
if c.Prometheus.ListenPort != 0 {
port = c.Prometheus.ListenPort
}
c.Cscli.PrometheusUrl = fmt.Sprintf("http://127.0.0.1:%d/", port)
}
}
if c.API.Client != nil && c.API.Client.CredentialsFilePath != "" && !c.DisableAgent {
fcontent, err := ioutil.ReadFile(c.API.Client.CredentialsFilePath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to read api client credential configuration file '%s'", c.API.Client.CredentialsFilePath))
}
err = yaml.UnmarshalStrict(fcontent, &c.API.Client.Credentials)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed unmarshaling api client credential configuration file '%s'", c.API.Client.CredentialsFilePath))
}
if c.API.Client.Credentials != nil && c.API.Client.Credentials.URL != "" {
if !strings.HasSuffix(c.API.Client.Credentials.URL, "/") {
c.API.Client.Credentials.URL = c.API.Client.Credentials.URL + "/"
}
}
if c.API.Client.InsecureSkipVerify == nil {
apiclient.InsecureSkipVerify = false
} else {
apiclient.InsecureSkipVerify = *c.API.Client.InsecureSkipVerify
}
}
if c.API.Server != nil && !c.DisableAPI {
c.API.Server.DbConfig = c.DbConfig
c.API.Server.LogDir = c.Common.LogDir
c.API.Server.LogMedia = c.Common.LogMedia
if err := c.API.Server.LoadProfiles(); err != nil {
return errors.Wrap(err, "while loading profiles for LAPI")
}
if c.API.Server.OnlineClient != nil && c.API.Server.OnlineClient.CredentialsFilePath != "" {
c.API.Server.OnlineClient.Credentials = new(ApiCredentialsCfg)
fcontent, err := ioutil.ReadFile(c.API.Server.OnlineClient.CredentialsFilePath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to read api server credentials configuration file '%s'", c.API.Server.OnlineClient.CredentialsFilePath))
}
err = yaml.UnmarshalStrict(fcontent, c.API.Server.OnlineClient.Credentials)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed unmarshaling api server credentials configuration file '%s'", c.API.Server.OnlineClient.CredentialsFilePath))
}
if c.API.Server.OnlineClient.Credentials.Login == "" || c.API.Server.OnlineClient.Credentials.Password == "" || c.API.Server.OnlineClient.Credentials.URL == "" {
log.Debugf("can't load CAPI credentials from '%s' (missing field)", c.API.Server.OnlineClient.CredentialsFilePath)
c.API.Server.OnlineClient.Credentials = nil
}
}
if c.API.Server.OnlineClient == nil || c.API.Server.OnlineClient.Credentials == nil {
log.Printf("push and pull to crowdsec API disabled")
}
}
return nil
}
func (c *GlobalConfig) LoadSimulation() error {
if c.ConfigPaths == nil {
return fmt.Errorf("ConfigPaths is empty")
}
simCfg := SimulationConfig{}
if c.ConfigPaths.SimulationFilePath == "" {
c.ConfigPaths.SimulationFilePath = filepath.Clean(c.ConfigPaths.ConfigDir + "/simulation.yaml")
}
rcfg, err := ioutil.ReadFile(c.ConfigPaths.SimulationFilePath)
if err != nil {
return errors.Wrapf(err, "while reading '%s'", c.ConfigPaths.SimulationFilePath)
} else {
if err := yaml.UnmarshalStrict(rcfg, &simCfg); err != nil {
return fmt.Errorf("while unmarshaling simulation file '%s' : %s", c.ConfigPaths.SimulationFilePath, err)
}
}
if simCfg.Simulation == nil {
simCfg.Simulation = new(bool)
}
if c.Crowdsec != nil {
c.Crowdsec.SimulationConfig = &simCfg
}
if c.Cscli != nil {
c.Cscli.SimulationConfig = &simCfg
}
return nil
}
func NewConfig() *GlobalConfig {
cfg := GlobalConfig{}
return &cfg
}
func NewDefaultConfig() *GlobalConfig {
logLevel := log.InfoLevel logLevel := log.InfoLevel
CommonCfg := CommonCfg{ CommonCfg := CommonCfg{
Daemonize: false, Daemonize: false,
@ -270,7 +103,7 @@ func NewDefaultConfig() *GlobalConfig {
DbPath: "/var/lib/crowdsec/data/crowdsec.db", DbPath: "/var/lib/crowdsec/data/crowdsec.db",
} }
globalCfg := GlobalConfig{ globalCfg := Config{
Common: &CommonCfg, Common: &CommonCfg,
Prometheus: &prometheus, Prometheus: &prometheus,
Crowdsec: &crowdsecCfg, Crowdsec: &crowdsecCfg,
@ -282,60 +115,3 @@ func NewDefaultConfig() *GlobalConfig {
return &globalCfg return &globalCfg
} }
func (c *GlobalConfig) CleanupPaths() error {
var err error
if c.Common != nil {
var CommonCleanup = []*string{
&c.Common.PidDir,
&c.Common.LogDir,
&c.Common.WorkingDir,
}
for _, k := range CommonCleanup {
if *k == "" {
continue
}
*k, err = filepath.Abs(*k)
if err != nil {
return errors.Wrap(err, "failed to clean path")
}
}
}
if c.Crowdsec != nil {
var crowdsecCleanup = []*string{
&c.Crowdsec.AcquisitionFilePath,
}
for _, k := range crowdsecCleanup {
if *k == "" {
continue
}
*k, err = filepath.Abs(*k)
if err != nil {
return errors.Wrap(err, "failed to clean path")
}
}
}
if c.ConfigPaths != nil {
var configPathsCleanup = []*string{
&c.ConfigPaths.HubDir,
&c.ConfigPaths.HubIndexFile,
&c.ConfigPaths.ConfigDir,
&c.ConfigPaths.DataDir,
&c.ConfigPaths.SimulationFilePath,
}
for _, k := range configPathsCleanup {
if *k == "" {
continue
}
*k, err = filepath.Abs(*k)
if err != nil {
return errors.Wrap(err, "failed to clean path")
}
}
}
return nil
}

View file

@ -1,5 +1,12 @@
package csconfig package csconfig
import (
"fmt"
"path/filepath"
"github.com/pkg/errors"
)
type ConfigurationPaths struct { type ConfigurationPaths struct {
ConfigDir string `yaml:"config_dir"` ConfigDir string `yaml:"config_dir"`
DataDir string `yaml:"data_dir,omitempty"` DataDir string `yaml:"data_dir,omitempty"`
@ -7,3 +14,41 @@ type ConfigurationPaths struct {
HubIndexFile string `yaml:"index_path,omitempty"` //path of the .index.json HubIndexFile string `yaml:"index_path,omitempty"` //path of the .index.json
HubDir string `yaml:"hub_dir,omitempty"` HubDir string `yaml:"hub_dir,omitempty"`
} }
func (c *Config) LoadConfigurationPaths() error {
var err error
if c.ConfigPaths == nil {
return fmt.Errorf("no configuration paths provided")
}
if c.ConfigPaths.DataDir == "" {
return fmt.Errorf("please provide a data directory with the 'data_dir' directive in the 'config_paths' section")
}
if c.ConfigPaths.HubDir == "" {
c.ConfigPaths.HubDir = filepath.Clean(c.ConfigPaths.ConfigDir + "/hub")
}
if c.ConfigPaths.HubIndexFile == "" {
c.ConfigPaths.HubIndexFile = filepath.Clean(c.ConfigPaths.HubDir + "/.index.json")
}
var configPathsCleanup = []*string{
&c.ConfigPaths.HubDir,
&c.ConfigPaths.HubIndexFile,
&c.ConfigPaths.ConfigDir,
&c.ConfigPaths.DataDir,
&c.ConfigPaths.SimulationFilePath,
}
for _, k := range configPathsCleanup {
if *k == "" {
continue
}
*k, err = filepath.Abs(*k)
if err != nil {
return errors.Wrapf(err, "failed to get absolute path of '%s'", *k)
}
}
return nil
}

View file

@ -2,203 +2,59 @@ package csconfig
import ( import (
"fmt" "fmt"
"log"
"strings" "strings"
"testing" "testing"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestDefaultConfig(t *testing.T) {
x := NewDefaultConfig()
x.Dump()
}
func TestNormalLoad(t *testing.T) { func TestNormalLoad(t *testing.T) {
x := NewConfig() _, err := NewConfig("./tests/config.yaml", false, false)
err := x.LoadConfigurationFile("./tests/config.yaml", false, false)
if err != nil { if err != nil {
t.Fatalf("unexpected error %s", err) t.Fatalf("unexpected error %s", err)
} }
x = NewConfig() _, err = NewConfig("./tests/xxx.yaml", false, false)
err = x.LoadConfigurationFile("./tests/xxx.yaml", false, false)
if fmt.Sprintf("%s", err) != "failed to read config file: open ./tests/xxx.yaml: no such file or directory" { if fmt.Sprintf("%s", err) != "failed to read config file: open ./tests/xxx.yaml: no such file or directory" {
t.Fatalf("unexpected error %s", err) t.Fatalf("unexpected error %s", err)
} }
x = NewConfig() _, err = NewConfig("./tests/simulation.yaml", false, false)
err = x.LoadConfigurationFile("./tests/simulation.yaml", false, false) if !strings.HasPrefix(fmt.Sprintf("%s", err), "yaml: unmarshal errors:") {
if !strings.HasPrefix(fmt.Sprintf("%s", err), "failed unmarshaling config: yaml: unmarshal error") {
t.Fatalf("unexpected error %s", err) t.Fatalf("unexpected error %s", err)
} }
} }
func TestCleanupPaths(t *testing.T) {
tests := []struct {
name string
Input *GlobalConfig
expectedResult *GlobalConfig
err string
}{
{
name: "daemon cleanup",
Input: &GlobalConfig{
Common: &CommonCfg{
PidDir: "////tmp//",
LogDir: "/////tmp///",
WorkingDir: "/////tmp///",
},
},
expectedResult: &GlobalConfig{
Common: &CommonCfg{
PidDir: "/tmp",
LogDir: "/tmp",
WorkingDir: "/tmp",
},
},
},
//
{
name: "crowdsec cleanup",
Input: &GlobalConfig{
Crowdsec: &CrowdsecServiceCfg{
AcquisitionFilePath: "////tmp//x.yaml",
},
},
expectedResult: &GlobalConfig{
Crowdsec: &CrowdsecServiceCfg{
AcquisitionFilePath: "/tmp/x.yaml",
},
},
},
//
{
name: "config paths cleanup",
Input: &GlobalConfig{
ConfigPaths: &ConfigurationPaths{
HubDir: "////tmp//",
HubIndexFile: "////tmp//x.yaml",
ConfigDir: "////tmp//",
DataDir: "////tmp//",
SimulationFilePath: "//tmp///toto.yaml",
},
},
expectedResult: &GlobalConfig{
ConfigPaths: &ConfigurationPaths{
HubDir: "/tmp",
HubIndexFile: "/tmp/x.yaml",
ConfigDir: "/tmp",
DataDir: "/tmp",
SimulationFilePath: "/tmp/toto.yaml",
},
},
},
}
for idx, test := range tests {
err := test.Input.CleanupPaths()
if test.err != "" {
if strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
t.Fatalf("%d/%d expected err %s got %s", idx, len(tests), test.err, fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input)
if !isOk {
t.Fatalf("%d/%d failed test", idx, len(tests))
}
}
}
func TestSimulationLoading(t *testing.T) {
tests := []struct {
name string
Input *GlobalConfig
expectedResult *SimulationConfig
err string
}{
{
name: "basic valid simulation",
Input: &GlobalConfig{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/simulation.yaml",
},
Crowdsec: &CrowdsecServiceCfg{},
},
expectedResult: &SimulationConfig{Simulation: new(bool)},
},
{
name: "basic bad file name",
Input: &GlobalConfig{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/xxx.yaml",
},
Crowdsec: &CrowdsecServiceCfg{},
},
err: "while reading './tests/xxx.yaml': open ./tests/xxx.yaml: no such file or directory",
},
{
name: "basic nil config",
Input: &GlobalConfig{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "",
},
Crowdsec: &CrowdsecServiceCfg{},
},
},
{
name: "basic bad file content",
Input: &GlobalConfig{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/config.yaml",
},
Crowdsec: &CrowdsecServiceCfg{},
},
err: "while unmarshaling simulation file './tests/config.yaml' : yaml: unmarshal errors",
},
}
for idx, test := range tests {
err := test.Input.LoadSimulation()
if err == nil && test.err != "" {
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Crowdsec.SimulationConfig)
if !isOk {
t.Fatalf("test '%s' failed", test.name)
}
}
}
func TestNewCrowdSecConfig(t *testing.T) { func TestNewCrowdSecConfig(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
expectedResult *GlobalConfig expectedResult *Config
err string err string
}{ }{
{ {
name: "new configuration: basic", name: "new configuration: basic",
expectedResult: &GlobalConfig{}, expectedResult: &Config{},
err: "", err: "",
}, },
} }
for _, test := range tests { for _, test := range tests {
result := NewConfig() result := &Config{}
isOk := assert.Equal(t, test.expectedResult, result) isOk := assert.Equal(t, test.expectedResult, result)
if !isOk { if !isOk {
t.Fatalf("test '%s' failed", test.name) t.Fatalf("TEST '%s': NOK", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
} }
log.Infof("test '%s' : OK", test.name)
} }
} }
func TestDefaultConfig(t *testing.T) {
x := NewDefaultConfig()
if err := x.Dump(); err != nil {
log.Fatal(err)
}
}

View file

@ -1,5 +1,14 @@
package csconfig package csconfig
import (
"fmt"
"os"
"path/filepath"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
/*Configurations needed for crowdsec to load parser/scenarios/... + acquisition*/ /*Configurations needed for crowdsec to load parser/scenarios/... + acquisition*/
type CrowdsecServiceCfg struct { type CrowdsecServiceCfg struct {
AcquisitionFilePath string `yaml:"acquisition_path,omitempty"` AcquisitionFilePath string `yaml:"acquisition_path,omitempty"`
@ -21,3 +30,82 @@ type CrowdsecServiceCfg struct {
HubIndexFile string `yaml:"-"` HubIndexFile string `yaml:"-"`
SimulationFilePath string `yaml:"-"` SimulationFilePath string `yaml:"-"`
} }
func (c *Config) LoadCrowdsec() error {
var err error
// Configuration paths are dependency to load crowdsec configuration
if err := c.LoadConfigurationPaths(); err != nil {
return err
}
if c.Crowdsec == nil {
log.Warningf("crowdsec agent is disabled")
c.DisableAgent = true
return nil
}
if c.Crowdsec.AcquisitionFilePath != "" {
log.Debugf("non-empty acquisition file path %s", c.Crowdsec.AcquisitionFilePath)
if _, err := os.Stat(c.Crowdsec.AcquisitionFilePath); err != nil {
return errors.Wrapf(err, "while checking acquisition path %s", c.Crowdsec.AcquisitionFilePath)
}
c.Crowdsec.AcquisitionFiles = append(c.Crowdsec.AcquisitionFiles, c.Crowdsec.AcquisitionFilePath)
}
if c.Crowdsec.AcquisitionDirPath != "" {
c.Crowdsec.AcquisitionDirPath, err = filepath.Abs(c.Crowdsec.AcquisitionDirPath)
if err != nil {
return errors.Wrapf(err, "can't get absolute path of '%s'", c.Crowdsec.AcquisitionDirPath)
}
files, err := filepath.Glob(c.Crowdsec.AcquisitionDirPath + "/*.yaml")
if err != nil {
return errors.Wrap(err, "while globing acquis_dir")
}
c.Crowdsec.AcquisitionFiles = append(c.Crowdsec.AcquisitionFiles, files...)
}
if c.Crowdsec.AcquisitionDirPath == "" && c.Crowdsec.AcquisitionFilePath == "" {
return fmt.Errorf("no acquisition_path nor acquisition_dir")
}
c.Crowdsec.ConfigDir = c.ConfigPaths.ConfigDir
c.Crowdsec.DataDir = c.ConfigPaths.DataDir
c.Crowdsec.HubDir = c.ConfigPaths.HubDir
c.Crowdsec.HubIndexFile = c.ConfigPaths.HubIndexFile
if c.Crowdsec.ParserRoutinesCount <= 0 {
c.Crowdsec.ParserRoutinesCount = 1
}
if c.Crowdsec.BucketsRoutinesCount <= 0 {
c.Crowdsec.BucketsRoutinesCount = 1
}
if c.Crowdsec.OutputRoutinesCount <= 0 {
c.Crowdsec.OutputRoutinesCount = 1
}
var crowdsecCleanup = []*string{
&c.Crowdsec.AcquisitionFilePath,
}
for _, k := range crowdsecCleanup {
if *k == "" {
continue
}
*k, err = filepath.Abs(*k)
if err != nil {
return errors.Wrapf(err, "failed to get absolute path of '%s'", *k)
}
}
for i, file := range c.Crowdsec.AcquisitionFiles {
f, err := filepath.Abs(file)
if err != nil {
return errors.Wrapf(err, "failed to get absolute path of '%s'", file)
}
c.Crowdsec.AcquisitionFiles[i] = f
}
if err := c.LoadAPIClient(); err != nil {
return fmt.Errorf("loading api client: %s", err.Error())
}
if err := c.LoadHub(); err != nil {
return fmt.Errorf("loading hub: %s", err)
}
return nil
}

View file

@ -0,0 +1,192 @@
package csconfig
import (
"fmt"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadCrowdsec(t *testing.T) {
acquisFullPath, err := filepath.Abs("./tests/acquis.yaml")
if err != nil {
t.Fatalf(err.Error())
}
acquisInDirFullPath, err := filepath.Abs("./tests/acquis/acquis.yaml")
if err != nil {
t.Fatalf(err.Error())
}
acquisDirFullPath, err := filepath.Abs("./tests/acquis")
if err != nil {
t.Fatalf(err.Error())
}
hubFullPath, err := filepath.Abs("./hub")
if err != nil {
t.Fatalf(err.Error())
}
dataFullPath, err := filepath.Abs("./data")
if err != nil {
t.Fatalf(err.Error())
}
configDirFullPath, err := filepath.Abs("./tests")
if err != nil {
t.Fatalf(err.Error())
}
hubIndexFileFullPath, err := filepath.Abs("./hub/.index.json")
if err != nil {
t.Fatalf(err.Error())
}
tests := []struct {
name string
Input *Config
expectedResult *CrowdsecServiceCfg
err string
}{
{
name: "basic valid configuration",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
DataDir: "./data",
HubDir: "./hub",
},
API: &APICfg{
Client: &LocalApiClientCfg{
CredentialsFilePath: "./tests/lapi-secrets.yaml",
},
},
Crowdsec: &CrowdsecServiceCfg{
AcquisitionFilePath: "./tests/acquis.yaml",
},
},
expectedResult: &CrowdsecServiceCfg{
AcquisitionDirPath: "",
AcquisitionFilePath: acquisFullPath,
ConfigDir: configDirFullPath,
DataDir: dataFullPath,
HubDir: hubFullPath,
HubIndexFile: hubIndexFileFullPath,
BucketsRoutinesCount: 1,
ParserRoutinesCount: 1,
OutputRoutinesCount: 1,
AcquisitionFiles: []string{acquisFullPath},
},
},
{
name: "basic valid configuration with acquisition dir",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
DataDir: "./data",
HubDir: "./hub",
},
API: &APICfg{
Client: &LocalApiClientCfg{
CredentialsFilePath: "./tests/lapi-secrets.yaml",
},
},
Crowdsec: &CrowdsecServiceCfg{
AcquisitionFilePath: "./tests/acquis.yaml",
AcquisitionDirPath: "./tests/acquis/",
},
},
expectedResult: &CrowdsecServiceCfg{
AcquisitionDirPath: acquisDirFullPath,
AcquisitionFilePath: acquisFullPath,
ConfigDir: configDirFullPath,
HubIndexFile: hubIndexFileFullPath,
DataDir: dataFullPath,
HubDir: hubFullPath,
BucketsRoutinesCount: 1,
ParserRoutinesCount: 1,
OutputRoutinesCount: 1,
AcquisitionFiles: []string{acquisFullPath, acquisInDirFullPath},
},
},
{
name: "no acquisition file and dir",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
DataDir: "./data",
HubDir: "./hub",
},
API: &APICfg{
Client: &LocalApiClientCfg{
CredentialsFilePath: "./tests/lapi-secrets.yaml",
},
},
Crowdsec: &CrowdsecServiceCfg{},
},
expectedResult: &CrowdsecServiceCfg{
BucketsRoutinesCount: 0,
ParserRoutinesCount: 0,
OutputRoutinesCount: 0,
},
},
{
name: "non existing acquisition file",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
DataDir: "./data",
HubDir: "./hub",
},
API: &APICfg{
Client: &LocalApiClientCfg{
CredentialsFilePath: "./tests/lapi-secrets.yaml",
},
},
Crowdsec: &CrowdsecServiceCfg{
AcquisitionFilePath: "./tests/acquis_not_exist.yaml",
},
},
expectedResult: &CrowdsecServiceCfg{
AcquisitionFilePath: "./tests/acquis_not_exist.yaml",
BucketsRoutinesCount: 0,
ParserRoutinesCount: 0,
OutputRoutinesCount: 0,
},
},
{
name: "agent disabled",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
DataDir: "./data",
HubDir: "./hub",
},
},
expectedResult: nil,
},
}
for idx, test := range tests {
fmt.Printf("TEST '%s'\n", test.name)
err := test.Input.LoadCrowdsec()
if err == nil && test.err != "" {
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Crowdsec)
if !isOk {
t.Fatalf("test '%s' failed", test.name)
}
}
}

View file

@ -13,3 +13,18 @@ type CscliCfg struct {
SimulationFilePath string `yaml:"-"` SimulationFilePath string `yaml:"-"`
PrometheusUrl string `yaml:"prometheus_uri"` PrometheusUrl string `yaml:"prometheus_uri"`
} }
func (c *Config) LoadCSCLI() error {
if c.Cscli == nil {
c.Cscli = &CscliCfg{}
}
if err := c.LoadConfigurationPaths(); err != nil {
return err
}
c.Cscli.ConfigDir = c.ConfigPaths.ConfigDir
c.Cscli.DataDir = c.ConfigPaths.DataDir
c.Cscli.HubDir = c.ConfigPaths.HubDir
c.Cscli.HubIndexFile = c.ConfigPaths.HubIndexFile
return nil
}

View file

@ -0,0 +1,84 @@
package csconfig
import (
"fmt"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadCSCLI(t *testing.T) {
hubFullPath, err := filepath.Abs("./hub")
if err != nil {
t.Fatalf(err.Error())
}
dataFullPath, err := filepath.Abs("./data")
if err != nil {
t.Fatalf(err.Error())
}
configDirFullPath, err := filepath.Abs("./tests")
if err != nil {
t.Fatalf(err.Error())
}
hubIndexFileFullPath, err := filepath.Abs("./hub/.index.json")
if err != nil {
t.Fatalf(err.Error())
}
tests := []struct {
name string
Input *Config
expectedResult *CscliCfg
err string
}{
{
name: "basic valid configuration",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
DataDir: "./data",
HubDir: "./hub",
HubIndexFile: "./hub/.index.json",
},
},
expectedResult: &CscliCfg{
ConfigDir: configDirFullPath,
DataDir: dataFullPath,
HubDir: hubFullPath,
HubIndexFile: hubIndexFileFullPath,
},
},
{
name: "no configuration path",
Input: &Config{},
expectedResult: &CscliCfg{},
},
}
for idx, test := range tests {
err := test.Input.LoadCSCLI()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Cscli)
if !isOk {
t.Fatalf("TEST '%s': NOK", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}

View file

@ -1,6 +1,10 @@
package csconfig package csconfig
import log "github.com/sirupsen/logrus" import (
"fmt"
log "github.com/sirupsen/logrus"
)
type DatabaseCfg struct { type DatabaseCfg struct {
User string `yaml:"user"` User string `yaml:"user"`
@ -18,3 +22,19 @@ type FlushDBCfg struct {
MaxItems *int `yaml:"max_items"` MaxItems *int `yaml:"max_items"`
MaxAge *string `yaml:"max_age"` MaxAge *string `yaml:"max_age"`
} }
func (c *Config) LoadDBConfig() error {
if c.DbConfig == nil {
return fmt.Errorf("no database configuration provided")
}
if c.Cscli != nil {
c.Cscli.DbConfig = c.DbConfig
}
if c.API != nil && c.API.Server != nil {
c.API.Server.DbConfig = c.DbConfig
}
return nil
}

View file

@ -0,0 +1,62 @@
package csconfig
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadDBConfig(t *testing.T) {
tests := []struct {
name string
Input *Config
expectedResult *DatabaseCfg
err string
}{
{
name: "basic valid configuration",
Input: &Config{
DbConfig: &DatabaseCfg{
Type: "sqlite",
DbPath: "./tests/test.db",
},
Cscli: &CscliCfg{},
API: &APICfg{
Server: &LocalApiServerCfg{},
},
},
expectedResult: &DatabaseCfg{
Type: "sqlite",
DbPath: "./tests/test.db",
},
},
{
name: "no configuration path",
Input: &Config{},
expectedResult: nil,
},
}
for idx, test := range tests {
err := test.Input.LoadDBConfig()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.DbConfig)
if !isOk {
t.Fatalf("TEST '%s': NOK", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}

24
pkg/csconfig/hub.go Normal file
View file

@ -0,0 +1,24 @@
package csconfig
/*cscli specific config, such as hub directory*/
type Hub struct {
HubDir string `yaml:"-"`
ConfigDir string `yaml:"-"`
HubIndexFile string `yaml:"-"`
DataDir string `yaml:"-"`
}
func (c *Config) LoadHub() error {
if err := c.LoadConfigurationPaths(); err != nil {
return err
}
c.Hub = &Hub{
HubIndexFile: c.ConfigPaths.HubIndexFile,
ConfigDir: c.ConfigPaths.ConfigDir,
HubDir: c.ConfigPaths.HubDir,
DataDir: c.ConfigPaths.DataDir,
}
return nil
}

94
pkg/csconfig/hub_test.go Normal file
View file

@ -0,0 +1,94 @@
package csconfig
import (
"fmt"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadHub(t *testing.T) {
hubFullPath, err := filepath.Abs("./hub")
if err != nil {
t.Fatalf(err.Error())
}
dataFullPath, err := filepath.Abs("./data")
if err != nil {
t.Fatalf(err.Error())
}
configDirFullPath, err := filepath.Abs("./tests")
if err != nil {
t.Fatalf(err.Error())
}
hubIndexFileFullPath, err := filepath.Abs("./hub/.index.json")
if err != nil {
t.Fatalf(err.Error())
}
tests := []struct {
name string
Input *Config
expectedResult *Hub
err string
}{
{
name: "basic valid configuration",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
DataDir: "./data",
HubDir: "./hub",
HubIndexFile: "./hub/.index.json",
},
},
expectedResult: &Hub{
ConfigDir: configDirFullPath,
DataDir: dataFullPath,
HubDir: hubFullPath,
HubIndexFile: hubIndexFileFullPath,
},
},
{
name: "no data dir",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
ConfigDir: "./tests",
HubDir: "./hub",
HubIndexFile: "./hub/.index.json",
},
},
expectedResult: nil,
},
{
name: "no configuration path",
Input: &Config{},
expectedResult: nil,
},
}
for idx, test := range tests {
err := test.Input.LoadHub()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Hub)
if !isOk {
t.Fatalf("TEST '%s': NOK", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}

View file

@ -1,5 +1,7 @@
package csconfig package csconfig
import "fmt"
/**/ /**/
type PrometheusCfg struct { type PrometheusCfg struct {
Enabled bool `yaml:"enabled"` Enabled bool `yaml:"enabled"`
@ -7,3 +9,13 @@ type PrometheusCfg struct {
ListenAddr string `yaml:"listen_addr"` ListenAddr string `yaml:"listen_addr"`
ListenPort int `yaml:"listen_port"` ListenPort int `yaml:"listen_port"`
} }
func (c *Config) LoadPrometheus() error {
if c.Cscli != nil && c.Cscli.PrometheusUrl == "" && c.Prometheus != nil {
if c.Prometheus.ListenAddr != "" && c.Prometheus.ListenPort != 0 {
c.Cscli.PrometheusUrl = fmt.Sprintf("http://%s:%d", c.Prometheus.ListenAddr, c.Prometheus.ListenPort)
}
}
return nil
}

View file

@ -0,0 +1,55 @@
package csconfig
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadPrometheus(t *testing.T) {
tests := []struct {
name string
Input *Config
expectedResult string
err string
}{
{
name: "basic valid configuration",
Input: &Config{
Prometheus: &PrometheusCfg{
Enabled: true,
Level: "full",
ListenAddr: "127.0.0.1",
ListenPort: 6060,
},
Cscli: &CscliCfg{},
},
expectedResult: "http://127.0.0.1:6060",
},
}
for idx, test := range tests {
err := test.Input.LoadPrometheus()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Cscli.PrometheusUrl)
if !isOk {
t.Fatalf("test '%s' failed\n", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}

View file

@ -1,5 +1,14 @@
package csconfig package csconfig
import (
"fmt"
"io/ioutil"
"path/filepath"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
type SimulationConfig struct { type SimulationConfig struct {
Simulation *bool `yaml:"simulation"` Simulation *bool `yaml:"simulation"`
Exclusions []string `yaml:"exclusions,omitempty"` Exclusions []string `yaml:"exclusions,omitempty"`
@ -19,3 +28,33 @@ func (s *SimulationConfig) IsSimulated(scenario string) bool {
} }
return simulated return simulated
} }
func (c *Config) LoadSimulation() error {
if err := c.LoadConfigurationPaths(); err != nil {
return err
}
simCfg := SimulationConfig{}
if c.ConfigPaths.SimulationFilePath == "" {
c.ConfigPaths.SimulationFilePath = filepath.Clean(c.ConfigPaths.ConfigDir + "/simulation.yaml")
}
rcfg, err := ioutil.ReadFile(c.ConfigPaths.SimulationFilePath)
if err != nil {
return errors.Wrapf(err, "while reading '%s'", c.ConfigPaths.SimulationFilePath)
} else {
if err := yaml.UnmarshalStrict(rcfg, &simCfg); err != nil {
return fmt.Errorf("while unmarshaling simulation file '%s' : %s", c.ConfigPaths.SimulationFilePath, err)
}
}
if simCfg.Simulation == nil {
simCfg.Simulation = new(bool)
}
if c.Crowdsec != nil {
c.Crowdsec.SimulationConfig = &simCfg
}
if c.Cscli != nil {
c.Cscli.SimulationConfig = &simCfg
}
return nil
}

View file

@ -0,0 +1,156 @@
package csconfig
import (
"fmt"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestSimulationLoading(t *testing.T) {
testXXFullPath, err := filepath.Abs("./tests/xxx.yaml")
if err != nil {
panic(err)
}
badYamlFullPath, err := filepath.Abs("./tests/config.yaml")
if err != nil {
panic(err)
}
tests := []struct {
name string
Input *Config
expectedResult *SimulationConfig
err string
}{
{
name: "basic valid simulation",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/simulation.yaml",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
Cscli: &CscliCfg{},
},
expectedResult: &SimulationConfig{Simulation: new(bool)},
},
{
name: "basic bad file name",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/xxx.yaml",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
},
err: fmt.Sprintf("while reading '%s': open %s: no such file or directory", testXXFullPath, testXXFullPath),
},
{
name: "basic nil config",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
},
},
{
name: "basic bad file content",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/config.yaml",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
},
err: fmt.Sprintf("while unmarshaling simulation file '%s' : yaml: unmarshal errors", badYamlFullPath),
},
{
name: "basic bad file content",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/config.yaml",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
},
err: fmt.Sprintf("while unmarshaling simulation file '%s' : yaml: unmarshal errors", badYamlFullPath),
},
}
for idx, test := range tests {
err := test.Input.LoadSimulation()
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
} else if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Crowdsec.SimulationConfig)
if !isOk {
t.Fatalf("TEST '%s': NOK\n", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
}
}
func TestIsSimulated(t *testing.T) {
simCfgOff := &SimulationConfig{
Simulation: new(bool),
Exclusions: []string{"test"},
}
simCfgOn := &SimulationConfig{
Simulation: new(bool),
Exclusions: []string{"test"},
}
*simCfgOn.Simulation = true
tests := []struct {
name string
SimulationConfig *SimulationConfig
Input string
expectedResult bool
err string
}{
{
name: "No simulation except (in exclusion)",
SimulationConfig: simCfgOff,
Input: "test",
expectedResult: true,
},
{
name: "All simulation (not in exclusion)",
SimulationConfig: simCfgOn,
Input: "toto",
expectedResult: true,
},
{
name: "All simulation (in exclusion)",
SimulationConfig: simCfgOn,
Input: "test",
expectedResult: false,
},
}
for _, test := range tests {
IsSimulated := test.SimulationConfig.IsSimulated(test.Input)
isOk := assert.Equal(t, test.expectedResult, IsSimulated)
if !isOk {
fmt.Printf("TEST: '%v' failed", test.name)
t.Fatal()
}
}
}

View file

View file

@ -0,0 +1 @@
unknown_key: test

View file

@ -0,0 +1,3 @@
login: test
password:
url:

View file

@ -0,0 +1,3 @@
url: http://localhost:8080
login: test
password: testpassword

View file

@ -0,0 +1,3 @@
url: http://crowdsec.api
login: test
password: testpassword

View file

@ -27,12 +27,12 @@ var testDataFolder = "."
func TestItemStatus(t *testing.T) { func TestItemStatus(t *testing.T) {
cfg := test_prepenv() cfg := test_prepenv()
err := UpdateHubIdx(cfg.Cscli) err := UpdateHubIdx(cfg.Hub)
//DownloadHubIdx() //DownloadHubIdx()
if err != nil { if err != nil {
t.Fatalf("failed to download index : %s", err) t.Fatalf("failed to download index : %s", err)
} }
if err := GetHubIdx(cfg.Cscli); err != nil { if err := GetHubIdx(cfg.Hub); err != nil {
t.Fatalf("failed to load hub index : %s", err) t.Fatalf("failed to load hub index : %s", err)
} }
@ -74,12 +74,12 @@ func TestItemStatus(t *testing.T) {
func TestGetters(t *testing.T) { func TestGetters(t *testing.T) {
cfg := test_prepenv() cfg := test_prepenv()
err := UpdateHubIdx(cfg.Cscli) err := UpdateHubIdx(cfg.Hub)
//DownloadHubIdx() //DownloadHubIdx()
if err != nil { if err != nil {
t.Fatalf("failed to download index : %s", err) t.Fatalf("failed to download index : %s", err)
} }
if err := GetHubIdx(cfg.Cscli); err != nil { if err := GetHubIdx(cfg.Hub); err != nil {
t.Fatalf("failed to load hub index : %s", err) t.Fatalf("failed to load hub index : %s", err)
} }
@ -135,58 +135,58 @@ func TestIndexDownload(t *testing.T) {
cfg := test_prepenv() cfg := test_prepenv()
err := UpdateHubIdx(cfg.Cscli) err := UpdateHubIdx(cfg.Hub)
//DownloadHubIdx() //DownloadHubIdx()
if err != nil { if err != nil {
t.Fatalf("failed to download index : %s", err) t.Fatalf("failed to download index : %s", err)
} }
if err := GetHubIdx(cfg.Cscli); err != nil { if err := GetHubIdx(cfg.Hub); err != nil {
t.Fatalf("failed to load hub index : %s", err) t.Fatalf("failed to load hub index : %s", err)
} }
} }
func test_prepenv() *csconfig.GlobalConfig { func test_prepenv() *csconfig.Config {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
var cfg = csconfig.NewConfig() var cfg = &csconfig.Config{}
cfg.Cscli = &csconfig.CscliCfg{} cfg.Hub = &csconfig.Hub{}
cfg.Cscli.ConfigDir, _ = filepath.Abs("./install") cfg.Hub.ConfigDir, _ = filepath.Abs("./install")
cfg.Cscli.HubDir, _ = filepath.Abs("./hubdir") cfg.Hub.HubDir, _ = filepath.Abs("./hubdir")
cfg.Cscli.HubIndexFile = filepath.Clean("./hubdir/.index.json") cfg.Hub.HubIndexFile = filepath.Clean("./hubdir/.index.json")
//Mock the http client //Mock the http client
http.DefaultClient.Transport = newMockTransport() http.DefaultClient.Transport = newMockTransport()
if err := os.RemoveAll(cfg.Cscli.ConfigDir); err != nil { if err := os.RemoveAll(cfg.Hub.ConfigDir); err != nil {
log.Fatalf("failed to remove %s : %s", cfg.Cscli.ConfigDir, err) log.Fatalf("failed to remove %s : %s", cfg.Hub.ConfigDir, err)
} }
if err := os.MkdirAll(cfg.Cscli.ConfigDir, 0700); err != nil { if err := os.MkdirAll(cfg.Hub.ConfigDir, 0700); err != nil {
log.Fatalf("mkdir : %s", err) log.Fatalf("mkdir : %s", err)
} }
if err := os.RemoveAll(cfg.Cscli.HubDir); err != nil { if err := os.RemoveAll(cfg.Hub.HubDir); err != nil {
log.Fatalf("failed to remove %s : %s", cfg.Cscli.HubDir, err) log.Fatalf("failed to remove %s : %s", cfg.Hub.HubDir, err)
} }
if err := os.MkdirAll(cfg.Cscli.HubDir, 0700); err != nil { if err := os.MkdirAll(cfg.Hub.HubDir, 0700); err != nil {
log.Fatalf("failed to mkdir %s : %s", cfg.Cscli.HubDir, err) log.Fatalf("failed to mkdir %s : %s", cfg.Hub.HubDir, err)
} }
if err := UpdateHubIdx(cfg.Cscli); err != nil { if err := UpdateHubIdx(cfg.Hub); err != nil {
log.Fatalf("failed to download index : %s", err) log.Fatalf("failed to download index : %s", err)
} }
// if err := os.RemoveAll(cfg.Cscli.InstallDir); err != nil { // if err := os.RemoveAll(cfg.Hub.InstallDir); err != nil {
// log.Fatalf("failed to remove %s : %s", cfg.Cscli.InstallDir, err) // log.Fatalf("failed to remove %s : %s", cfg.Hub.InstallDir, err)
// } // }
// if err := os.MkdirAll(cfg.Cscli.InstallDir, 0700); err != nil { // if err := os.MkdirAll(cfg.Hub.InstallDir, 0700); err != nil {
// log.Fatalf("failed to mkdir %s : %s", cfg.Cscli.InstallDir, err) // log.Fatalf("failed to mkdir %s : %s", cfg.Hub.InstallDir, err)
// } // }
return cfg return cfg
} }
func testInstallItem(cfg *csconfig.CscliCfg, t *testing.T, item Item) { func testInstallItem(cfg *csconfig.Hub, t *testing.T, item Item) {
//Install the parser //Install the parser
item, err := DownloadLatest(cfg, item, false) item, err := DownloadLatest(cfg, item, false)
@ -218,7 +218,7 @@ func testInstallItem(cfg *csconfig.CscliCfg, t *testing.T, item Item) {
} }
} }
func testTaintItem(cfg *csconfig.CscliCfg, t *testing.T, item Item) { func testTaintItem(cfg *csconfig.Hub, t *testing.T, item Item) {
if hubIdx[item.Type][item.Name].Tainted { if hubIdx[item.Type][item.Name].Tainted {
t.Fatalf("pre-taint: %s should not be tainted", item.Name) t.Fatalf("pre-taint: %s should not be tainted", item.Name)
} }
@ -240,7 +240,7 @@ func testTaintItem(cfg *csconfig.CscliCfg, t *testing.T, item Item) {
} }
} }
func testUpdateItem(cfg *csconfig.CscliCfg, t *testing.T, item Item) { func testUpdateItem(cfg *csconfig.Hub, t *testing.T, item Item) {
if hubIdx[item.Type][item.Name].UpToDate { if hubIdx[item.Type][item.Name].UpToDate {
t.Fatalf("update: %s should NOT be up-to-date", item.Name) t.Fatalf("update: %s should NOT be up-to-date", item.Name)
@ -262,7 +262,7 @@ func testUpdateItem(cfg *csconfig.CscliCfg, t *testing.T, item Item) {
} }
} }
func testDisableItem(cfg *csconfig.CscliCfg, t *testing.T, item Item) { func testDisableItem(cfg *csconfig.Hub, t *testing.T, item Item) {
if !item.Installed { if !item.Installed {
t.Fatalf("disable: %s should be installed", item.Name) t.Fatalf("disable: %s should be installed", item.Name)
} }
@ -314,20 +314,20 @@ func TestInstallParser(t *testing.T) {
*/ */
cfg := test_prepenv() cfg := test_prepenv()
if err := GetHubIdx(cfg.Cscli); err != nil { if err := GetHubIdx(cfg.Hub); err != nil {
t.Fatalf("failed to load hub index") t.Fatalf("failed to load hub index")
} }
//map iteration is random by itself //map iteration is random by itself
for _, it := range hubIdx[PARSERS] { for _, it := range hubIdx[PARSERS] {
testInstallItem(cfg.Cscli, t, it) testInstallItem(cfg.Hub, t, it)
it = hubIdx[PARSERS][it.Name] it = hubIdx[PARSERS][it.Name]
_ = HubStatus(PARSERS, it.Name, false) _ = HubStatus(PARSERS, it.Name, false)
testTaintItem(cfg.Cscli, t, it) testTaintItem(cfg.Hub, t, it)
it = hubIdx[PARSERS][it.Name] it = hubIdx[PARSERS][it.Name]
_ = HubStatus(PARSERS, it.Name, false) _ = HubStatus(PARSERS, it.Name, false)
testUpdateItem(cfg.Cscli, t, it) testUpdateItem(cfg.Hub, t, it)
it = hubIdx[PARSERS][it.Name] it = hubIdx[PARSERS][it.Name]
testDisableItem(cfg.Cscli, t, it) testDisableItem(cfg.Hub, t, it)
it = hubIdx[PARSERS][it.Name] it = hubIdx[PARSERS][it.Name]
break break
@ -347,18 +347,18 @@ func TestInstallCollection(t *testing.T) {
*/ */
cfg := test_prepenv() cfg := test_prepenv()
if err := GetHubIdx(cfg.Cscli); err != nil { if err := GetHubIdx(cfg.Hub); err != nil {
t.Fatalf("failed to load hub index") t.Fatalf("failed to load hub index")
} }
//map iteration is random by itself //map iteration is random by itself
for _, it := range hubIdx[COLLECTIONS] { for _, it := range hubIdx[COLLECTIONS] {
testInstallItem(cfg.Cscli, t, it) testInstallItem(cfg.Hub, t, it)
it = hubIdx[COLLECTIONS][it.Name] it = hubIdx[COLLECTIONS][it.Name]
testTaintItem(cfg.Cscli, t, it) testTaintItem(cfg.Hub, t, it)
it = hubIdx[COLLECTIONS][it.Name] it = hubIdx[COLLECTIONS][it.Name]
testUpdateItem(cfg.Cscli, t, it) testUpdateItem(cfg.Hub, t, it)
it = hubIdx[COLLECTIONS][it.Name] it = hubIdx[COLLECTIONS][it.Name]
testDisableItem(cfg.Cscli, t, it) testDisableItem(cfg.Hub, t, it)
it = hubIdx[COLLECTIONS][it.Name] it = hubIdx[COLLECTIONS][it.Name]
x := HubStatus(COLLECTIONS, it.Name, false) x := HubStatus(COLLECTIONS, it.Name, false)

View file

@ -22,9 +22,9 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
func UpdateHubIdx(cscli *csconfig.CscliCfg) error { func UpdateHubIdx(hub *csconfig.Hub) error {
bidx, err := DownloadHubIdx(cscli) bidx, err := DownloadHubIdx(hub)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to download index") return errors.Wrap(err, "failed to download index")
} }
@ -35,13 +35,13 @@ func UpdateHubIdx(cscli *csconfig.CscliCfg) error {
} }
} }
hubIdx = ret hubIdx = ret
if err := LocalSync(cscli); err != nil { if err := LocalSync(hub); err != nil {
return errors.Wrap(err, "failed to sync") return errors.Wrap(err, "failed to sync")
} }
return nil return nil
} }
func DownloadHubIdx(cscli *csconfig.CscliCfg) ([]byte, error) { func DownloadHubIdx(hub *csconfig.Hub) ([]byte, error) {
log.Debugf("fetching index from branch %s (%s)", HubBranch, fmt.Sprintf(RawFileURLTemplate, HubBranch, HubIndexFile)) log.Debugf("fetching index from branch %s (%s)", HubBranch, fmt.Sprintf(RawFileURLTemplate, HubBranch, HubIndexFile))
req, err := http.NewRequest("GET", fmt.Sprintf(RawFileURLTemplate, HubBranch, HubIndexFile), nil) req, err := http.NewRequest("GET", fmt.Sprintf(RawFileURLTemplate, HubBranch, HubIndexFile), nil)
if err != nil { if err != nil {
@ -59,7 +59,7 @@ func DownloadHubIdx(cscli *csconfig.CscliCfg) ([]byte, error) {
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to read request answer for hub index") return nil, errors.Wrap(err, "failed to read request answer for hub index")
} }
file, err := os.OpenFile(cscli.HubIndexFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) file, err := os.OpenFile(hub.HubIndexFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "while opening hub index file") return nil, errors.Wrap(err, "while opening hub index file")
@ -70,12 +70,12 @@ func DownloadHubIdx(cscli *csconfig.CscliCfg) ([]byte, error) {
if err != nil { if err != nil {
return nil, errors.Wrap(err, "while writting hub index file") return nil, errors.Wrap(err, "while writting hub index file")
} }
log.Infof("Wrote new %d bytes index to %s", wsize, cscli.HubIndexFile) log.Infof("Wrote new %d bytes index to %s", wsize, hub.HubIndexFile)
return body, nil return body, nil
} }
//DownloadLatest will download the latest version of Item to the tdir directory //DownloadLatest will download the latest version of Item to the tdir directory
func DownloadLatest(cscli *csconfig.CscliCfg, target Item, overwrite bool) (Item, error) { func DownloadLatest(hub *csconfig.Hub, target Item, overwrite bool) (Item, error) {
var err error var err error
log.Debugf("Downloading %s %s", target.Type, target.Name) log.Debugf("Downloading %s %s", target.Type, target.Name)
@ -89,12 +89,12 @@ func DownloadLatest(cscli *csconfig.CscliCfg, target Item, overwrite bool) (Item
//recurse as it's a collection //recurse as it's a collection
if ptrtype == COLLECTIONS { if ptrtype == COLLECTIONS {
log.Tracef("collection, recurse") log.Tracef("collection, recurse")
hubIdx[ptrtype][p], err = DownloadLatest(cscli, val, overwrite) hubIdx[ptrtype][p], err = DownloadLatest(hub, val, overwrite)
if err != nil { if err != nil {
return target, errors.Wrap(err, fmt.Sprintf("while downloading %s", val.Name)) return target, errors.Wrap(err, fmt.Sprintf("while downloading %s", val.Name))
} }
} }
item, err := DownloadItem(cscli, val, overwrite) item, err := DownloadItem(hub, val, overwrite)
if err != nil { if err != nil {
return target, errors.Wrap(err, fmt.Sprintf("while downloading %s", val.Name)) return target, errors.Wrap(err, fmt.Sprintf("while downloading %s", val.Name))
} }
@ -102,7 +102,7 @@ func DownloadLatest(cscli *csconfig.CscliCfg, target Item, overwrite bool) (Item
// We need to enable an item when it has been added to a collection since latest release of the collection. // We need to enable an item when it has been added to a collection since latest release of the collection.
// We check if val.Downloaded is false because maybe the item has been disabled by the user. // We check if val.Downloaded is false because maybe the item has been disabled by the user.
if !item.Installed && !val.Downloaded { if !item.Installed && !val.Downloaded {
if item, err = EnableItem(cscli, item); err != nil { if item, err = EnableItem(hub, item); err != nil {
return target, errors.Wrapf(err, "enabling '%s'", item.Name) return target, errors.Wrapf(err, "enabling '%s'", item.Name)
} }
} }
@ -112,20 +112,20 @@ func DownloadLatest(cscli *csconfig.CscliCfg, target Item, overwrite bool) (Item
} }
} }
} }
target, err = DownloadItem(cscli, target, overwrite) target, err = DownloadItem(hub, target, overwrite)
if err != nil { if err != nil {
return target, fmt.Errorf("failed to download item : %s", err) return target, fmt.Errorf("failed to download item : %s", err)
} }
} else { } else {
return DownloadItem(cscli, target, overwrite) return DownloadItem(hub, target, overwrite)
} }
return target, nil return target, nil
} }
func DownloadItem(cscli *csconfig.CscliCfg, target Item, overwrite bool) (Item, error) { func DownloadItem(hub *csconfig.Hub, target Item, overwrite bool) (Item, error) {
var tdir = cscli.HubDir var tdir = hub.HubDir
var dataFolder = cscli.DataDir var dataFolder = hub.DataDir
/*if user didn't --force, don't overwrite local, tainted, up-to-date files*/ /*if user didn't --force, don't overwrite local, tainted, up-to-date files*/
if !overwrite { if !overwrite {
if target.Tainted { if target.Tainted {

View file

@ -12,21 +12,27 @@ import (
func TestDownloadHubIdx(t *testing.T) { func TestDownloadHubIdx(t *testing.T) {
back := RawFileURLTemplate back := RawFileURLTemplate
//bad url template //bad url template
fmt.Println("Test 'bad URL'")
RawFileURLTemplate = "x" RawFileURLTemplate = "x"
ret, err := DownloadHubIdx(&csconfig.CscliCfg{}) ret, err := DownloadHubIdx(&csconfig.Hub{})
if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "failed to build request for hub index: parse ") { if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "failed to build request for hub index: parse ") {
log.Errorf("unexpected error %s", err) log.Errorf("unexpected error %s", err)
} }
fmt.Printf("->%+v", ret)
//bad domain //bad domain
fmt.Println("Test 'bad domain'")
RawFileURLTemplate = "https://baddomain/crowdsecurity/hub/%s/%s" RawFileURLTemplate = "https://baddomain/crowdsecurity/hub/%s/%s"
ret, err = DownloadHubIdx(&csconfig.CscliCfg{}) ret, err = DownloadHubIdx(&csconfig.Hub{})
if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "failed http request for hub index: Get") { if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "failed http request for hub index: Get") {
log.Errorf("unexpected error %s", err) log.Errorf("unexpected error %s", err)
} }
fmt.Printf("->%+v", ret)
//bad target path //bad target path
fmt.Println("Test 'bad target path'")
RawFileURLTemplate = back RawFileURLTemplate = back
ret, err = DownloadHubIdx(&csconfig.CscliCfg{HubIndexFile: "/does/not/exist/index.json"}) ret, err = DownloadHubIdx(&csconfig.Hub{HubIndexFile: "/does/not/exist/index.json"})
if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "while opening hub index file: open /does/not/exist/index.json:") { if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "while opening hub index file: open /does/not/exist/index.json:") {
log.Errorf("unexpected error %s", err) log.Errorf("unexpected error %s", err)
} }

View file

@ -11,9 +11,9 @@ import (
) )
//DisableItem to disable an item managed by the hub, removes the symlink if purge is true //DisableItem to disable an item managed by the hub, removes the symlink if purge is true
func DisableItem(cscli *csconfig.CscliCfg, target Item, purge bool, force bool) (Item, error) { func DisableItem(hub *csconfig.Hub, target Item, purge bool, force bool) (Item, error) {
var tdir = cscli.ConfigDir var tdir = hub.ConfigDir
var hdir = cscli.HubDir var hdir = hub.HubDir
syml, err := filepath.Abs(tdir + "/" + target.Type + "/" + target.Stage + "/" + target.FileName) syml, err := filepath.Abs(tdir + "/" + target.Type + "/" + target.Stage + "/" + target.FileName)
if err != nil { if err != nil {
@ -34,7 +34,7 @@ func DisableItem(cscli *csconfig.CscliCfg, target Item, purge bool, force bool)
ptrtype := ItemTypes[idx] ptrtype := ItemTypes[idx]
for _, p := range ptr { for _, p := range ptr {
if val, ok := hubIdx[ptrtype][p]; ok { if val, ok := hubIdx[ptrtype][p]; ok {
hubIdx[ptrtype][p], err = DisableItem(cscli, val, purge, force) hubIdx[ptrtype][p], err = DisableItem(hub, val, purge, force)
if err != nil { if err != nil {
return target, errors.Wrap(err, fmt.Sprintf("while disabling %s", p)) return target, errors.Wrap(err, fmt.Sprintf("while disabling %s", p))
} }
@ -51,7 +51,7 @@ func DisableItem(cscli *csconfig.CscliCfg, target Item, purge bool, force bool)
return target, fmt.Errorf("can't delete %s : %s doesn't exist", target.Name, syml) return target, fmt.Errorf("can't delete %s : %s doesn't exist", target.Name, syml)
} }
} else { } else {
//if it's managed by hub, it's a symlink to csconfig.GConfig.Cscli.HubDir / ... //if it's managed by hub, it's a symlink to csconfig.GConfig.hub.HubDir / ...
if stat.Mode()&os.ModeSymlink == 0 { if stat.Mode()&os.ModeSymlink == 0 {
log.Warningf("%s (%s) isn't a symlink, can't disable", target.Name, syml) log.Warningf("%s (%s) isn't a symlink, can't disable", target.Name, syml)
return target, fmt.Errorf("%s isn't managed by hub", target.Name) return target, fmt.Errorf("%s isn't managed by hub", target.Name)
@ -90,9 +90,9 @@ func DisableItem(cscli *csconfig.CscliCfg, target Item, purge bool, force bool)
return target, nil return target, nil
} }
func EnableItem(cscli *csconfig.CscliCfg, target Item) (Item, error) { func EnableItem(hub *csconfig.Hub, target Item) (Item, error) {
var tdir = cscli.ConfigDir var tdir = hub.ConfigDir
var hdir = cscli.HubDir var hdir = hub.HubDir
var err error var err error
parent_dir := filepath.Clean(tdir + "/" + target.Type + "/" + target.Stage + "/") parent_dir := filepath.Clean(tdir + "/" + target.Type + "/" + target.Stage + "/")
/*create directories if needed*/ /*create directories if needed*/
@ -123,7 +123,7 @@ func EnableItem(cscli *csconfig.CscliCfg, target Item) (Item, error) {
ptrtype := ItemTypes[idx] ptrtype := ItemTypes[idx]
for _, p := range ptr { for _, p := range ptr {
if val, ok := hubIdx[ptrtype][p]; ok { if val, ok := hubIdx[ptrtype][p]; ok {
hubIdx[ptrtype][p], err = EnableItem(cscli, val) hubIdx[ptrtype][p], err = EnableItem(hub, val)
if err != nil { if err != nil {
return target, errors.Wrap(err, fmt.Sprintf("while installing %s", p)) return target, errors.Wrap(err, fmt.Sprintf("while installing %s", p))
} }

View file

@ -49,7 +49,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
subs := strings.Split(path, "/") subs := strings.Split(path, "/")
log.Tracef("path:%s, hubdir:%s, installdir:%s", path, hubdir, installdir) log.Tracef("path:%s, hubdir:%s, installdir:%s", path, hubdir, installdir)
/*we're in hub (~/.cscli/hub/)*/ /*we're in hub (~/.hub/hub/)*/
if strings.HasPrefix(path, hubdir) { if strings.HasPrefix(path, hubdir) {
log.Tracef("in hub dir") log.Tracef("in hub dir")
inhub = true inhub = true
@ -95,7 +95,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
/* /*
we can encounter 'collections' in the form of a symlink : we can encounter 'collections' in the form of a symlink :
/etc/crowdsec/.../collections/linux.yaml -> ~/.cscli/hub/collections/.../linux.yaml /etc/crowdsec/.../collections/linux.yaml -> ~/.hub/hub/collections/.../linux.yaml
when the collection is installed, both files are created when the collection is installed, both files are created
*/ */
//non symlinks are local user files or hub files //non symlinks are local user files or hub files
@ -108,7 +108,7 @@ func parser_visit(path string, f os.FileInfo, err error) error {
if err != nil { if err != nil {
return fmt.Errorf("unable to read symlink of %s", path) return fmt.Errorf("unable to read symlink of %s", path)
} }
//the symlink target doesn't exist, user might have remove ~/.cscli/hub/...yaml without deleting /etc/crowdsec/....yaml //the symlink target doesn't exist, user might have remove ~/.hub/hub/...yaml without deleting /etc/crowdsec/....yaml
_, err := os.Lstat(hubpath) _, err := os.Lstat(hubpath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
log.Infof("%s is a symlink to %s that doesn't exist, deleting symlink", path, hubpath) log.Infof("%s is a symlink to %s that doesn't exist, deleting symlink", path, hubpath)
@ -294,10 +294,10 @@ func CollecDepsCheck(v *Item) error {
return nil return nil
} }
func SyncDir(cscli *csconfig.CscliCfg, dir string) error { func SyncDir(hub *csconfig.Hub, dir string) error {
hubdir = cscli.HubDir hubdir = hub.HubDir
installdir = cscli.ConfigDir installdir = hub.ConfigDir
indexpath = cscli.HubIndexFile indexpath = hub.HubIndexFile
/*For each, scan PARSERS, PARSERS_OVFLW, SCENARIOS and COLLECTIONS last*/ /*For each, scan PARSERS, PARSERS_OVFLW, SCENARIOS and COLLECTIONS last*/
for _, scan := range ItemTypes { for _, scan := range ItemTypes {
@ -322,13 +322,13 @@ func SyncDir(cscli *csconfig.CscliCfg, dir string) error {
} }
/* Updates the infos from HubInit() with the local state */ /* Updates the infos from HubInit() with the local state */
func LocalSync(cscli *csconfig.CscliCfg) error { func LocalSync(hub *csconfig.Hub) error {
skippedLocal = 0 skippedLocal = 0
skippedTainted = 0 skippedTainted = 0
for _, dir := range []string{cscli.ConfigDir, cscli.HubDir} { for _, dir := range []string{hub.ConfigDir, hub.HubDir} {
log.Debugf("scanning %s", dir) log.Debugf("scanning %s", dir)
if err := SyncDir(cscli, dir); err != nil { if err := SyncDir(hub, dir); err != nil {
return fmt.Errorf("failed to scan %s : %s", dir, err) return fmt.Errorf("failed to scan %s : %s", dir, err)
} }
} }
@ -336,12 +336,12 @@ func LocalSync(cscli *csconfig.CscliCfg) error {
return nil return nil
} }
func GetHubIdx(cscli *csconfig.CscliCfg) error { func GetHubIdx(hub *csconfig.Hub) error {
if cscli == nil { if hub == nil {
return fmt.Errorf("no configuration found for cscli") return fmt.Errorf("no configuration found for hub")
} }
log.Debugf("loading hub idx %s", cscli.HubIndexFile) log.Debugf("loading hub idx %s", hub.HubIndexFile)
bidx, err := ioutil.ReadFile(cscli.HubIndexFile) bidx, err := ioutil.ReadFile(hub.HubIndexFile)
if err != nil { if err != nil {
return errors.Wrap(err, "unable to read index file") return errors.Wrap(err, "unable to read index file")
} }
@ -353,7 +353,7 @@ func GetHubIdx(cscli *csconfig.CscliCfg) error {
return err return err
} }
hubIdx = ret hubIdx = ret
if err := LocalSync(cscli); err != nil { if err := LocalSync(hub); err != nil {
log.Fatalf("Failed to sync Hub index with local deployment : %v", err) log.Fatalf("Failed to sync Hub index with local deployment : %v", err)
} }
return nil return nil

View file

@ -69,7 +69,6 @@ func NewClient(config *csconfig.DatabaseCfg) (*Client, error) {
if err := types.ConfigureLogger(clog); err != nil { if err := types.ConfigureLogger(clog); err != nil {
return nil, errors.Wrap(err, "while configuring db logger") return nil, errors.Wrap(err, "while configuring db logger")
} }
log.Infof("Log level: %+v", config.LogLevel)
if config.LogLevel != nil { if config.LogLevel != nil {
clog.SetLevel(*config.LogLevel) clog.SetLevel(*config.LogLevel)
if *config.LogLevel >= log.DebugLevel { if *config.LogLevel >= log.DebugLevel {

View file

@ -1,8 +1,8 @@
package parser package parser
import ( import (
"io/ioutil"
"fmt" "fmt"
"io/ioutil"
"github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/csconfig"
@ -45,8 +45,7 @@ func Init(c map[string]interface{}) (*UnixParserCtx, error) {
return &r, nil return &r, nil
} }
func LoadParsers(cConfig *csconfig.Config, parsers *Parsers) (*Parsers, error) {
func LoadParsers(cConfig *csconfig.GlobalConfig, parsers *Parsers) (*Parsers, error) {
var err error var err error
log.Infof("Loading grok library %s", cConfig.Crowdsec.ConfigDir+string("/patterns/")) log.Infof("Loading grok library %s", cConfig.Crowdsec.ConfigDir+string("/patterns/"))

View file

@ -0,0 +1,43 @@
common:
daemonize: true
pid_dir: /var/run/
log_media: file
log_level: info
log_dir: /var/log/
working_dir: .
config_paths:
config_dir: /etc/crowdsec/
data_dir: /var/lib/crowdsec/data/
simulation_path: /etc/crowdsec/simulation.yaml
hub_dir: /etc/crowdsec/hub/
index_path: /etc/crowdsec/hub/.index.json
crowdsec_service:
acquisition_path: /etc/crowdsec/acquis.yaml
parser_routines: 1
cscli:
output: human
db_config:
log_level: info
type: sqlite
db_path: /var/lib/crowdsec/data/crowdsec.db
flush:
max_items: 5000
max_age: 7d
api:
client:
insecure_skip_verify: false
credentials_path: /etc/crowdsec/local_api_credentials.yaml
server:
log_level: info
listen_uri: 127.0.0.1:8080
profiles_path: /etc/crowdsec/profiles.yaml
online_client: # Crowdsec API credentials (to push signals and receive bad IPs)
credentials_path: /etc/crowdsec/online_api_credentials.yaml
# tls:
# cert_file: /etc/crowdsec/ssl/cert.pem
# key_file: /etc/crowdsec/ssl/key.pem
prometheus:
enabled: true
level: full
listen_addr: 127.0.0.1
listen_port: 6060

View file

@ -0,0 +1,41 @@
common:
daemonize: true
pid_dir: /var/run/
log_media: file
log_level: info
log_dir: ./
working_dir: .
config_paths:
config_dir: /etc/crowdsec/
data_dir: /var/lib/crowdsec/data/
simulation_path: /etc/crowdsec/simulation.yaml
hub_dir: /etc/crowdsec/hub/
index_path: /etc/crowdsec/hub/.index.json
cscli:
output: human
db_config:
log_level: info
type: sqlite
db_path: /var/lib/crowdsec/data/crowdsec.db
flush:
max_items: 5000
max_age: 7d
api:
client:
insecure_skip_verify: false
credentials_path: /etc/crowdsec/local_api_credentials.yaml
server:
log_level: info
listen_uri: 127.0.0.1:8080
profiles_path: /etc/crowdsec/profiles.yaml
online_client: # Crowdsec API credentials (to push signals and receive bad IPs)
credentials_path: /etc/crowdsec/online_api_credentials.yaml
# tls:
# cert_file: /etc/crowdsec/ssl/cert.pem
# key_file: /etc/crowdsec/ssl/key.pem
prometheus:
enabled: true
level: full
listen_addr: 127.0.0.1
listen_port: 6060

View file

@ -0,0 +1,38 @@
common:
daemonize: true
pid_dir: /var/run/
log_media: file
log_level: info
log_dir: ./
working_dir: .
config_paths:
config_dir: /etc/crowdsec/
data_dir: /var/lib/crowdsec/data/
simulation_path: /etc/crowdsec/simulation.yaml
hub_dir: /etc/crowdsec/hub/
index_path: /etc/crowdsec/hub/.index.json
crowdsec_service:
acquisition_path: /etc/crowdsec/acquis.yaml
parser_routines: 1
cscli:
output: human
db_config:
log_level: info
type: sqlite
db_path: /var/lib/crowdsec/data/crowdsec.db
flush:
max_items: 5000
max_age: 7d
api:
client:
insecure_skip_verify: false
credentials_path: /etc/crowdsec/local_api_credentials.yaml
server:
log_level: info
listen_uri: 127.0.0.1:8080
profiles_path: /etc/crowdsec/profiles.yaml
prometheus:
enabled: true
level: full
listen_addr: 127.0.0.1
listen_port: 6060

View file

@ -0,0 +1,34 @@
common:
daemonize: true
pid_dir: /var/run/
log_media: file
log_level: info
log_dir: ./
working_dir: .
config_paths:
config_dir: /etc/crowdsec/
data_dir: /var/lib/crowdsec/data/
simulation_path: /etc/crowdsec/simulation.yaml
hub_dir: /etc/crowdsec/hub/
index_path: /etc/crowdsec/hub/.index.json
crowdsec_service:
acquisition_path: /etc/crowdsec/acquis.yaml
parser_routines: 1
cscli:
output: human
db_config:
log_level: info
type: sqlite
db_path: /var/lib/crowdsec/data/crowdsec.db
flush:
max_items: 5000
max_age: 7d
api:
client:
insecure_skip_verify: false
credentials_path: /etc/crowdsec/local_api_credentials.yaml
prometheus:
enabled: true
level: full
listen_addr: 127.0.0.1
listen_port: 6060

View file

@ -0,0 +1,15 @@
[Unit]
Description=Crowdsec agent
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=notify
Environment=LC_ALL=C LANG=C
PIDFile=/var/run/crowdsec.pid
ExecStartPre=/usr/local/bin/crowdsec -c /etc/crowdsec/config.yaml -t
ExecStart=/usr/local/bin/crowdsec -c /etc/crowdsec/config.yaml
#ExecStartPost=/bin/sleep 0.1
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,15 @@
[Unit]
Description=Crowdsec agent
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=notify
Environment=LC_ALL=C LANG=C
PIDFile=/var/run/crowdsec.pid
ExecStartPre=/usr/local/bin/crowdsec -c /etc/crowdsec/config.yaml -t
ExecStart=/usr/local/bin/crowdsec -c /etc/crowdsec/config.yaml -no-cs
#ExecStartPost=/bin/sleep 0.1
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,15 @@
[Unit]
Description=Crowdsec agent
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=notify
Environment=LC_ALL=C LANG=C
PIDFile=/var/run/crowdsec.pid
ExecStartPre=/usr/local/bin/crowdsec -c /etc/crowdsec/config.yaml -t
ExecStart=/usr/local/bin/crowdsec -c /etc/crowdsec/config.yaml -no-api
#ExecStartPost=/bin/sleep 0.1
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

View file

@ -13,7 +13,7 @@ JQ="jq -e"
SYSTEMCTL="sudo systemctl --no-pager" SYSTEMCTL="sudo systemctl --no-pager"
CROWDSEC="sudo crowdsec" CROWDSEC="sudo crowdsec"
CROWDSEC_PROCESS="crowdsec"
# helpers # helpers
function fail { function fail {
echo "ACTION FAILED, STOP : $@" echo "ACTION FAILED, STOP : $@"

View file

@ -5,9 +5,13 @@ source tests_base.sh
##########################
## TEST AGENT/LAPI/CAPI ##
echo "CROWDSEC (AGENT+LAPI+CAPI)"
## status / start / stop ## status / start / stop
# service should be up # service should be up
pidof crowdsec || fail "crowdsec process shouldn't be running" pidof crowdsec || fail "crowdsec process should be running"
${SYSTEMCTL} status crowdsec || fail "systemctl status crowdsec failed" ${SYSTEMCTL} status crowdsec || fail "systemctl status crowdsec failed"
#shut it down #shut it down
@ -18,43 +22,125 @@ pidof crowdsec && fail "crowdsec process shouldn't be running"
#start it again #start it again
${SYSTEMCTL} start crowdsec || fail "failed to stop service" ${SYSTEMCTL} start crowdsec || fail "failed to stop service"
${SYSTEMCTL} status crowdsec || fail "crowdsec should be down" ${SYSTEMCTL} status crowdsec || fail "crowdsec should be down"
pidof crowdsec || fail "crowdsec process shouldn't be running" pidof crowdsec || fail "crowdsec process should be running"
#restart it #restart it
${SYSTEMCTL} restart crowdsec || fail "failed to stop service" ${SYSTEMCTL} restart crowdsec || fail "failed to stop service"
${SYSTEMCTL} status crowdsec || fail "crowdsec should be down" ${SYSTEMCTL} status crowdsec || fail "crowdsec should be down"
pidof crowdsec || fail "crowdsec process shouldn't be running" pidof crowdsec || fail "crowdsec process should be running"
## version ## version
${CSCLI} version || fail "cannot run cscli version" ${CSCLI} version || fail "cannot run cscli version"
## alerts ## alerts
# alerts list at startup should just return one entry : comunity pull # alerts list at startup should just return one entry : comunity pull
sleep 5 sleep 5
${CSCLI} alerts list -ojson | ${JQ} '. | length >= 1' || fail "expected at least one entry from cscli alerts list" ${CSCLI} alerts list -ojson | ${JQ} '. | length >= 1' || fail "expected at least one entry from cscli alerts list"
## capi ## capi
${CSCLI} capi status || fail "capi status should be ok" ${CSCLI} capi status || fail "capi status should be ok"
## config ## config
${CSCLI} config show || fail "failed to show config" ${CSCLI} config show || fail "failed to show config"
${CSCLI} config backup ./test || fail "failed to backup config" ${CSCLI} config backup ./test || fail "failed to backup config"
sudo rm -rf ./test
## lapi ## lapi
${CSCLI} lapi status || fail "lapi status failed" ${CSCLI} lapi status || fail "lapi status failed"
## metrics ## metrics
${CSCLI} metrics || fail "failed to get metrics" ${CSCLI} metrics || fail "failed to get metrics"
${SYSTEMCTL} stop crowdsec || fail "crowdsec should be down"
#######################
## TEST WITHOUT LAPI ##
echo "CROWDSEC (AGENT)"
# test with -no-api flag
sudo cp ./systemd/crowdsec_no_lapi.service /etc/systemd/system/crowdsec.service
${SYSTEMCTL} daemon-reload
${SYSTEMCTL} start crowdsec
sleep 1
pidof crowdsec && fail "crowdsec shouldn't run without LAPI (in flag)"
${SYSTEMCTL} stop crowdsec
sudo cp ./systemd/crowdsec.service /etc/systemd/system/crowdsec.service
${SYSTEMCTL} daemon-reload
# test with no api server in configuration file
sudo cp ./config/config_no_lapi.yaml /etc/crowdsec/config.yaml
${SYSTEMCTL} start crowdsec
sleep 1
pidof crowdsec && fail "crowdsec agent should not run without lapi (in configuration file)"
##### cscli test ####
## capi
${CSCLI} -c ./config/config_no_lapi.yaml capi status && fail "capi status shouldn't be ok"
## config
${CSCLI_BIN} -c ./config/config_no_lapi.yaml config show || fail "failed to show config"
${CSCLI} -c ./config/config_no_lapi.yaml config backup ./test || fail "failed to backup config"
sudo rm -rf ./test
## lapi
${CSCLI} -c ./config/config_no_lapi.yaml lapi status && fail "lapi status should not be ok" ## if lapi status success, it means that the test fail
## metrics
${CSCLI_BIN} -c ./config/config_no_lapi.yaml metrics
${SYSTEMCTL} stop crowdsec
sudo cp ./config/config.yaml /etc/crowdsec/config.yaml
########################
## TEST WITHOUT AGENT ##
echo "CROWDSEC (LAPI+CAPI)"
# test with -no-cs flag
sudo cp ./systemd/crowdsec_no_agent.service /etc/systemd/system/crowdsec.service
${SYSTEMCTL} daemon-reload
${SYSTEMCTL} start crowdsec
pidof crowdsec || fail "crowdsec LAPI should run without agent (in flag)"
${SYSTEMCTL} stop crowdsec
sudo cp ./systemd/crowdsec.service /etc/systemd/system/crowdsec.service
${SYSTEMCTL} daemon-reload
# test with no crowdsec agent in configuration file
sudo cp ./config/config_no_agent.yaml /etc/crowdsec/config.yaml
${SYSTEMCTL} start crowdsec
pidof crowdsec || fail "crowdsec LAPI should run without agent (in configuration file)"
## capi
${CSCLI} -c ./config/config_no_agent.yaml capi status || fail "capi status should be ok"
## config
${CSCLI_BIN} -c ./config/config_no_agent.yaml config show || fail "failed to show config"
${CSCLI} -c ./config/config_no_agent.yaml config backup ./test || fail "failed to backup config"
sudo rm -rf ./test
## lapi
${CSCLI} -c ./config/config_no_agent.yaml lapi status || fail "lapi status failed"
## metrics
${CSCLI_BIN} -c ./config/config_no_agent.yaml metrics || fail "failed to get metrics"
${SYSTEMCTL} stop crowdsec
sudo cp ./config/config.yaml /etc/crowdsec/config.yaml
#######################
## TEST WITHOUT CAPI ##
echo "CROWDSEC (AGENT+LAPI)"
# test with no online client in configuration file
sudo cp ./config/config_no_capi.yaml /etc/crowdsec/config.yaml
${SYSTEMCTL} start crowdsec
pidof crowdsec || fail "crowdsec LAPI should run without CAPI (in configuration file)"
## capi
${CSCLI} -c ./config/config_no_capi.yaml capi status && fail "capi status should not be ok" ## if capi status success, it means that the test fail
## config
${CSCLI_BIN} -c ./config/config_no_capi.yaml config show || fail "failed to show config"
${CSCLI} -c ./config/config_no_capi.yaml config backup ./test || fail "failed to backup config"
sudo rm -rf ./test
## lapi
${CSCLI} -c ./config/config_no_capi.yaml lapi status || fail "lapi status failed"
## metrics
${CSCLI_BIN} -c ./config/config_no_capi.yaml metrics || fail "failed to get metrics"
sudo cp ./config/config.yaml /etc/crowdsec/config.yaml
${SYSTEMCTL} restart crowdsec

View file

@ -5,12 +5,12 @@ source tests_base.sh
## collections ## collections
${CSCLI} collections list || fail "failed to list collections" ${CSCLI_BIN} collections list || fail "failed to list collections"
BASE_COLLECTION_COUNT=2 BASE_COLLECTION_COUNT=2
# we expect 1 collections : linux # we expect 1 collections : linux
${CSCLI} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(first) expected exactly ${BASE_COLLECTION_COUNT} collection" ${CSCLI_BIN} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(first) expected exactly ${BASE_COLLECTION_COUNT} collection"
# install an extra collection # install an extra collection
${CSCLI} collections install crowdsecurity/mysql || fail "failed to install collection" ${CSCLI} collections install crowdsecurity/mysql || fail "failed to install collection"
@ -18,7 +18,7 @@ ${CSCLI} collections install crowdsecurity/mysql || fail "failed to install coll
BASE_COLLECTION_COUNT=$(($BASE_COLLECTION_COUNT+1)) BASE_COLLECTION_COUNT=$(($BASE_COLLECTION_COUNT+1))
# we should now have 2 collections :) # we should now have 2 collections :)
${CSCLI} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(post install) expected exactly ${BASE_COLLECTION_COUNT} collection" ${CSCLI_BIN} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(post install) expected exactly ${BASE_COLLECTION_COUNT} collection"
# remove the collection # remove the collection
${CSCLI} collections remove crowdsecurity/mysql || fail "failed to remove collection" ${CSCLI} collections remove crowdsecurity/mysql || fail "failed to remove collection"
@ -26,5 +26,5 @@ ${CSCLI} collections remove crowdsecurity/mysql || fail "failed to remove collec
BASE_COLLECTION_COUNT=$(($BASE_COLLECTION_COUNT-1)) BASE_COLLECTION_COUNT=$(($BASE_COLLECTION_COUNT-1))
# we expect 1 collections : linux # we expect 1 collections : linux
${CSCLI} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(post remove) expected exactly ${BASE_COLLECTION_COUNT} collection" ${CSCLI_BIN} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(post remove) expected exactly ${BASE_COLLECTION_COUNT} collection"