浏览代码

Allow for acquisition files to be specified from a directory as well (#619)

* allow a acquisition_dir in crowdsec's config + change the behaviour of config loading so that it's working with a list instead. keep backward compat with acquisition_path

* remove the default behaviour of 'guessing' acquis path if param isn't present, and error
Thibault "bui" Koechlin 4 年之前
父节点
当前提交
22ada59393

+ 72 - 1
cmd/crowdsec-cli/config.go

@@ -55,13 +55,40 @@ func backupConfigToDirectory(dirPath string) error {
 		}
 		}
 		log.Infof("Saved simulation to %s", backupSimulation)
 		log.Infof("Saved simulation to %s", backupSimulation)
 	}
 	}
+
+	/*
+	   - backup AcquisitionFilePath
+	   - backup the other files of acquisition directory
+	*/
 	if csConfig.Crowdsec != nil && csConfig.Crowdsec.AcquisitionFilePath != "" {
 	if csConfig.Crowdsec != nil && csConfig.Crowdsec.AcquisitionFilePath != "" {
 		backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
 		backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
 		if err = types.CopyFile(csConfig.Crowdsec.AcquisitionFilePath, backupAcquisition); err != nil {
 		if err = types.CopyFile(csConfig.Crowdsec.AcquisitionFilePath, backupAcquisition); err != nil {
 			return fmt.Errorf("failed copy %s to %s : %s", csConfig.Crowdsec.AcquisitionFilePath, backupAcquisition, err)
 			return fmt.Errorf("failed copy %s to %s : %s", csConfig.Crowdsec.AcquisitionFilePath, backupAcquisition, err)
 		}
 		}
-		log.Infof("Saved acquis to %s", backupAcquisition)
 	}
 	}
