package csconfig import ( "fmt" "os" "path/filepath" "strings" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" "github.com/crowdsecurity/go-cs-lib/ptr" ) type ContextToSend struct { SourceFile string `yaml:"filepath" json:"filepath"` Context map[string][]string `yaml:"context" json:"context"` } // CrowdsecServiceCfg contains the location of parsers/scenarios/... and acquisition files type CrowdsecServiceCfg struct { Enable *bool `yaml:"enable"` AcquisitionFilePath string `yaml:"acquisition_path,omitempty"` AcquisitionDirPath string `yaml:"acquisition_dir,omitempty"` ContextPath string `yaml:"console_context_path"` ContextDir string `yaml:"console_context_dir"` ContextValueLength int `yaml:"console_context_value_length"` AcquisitionFiles []string `yaml:"-"` ParserRoutinesCount int `yaml:"parser_routines"` BucketsRoutinesCount int `yaml:"buckets_routines"` OutputRoutinesCount int `yaml:"output_routines"` SimulationConfig *SimulationConfig `yaml:"-"` LintOnly bool `yaml:"-"` // if set to true, exit after loading configs BucketStateFile string `yaml:"state_input_file,omitempty"` // if we need to unserialize buckets at start BucketStateDumpDir string `yaml:"state_output_dir,omitempty"` // if we need to unserialize buckets on shutdown BucketsGCEnabled bool `yaml:"-"` // we need to garbage collect buckets when in forensic mode HubDir string `yaml:"-"` DataDir string `yaml:"-"` ConfigDir string `yaml:"-"` HubIndexFile string `yaml:"-"` SimulationFilePath string `yaml:"-"` ContextToSend []ContextToSend `yaml:"-"` } func (c *Config) LoadCrowdsec() error { var err error if c.Crowdsec == nil { log.Warning("crowdsec agent is disabled") c.DisableAgent = true return nil } if c.Crowdsec.Enable == nil { // if the option is not present, it is enabled by default c.Crowdsec.Enable = ptr.Of(true) } if !*c.Crowdsec.Enable { log.Warning("crowdsec agent is disabled") c.DisableAgent = true return nil } if c.Crowdsec.AcquisitionFiles == nil { c.Crowdsec.AcquisitionFiles = []string{} } if c.Crowdsec.AcquisitionFilePath != "" { log.Debugf("non-empty acquisition_path %s", c.Crowdsec.AcquisitionFilePath) if _, err = os.Stat(c.Crowdsec.AcquisitionFilePath); err != nil { return fmt.Errorf("while checking acquisition_path: %w", err) } 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 fmt.Errorf("can't get absolute path of '%s': %w", c.Crowdsec.AcquisitionDirPath, err) } var files []string files, err = filepath.Glob(c.Crowdsec.AcquisitionDirPath + "/*.yaml") if err != nil { return fmt.Errorf("while globbing acquis_dir: %w", err) } c.Crowdsec.AcquisitionFiles = append(c.Crowdsec.AcquisitionFiles, files...) files, err = filepath.Glob(c.Crowdsec.AcquisitionDirPath + "/*.yml") if err != nil { return fmt.Errorf("while globbing acquis_dir: %w", err) } c.Crowdsec.AcquisitionFiles = append(c.Crowdsec.AcquisitionFiles, files...) } if c.Crowdsec.AcquisitionDirPath == "" && c.Crowdsec.AcquisitionFilePath == "" { log.Warning("no acquisition_path or acquisition_dir specified") } if len(c.Crowdsec.AcquisitionFiles) == 0 { log.Warning("no acquisition file found") } if err = c.LoadSimulation(); err != nil { return fmt.Errorf("load error (simulation): %w", err) } 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 fmt.Errorf("failed to get absolute path of '%s': %w", *k, err) } } // Convert relative paths to absolute paths for i, file := range c.Crowdsec.AcquisitionFiles { f, err := filepath.Abs(file) if err != nil { return fmt.Errorf("failed to get absolute path of '%s': %w", file, err) } c.Crowdsec.AcquisitionFiles[i] = f } if err := c.LoadAPIClient(); err != nil { return fmt.Errorf("loading api client: %s", err) } if err := c.LoadHub(); err != nil { return fmt.Errorf("while loading hub: %w", err) } c.Crowdsec.ContextToSend = make([]ContextToSend, 0) fallback := false if c.Crowdsec.ContextPath == "" { // fallback to default config file c.Crowdsec.ContextPath = filepath.Join(c.Crowdsec.ConfigDir, "console", "context.yaml") fallback = true } f, err := filepath.Abs(c.Crowdsec.ContextPath) if err != nil { return fmt.Errorf("fail to get absolute path of %s: %s", c.Crowdsec.ContextPath, err) } c.Crowdsec.ContextPath = f yamlFile, err := os.ReadFile(c.Crowdsec.ContextPath) if err != nil { if fallback { log.Debugf("Default context config file doesn't exist, will not use it") } else { return fmt.Errorf("failed to open context file: %s", err) } } else { ctxToSend := ContextToSend{ SourceFile: c.Crowdsec.ContextPath, Context: make(map[string][]string), } err = yaml.Unmarshal(yamlFile, ctxToSend.Context) if err != nil { return fmt.Errorf("unmarshaling context config file '%s': %s", c.Crowdsec.ContextPath, err) } c.Crowdsec.ContextToSend = append(c.Crowdsec.ContextToSend, ctxToSend) } if c.Crowdsec.ContextDir == "" { // fallback to default config file c.Crowdsec.ContextDir = filepath.Join(c.Crowdsec.ConfigDir, "context") fallback = true } // if context folder exist, try to read it if _, err := os.Stat(c.Crowdsec.ContextDir); !os.IsNotExist(err) { err := filepath.Walk(c.Crowdsec.ContextDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !strings.HasSuffix(path, ".yaml") && !strings.HasSuffix(path, ".yml") { return nil } f, err := filepath.Abs(path) if err != nil { return fmt.Errorf("fail to get absolute path of %s: %s", path, err) } yamlFile, err := os.ReadFile(f) if err != nil { return fmt.Errorf("unable to read context file '%s': %s", f, err.Error()) } ctxToSend := ContextToSend{ SourceFile: f, Context: make(map[string][]string), } err = yaml.Unmarshal(yamlFile, ctxToSend.Context) if err != nil { return fmt.Errorf("unable to unmarshal context file '%s': %s", f, err) } log.Debugf("Reading %s context file: %+v", f, string(yamlFile)) c.Crowdsec.ContextToSend = append(c.Crowdsec.ContextToSend, ctxToSend) return nil }) if err != nil { return fmt.Errorf("unable to list context directory: %s", err.Error()) } } return nil } func (c *CrowdsecServiceCfg) DumpContextConfigFile(contextToSend ContextToSend) error { var out []byte var err error if out, err = yaml.Marshal(contextToSend.Context); err != nil { return fmt.Errorf("while marshaling ConsoleConfig (for %s): %w", contextToSend.SourceFile, err) } if err := os.WriteFile(contextToSend.SourceFile, out, 0600); err != nil { return fmt.Errorf("while dumping console config to %s: %w", contextToSend.SourceFile, err) } log.Infof("%s file saved", contextToSend.SourceFile) return nil }