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
This commit is contained in:
Thibault "bui" Koechlin 2021-02-17 13:55:36 +01:00 committed by GitHub
parent 7d93302e05
commit 22ada59393
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 148 additions and 29 deletions

View file

@ -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)
} }

View file

@ -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

View file

@ -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 :

View file

@ -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`

View file

@ -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) for _, acquisFile := range acquisSources {
if err != nil { log.Infof("loading acquisition file : %s", acquisFile)
return nil, errors.Wrapf(err, "can't open %s", config.AcquisitionFilePath) yamlFile, err := os.Open(acquisFile)
}
dec := yaml.NewDecoder(yamlFile)
dec.SetStrict(true)
for {
sub := DataSourceCfg{}
err = dec.Decode(&sub)
if err != nil { if err != nil {
if err == io.EOF { return nil, errors.Wrapf(err, "can't open %s", acquisFile)
log.Tracef("End of yaml file") }
break 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))
} }
return nil, errors.Wrap(err, fmt.Sprintf("failed to yaml decode %s", config.AcquisitionFilePath)) src, err := DataSourceConfigure(sub)
if err != nil {
log.Warningf("while configuring datasource : %s", err)
continue
}
sources = append(sources, src)
} }
src, err := DataSourceConfigure(sub)
if err != nil {
log.Warningf("while configuring datasource : %s", err)
continue
}
sources = append(sources, src)
} }
return sources, nil return sources, nil
} }

View file

@ -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 {

View file

@ -83,9 +83,24 @@ func (c *GlobalConfig) LoadConfiguration() error {
} }
if c.Crowdsec != nil { if c.Crowdsec != nil {
if c.Crowdsec.AcquisitionFilePath == "" { if c.Crowdsec.AcquisitionFilePath != "" {
c.Crowdsec.AcquisitionFilePath = filepath.Clean(c.ConfigPaths.ConfigDir + "/acquis.yaml") 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")

View file

@ -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"`

View file

View file

@ -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