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)
}
/*
- backup AcquisitionFilePath
- backup the other files of acquisition directory
*/
if csConfig.Crowdsec != nil && csConfig.Crowdsec.AcquisitionFilePath != "" {
backupAcquisition := fmt.Sprintf("%s/acquis.yaml", dirPath)
if err = types.CopyFile(csConfig.Crowdsec.AcquisitionFilePath, backupAcquisition); err != nil {
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 != "" {
backupMain := fmt.Sprintf("%s/config.yaml", dirPath)
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)
if _, err = os.Stat(backupAcquisition); err == nil {
log.Debugf("restoring backup'ed %s", backupAcquisition)
if err = types.CopyFile(backupAcquisition, csConfig.Crowdsec.AcquisitionFilePath); err != nil {
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 {
return fmt.Errorf("failed to restore hub config : %s", err)
}

View file

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

View file

@ -1,6 +1,14 @@
# 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.
A least one of :

View file

@ -24,6 +24,7 @@ config_paths:
index_path: /etc/crowdsec/hub/.index.json
crowdsec_service:
acquisition_path: /etc/crowdsec/acquis.yaml
#acquisition_dir: /etc/crowdsec/acquis/
parser_routines: 1
buckets_routines: 1
output_routines: 1
@ -108,6 +109,7 @@ config_paths:
index_path: <path_to_hub_index_file>
crowdsec_service:
acquisition_path: <acqusition_file_path>
acquisition_dir: <acquisition_dir_path>
parser_routines: <number_of_parser_routines>
buckets_routines: <number_of_buckets_routines>
output_routines: <number_of_output_routines>
@ -242,6 +244,7 @@ This section is only used by crowdsec agent.
```yaml
crowdsec_service:
acquisition_path: <acqusition_file_path>
acquisition_dir: <acqusition_dir_path>
parser_routines: <number_of_parser_routines>
buckets_routines: <number_of_buckets_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.
#### `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`

View file

@ -108,30 +108,35 @@ func DataSourceConfigure(config DataSourceCfg) (DataSource, error) {
func LoadAcquisitionFromFile(config *csconfig.CrowdsecServiceCfg) ([]DataSource, error) {
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 == io.EOF {
log.Tracef("End of yaml file")
break
return nil, errors.Wrapf(err, "can't open %s", acquisFile)
}
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
}

View file

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

View file

@ -83,9 +83,24 @@ func (c *GlobalConfig) LoadConfiguration() error {
}
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.DataDir = c.ConfigPaths.DataDir
c.Crowdsec.HubDir = c.ConfigPaths.HubDir
@ -276,6 +291,9 @@ func (c *GlobalConfig) CleanupPaths() error {
&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")
@ -288,6 +306,9 @@ func (c *GlobalConfig) CleanupPaths() error {
&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")
@ -304,6 +325,9 @@ func (c *GlobalConfig) CleanupPaths() error {
&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")

View file

@ -2,7 +2,10 @@ package csconfig
/*Configurations needed for crowdsec to load parser/scenarios/... + acquisition*/
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"`
BucketsRoutinesCount int `yaml:"buckets_routines"`
OutputRoutinesCount int `yaml:"output_routines"`

View file

View file

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