+
+	acquisBackupDir := dirPath + "/acquis/"
+	if err = os.Mkdir(acquisBackupDir, 0600); err != nil {
+		return fmt.Errorf("error while creating %s : %s", acquisBackupDir, err)
+	}
+
+	if csConfig.Crowdsec != nil && len(csConfig.Crowdsec.AcquisitionFiles) > 0 {
+		for _, acquisFile := range csConfig.Crowdsec.AcquisitionFiles {
+			/*if it was the default one, it was already backup'ed*/
+			if csConfig.Crowdsec.AcquisitionFilePath == acquisFile {
+				continue
+			}
+			targetFname, err := filepath.Abs(acquisBackupDir + filepath.Base(acquisFile))
+			if err != nil {
+				return errors.Wrapf(err, "while saving %s to %s", acquisFile, acquisBackupDir)
+			}
+			if err = types.CopyFile(acquisFile, targetFname); err != nil {
+				return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
+			}
+			log.Infof("Saved acquis %s to %s", acquisFile, targetFname)
+		}
+	}
+
 	if ConfigFilePath != "" {
 	if ConfigFilePath != "" {
 		backupMain := fmt.Sprintf("%s/config.yaml", dirPath)
 		backupMain := fmt.Sprintf("%s/config.yaml", dirPath)
 		if err = types.CopyFile(ConfigFilePath, backupMain); err != nil {
 		if err = types.CopyFile(ConfigFilePath, backupMain); err != nil {
@@ -186,13 +213,57 @@ func restoreConfigFromDirectory(dirPath string) error {
 		}
 		}
 	}
 	}
 
 
+	/*if there is a acquisition dir, restore its content*/
+	if csConfig.Crowdsec.AcquisitionDirPath != "" {
+		if err = os.Mkdir(csConfig.Crowdsec.AcquisitionDirPath, 0600); err != nil {
+			return fmt.Errorf("error while creating %s : %s", csConfig.Crowdsec.AcquisitionDirPath, err)
+		}
+
+	}
+
+	//if there was a single one
 	backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
 	backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
 	if _, err = os.Stat(backupAcquisition); err == nil {
 	if _, err = os.Stat(backupAcquisition); err == nil {
+		log.Debugf("restoring backup'ed %s", backupAcquisition)
 		if err = types.CopyFile(backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath); err != nil {
 		if err = types.CopyFile(backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath); err != nil {
 			return fmt.Errorf("failed copy %s to %s : %s", backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath, err)
 			return fmt.Errorf("failed copy %s to %s : %s", backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath, err)
 		}
 		}
 	}
 	}
 
 
+	//if there is files in the acquis backup dir, restore them
+	acquisBackupDir := dirPath + "/acquis/*.yaml"
+	if acquisFiles, err := filepath.Glob(acquisBackupDir); err == nil {
+		for _, acquisFile := range acquisFiles {
+			targetFname, err := filepath.Abs(csConfig.Crowdsec.AcquisitionDirPath + "/" + filepath.Base(acquisFile))
+			if err != nil {
+				return errors.Wrapf(err, "while saving %s to %s", acquisFile, targetFname)
+			}
+			log.Debugf("restoring %s to %s", acquisFile, targetFname)
+			if err = types.CopyFile(acquisFile, targetFname); err != nil {
+				return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
+			}
+		}
+	}
+
+	if csConfig.Crowdsec != nil && len(csConfig.Crowdsec.AcquisitionFiles) > 0 {
+		for _, acquisFile := range csConfig.Crowdsec.AcquisitionFiles {
+			log.Infof("backup filepath from dir -> %s", acquisFile)
+			/*if it was the default one, it was already backup'ed*/
+			if csConfig.Crowdsec.AcquisitionFilePath == acquisFile {
+				log.Infof("skip this one")
+				continue
+			}
+			targetFname, err := filepath.Abs(acquisBackupDir + filepath.Base(acquisFile))
+			if err != nil {
+				return errors.Wrapf(err, "while saving %s to %s", acquisFile, acquisBackupDir)
+			}
+			if err = types.CopyFile(acquisFile, targetFname); err != nil {
+				return fmt.Errorf("failed copy %s to %s : %s", acquisFile, targetFname, err)
+			}
+			log.Infof("Saved acquis %s to %s", acquisFile, targetFname)
+		}
+	}
+
 	if err = RestoreHub(dirPath); err != nil {
 	if err = RestoreHub(dirPath); err != nil {
 		return fmt.Errorf("failed to restore hub config : %s", err)
 		return fmt.Errorf("failed to restore hub config : %s", err)
 	}
 	}

+ 1 - 1
config/dev.yaml

@@ -11,7 +11,7 @@ config_paths:
   #hub_dir: /etc/crowdsec/hub/
   #hub_dir: /etc/crowdsec/hub/
   #index_path: ./config/hub/.index.json
   #index_path: ./config/hub/.index.json
 crowdsec_service:
 crowdsec_service:
-  #acquisition_path: ./config/acquis.yaml
+  acquisition_path: ./config/acquis.yaml
   parser_routines: 1
   parser_routines: 1
 cscli:
 cscli:
   output: human
   output: human

+ 9 - 1
docs/v1.X/docs/references/acquisition.md

@@ -1,6 +1,14 @@
 # Acquisition format
 # Acquisition format
 
 
-The `/etc/crowdsec/acquis.yaml` defines which files are read by crowdsec at runtime.
+The `crowdsec_service` section of configuration supports `acquisition_path` and `acquisition_dir` (>1.0.7).
+
+The default setting is to have `acquisition_path` pointing to `/etc/crowdsec/acquis.yaml`.
+
+`acquisition_dir` can be set to point to a directory where every `.yaml` file is considered as a valid acquisition configuration file.
+
+
+
+The acquisition file(s) define which source of information (ie. files or journald streams) are read by crowdsec at runtime.
 The file is a list of object representing groups of files to read, with the following properties.
 The file is a list of object representing groups of files to read, with the following properties.
 
 
 A least one of :
 A least one of :

+ 8 - 0
docs/v1.X/docs/references/crowdsec-config.md

@@ -24,6 +24,7 @@ config_paths:
   index_path: /etc/crowdsec/hub/.index.json
   index_path: /etc/crowdsec/hub/.index.json
 crowdsec_service:
 crowdsec_service:
   acquisition_path: /etc/crowdsec/acquis.yaml
   acquisition_path: /etc/crowdsec/acquis.yaml
+  #acquisition_dir: /etc/crowdsec/acquis/
   parser_routines: 1
   parser_routines: 1
   buckets_routines: 1
   buckets_routines: 1
   output_routines: 1
   output_routines: 1
@@ -108,6 +109,7 @@ config_paths:
   index_path: <path_to_hub_index_file>
   index_path: <path_to_hub_index_file>
 crowdsec_service:
 crowdsec_service:
   acquisition_path: <acqusition_file_path>
   acquisition_path: <acqusition_file_path>
+  acquisition_dir: <acquisition_dir_path>
   parser_routines: <number_of_parser_routines>
   parser_routines: <number_of_parser_routines>
   buckets_routines: <number_of_buckets_routines>
   buckets_routines: <number_of_buckets_routines>
   output_routines: <number_of_output_routines>
   output_routines: <number_of_output_routines>
@@ -242,6 +244,7 @@ This section is only used by crowdsec agent.
 ```yaml
 ```yaml
 crowdsec_service:
 crowdsec_service:
   acquisition_path: <acqusition_file_path>
   acquisition_path: <acqusition_file_path>
+  acquisition_dir: <acqusition_dir_path>
   parser_routines: <number_of_parser_routines>
   parser_routines: <number_of_parser_routines>
   buckets_routines: <number_of_buckets_routines>
   buckets_routines: <number_of_buckets_routines>
   output_routines: <number_of_output_routines>
   output_routines: <number_of_output_routines>
@@ -268,6 +271,11 @@ Number of dedicated goroutines for pushing data to local api.
 
 
 Path to the yaml file containing logs that needs to be read.
 Path to the yaml file containing logs that needs to be read.
 
 
+#### `acquisition_dir`
+> string
+
+(>1.0.7) Path to a directory where each yaml is considered as a acquisition configuration file containing logs that needs to be read.
+
 
 
 ### `cscli`
 ### `cscli`
 
 

+ 24 - 19
pkg/acquisition/acquisition.go

@@ -108,30 +108,35 @@ func DataSourceConfigure(config DataSourceCfg) (DataSource, error) {
 func LoadAcquisitionFromFile(config *csconfig.CrowdsecServiceCfg) ([]DataSource, error) {
 func LoadAcquisitionFromFile(config *csconfig.CrowdsecServiceCfg) ([]DataSource, error) {
 
 
 	var sources []DataSource
 	var sources []DataSource
+	var acquisSources = config.AcquisitionFiles
 
 
-	yamlFile, err := os.Open(config.AcquisitionFilePath)
-	if err != nil {
-		return nil, errors.Wrapf(err, "can't open %s", config.AcquisitionFilePath)
-	}
-	dec := yaml.NewDecoder(yamlFile)
-	dec.SetStrict(true)
-	for {
-		sub := DataSourceCfg{}
-		err = dec.Decode(&sub)
+	for _, acquisFile := range acquisSources {
+		log.Infof("loading acquisition file : %s", acquisFile)
+		yamlFile, err := os.Open(acquisFile)
 		if err != nil {
 		if err != nil {
-			if err == io.EOF {
-				log.Tracef("End of yaml file")
-				break
-			}
-			return nil, errors.Wrap(err, fmt.Sprintf("failed to yaml decode %s", config.AcquisitionFilePath))
+			return nil, errors.Wrapf(err, "can't open %s", acquisFile)
 		}
 		}
-		src, err := DataSourceConfigure(sub)
-		if err != nil {
-			log.Warningf("while configuring datasource : %s", err)
-			continue
+		dec := yaml.NewDecoder(yamlFile)
+		dec.SetStrict(true)
+		for {
+			sub := DataSourceCfg{}
+			err = dec.Decode(&sub)
+			if err != nil {
+				if err == io.EOF {
+					log.Tracef("End of yaml file")
+					break
+				}
+				return nil, errors.Wrap(err, fmt.Sprintf("failed to yaml decode %s", acquisFile))
+			}
+			src, err := DataSourceConfigure(sub)
+			if err != nil {
+				log.Warningf("while configuring datasource : %s", err)
+				continue
+			}
+			sources = append(sources, src)
 		}
 		}
-		sources = append(sources, src)
 	}
 	}
+
 	return sources, nil
 	return sources, nil
 }
 }
 
 

+ 3 - 3
pkg/acquisition/acquisition_test.go

@@ -16,19 +16,19 @@ import (
 func TestConfigLoading(t *testing.T) {
 func TestConfigLoading(t *testing.T) {
 	//bad filename
 	//bad filename
 	cfg := csconfig.CrowdsecServiceCfg{
 	cfg := csconfig.CrowdsecServiceCfg{
-		AcquisitionFilePath: "./tests/xxx.yaml",
+		AcquisitionFiles: []string{"./tests/xxx.yaml"},
 	}
 	}
 	_, err := LoadAcquisitionFromFile(&cfg)
 	_, err := LoadAcquisitionFromFile(&cfg)
 	assert.Contains(t, fmt.Sprintf("%s", err), "can't open ./tests/xxx.yaml: open ./tests/xxx.yaml: no such file or directory")
 	assert.Contains(t, fmt.Sprintf("%s", err), "can't open ./tests/xxx.yaml: open ./tests/xxx.yaml: no such file or directory")
 	//bad config file
 	//bad config file
 	cfg = csconfig.CrowdsecServiceCfg{
 	cfg = csconfig.CrowdsecServiceCfg{
-		AcquisitionFilePath: "./tests/test.log",
+		AcquisitionFiles: []string{"./tests/test.log"},
 	}
 	}
 	_, err = LoadAcquisitionFromFile(&cfg)
 	_, err = LoadAcquisitionFromFile(&cfg)
 	assert.Contains(t, fmt.Sprintf("%s", err), "failed to yaml decode ./tests/test.log: yaml: unmarshal errors")
 	assert.Contains(t, fmt.Sprintf("%s", err), "failed to yaml decode ./tests/test.log: yaml: unmarshal errors")
 	//correct config file
 	//correct config file
 	cfg = csconfig.CrowdsecServiceCfg{
 	cfg = csconfig.CrowdsecServiceCfg{
-		AcquisitionFilePath: "./tests/acquis_test.yaml",
+		AcquisitionFiles: []string{"./tests/acquis_test.yaml"},
 	}
 	}
 	srcs, err := LoadAcquisitionFromFile(&cfg)
 	srcs, err := LoadAcquisitionFromFile(&cfg)
 	if err != nil {
 	if err != nil {

+ 26 - 2
pkg/csconfig/config.go

@@ -83,9 +83,24 @@ func (c *GlobalConfig) LoadConfiguration() error {
 	}
 	}
 
 
 	if c.Crowdsec != nil {
 	if c.Crowdsec != nil {
-		if c.Crowdsec.AcquisitionFilePath == "" {
-			c.Crowdsec.AcquisitionFilePath = filepath.Clean(c.ConfigPaths.ConfigDir + "/acquis.yaml")
+		if c.Crowdsec.AcquisitionFilePath != "" {
+			log.Infof("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.ConfigDir = c.ConfigPaths.ConfigDir
 		c.Crowdsec.DataDir = c.ConfigPaths.DataDir
 		c.Crowdsec.DataDir = c.ConfigPaths.DataDir
 		c.Crowdsec.HubDir = c.ConfigPaths.HubDir
 		c.Crowdsec.HubDir = c.ConfigPaths.HubDir
@@ -276,6 +291,9 @@ func (c *GlobalConfig) CleanupPaths() error {
 			&c.Common.WorkingDir,
 			&c.Common.WorkingDir,
 		}
 		}
 		for _, k := range CommonCleanup {
 		for _, k := range CommonCleanup {
+			if *k == "" {
+				continue
+			}
 			*k, err = filepath.Abs(*k)
 			*k, err = filepath.Abs(*k)
 			if err != nil {
 			if err != nil {
 				return errors.Wrap(err, "failed to clean path")
 				return errors.Wrap(err, "failed to clean path")
@@ -288,6 +306,9 @@ func (c *GlobalConfig) CleanupPaths() error {
 			&c.Crowdsec.AcquisitionFilePath,
 			&c.Crowdsec.AcquisitionFilePath,
 		}
 		}
 		for _, k := range crowdsecCleanup {
 		for _, k := range crowdsecCleanup {
+			if *k == "" {
+				continue
+			}
 			*k, err = filepath.Abs(*k)
 			*k, err = filepath.Abs(*k)
 			if err != nil {
 			if err != nil {
 				return errors.Wrap(err, "failed to clean path")
 				return errors.Wrap(err, "failed to clean path")
@@ -304,6 +325,9 @@ func (c *GlobalConfig) CleanupPaths() error {
 			&c.ConfigPaths.SimulationFilePath,
 			&c.ConfigPaths.SimulationFilePath,
 		}
 		}
 		for _, k := range configPathsCleanup {
 		for _, k := range configPathsCleanup {
+			if *k == "" {
+				continue
+			}
 			*k, err = filepath.Abs(*k)
 			*k, err = filepath.Abs(*k)
 			if err != nil {
 			if err != nil {
 				return errors.Wrap(err, "failed to clean path")
 				return errors.Wrap(err, "failed to clean path")

+ 4 - 1
pkg/csconfig/crowdsec_service.go

@@ -2,7 +2,10 @@ package csconfig
 
 
 /*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"`
+	AcquisitionDirPath  string `yaml:"acquisition_dir,omitempty"`
+
+	AcquisitionFiles     []string          `yaml:"-"`
 	ParserRoutinesCount  int               `yaml:"parser_routines"`
 	ParserRoutinesCount  int               `yaml:"parser_routines"`
 	BucketsRoutinesCount int               `yaml:"buckets_routines"`
 	BucketsRoutinesCount int               `yaml:"buckets_routines"`
 	OutputRoutinesCount  int               `yaml:"output_routines"`
 	OutputRoutinesCount  int               `yaml:"output_routines"`

+ 0 - 0
pkg/csconfig/tests/acquis.yaml


+ 1 - 1
pkg/csconfig/tests/config.yaml

@@ -8,7 +8,7 @@ prometheus:
   enabled: true
   enabled: true
   level: full
   level: full
 crowdsec_service:
 crowdsec_service:
-#  acquisition_path: ./config/acquis.yaml
+  acquisition_path: ./tests/acquis.yaml
   parser_routines: 1
   parser_routines: 1
 cscli:
 cscli:
   output: human
   output: human