diff --git a/cmd/crowdsec-cli/console.go b/cmd/crowdsec-cli/console.go index 342fbd68b..843bba690 100644 --- a/cmd/crowdsec-cli/console.go +++ b/cmd/crowdsec-cli/console.go @@ -213,6 +213,12 @@ Disable given information push to the central API.`, activated = string(emoji.CheckMarkButton) } table.Append([]string{option, activated, "Send alerts from tainted scenarios to the console"}) + case csconfig.SEND_LABEL: + activated := string(emoji.CrossMark) + if *csConfig.API.Server.ConsoleConfig.ShareLabel { + activated = string(emoji.CheckMarkButton) + } + table.Append([]string{option, activated, "Send label with alerts to the console"}) } } table.Render() @@ -233,6 +239,7 @@ Disable given information push to the central API.`, {"share_manual_decisions", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareManualDecisions)}, {"share_custom", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareCustomScenarios)}, {"share_tainted", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareTaintedScenarios)}, + {"share_labels", fmt.Sprintf("%t", *csConfig.API.Server.ConsoleConfig.ShareLabel)}, } for _, row := range rows { err = csvwriter.Write(row) @@ -246,6 +253,46 @@ Disable given information push to the central API.`, } cmdConsole.AddCommand(cmdConsoleStatus) + + cmdLabel := &cobra.Command{ + Use: "label [feature-flag]", + Short: "Manage label to send with alerts", + DisableAutoGenTag: true, + Run: func(cmd *cobra.Command, args []string) { + printHelp(cmd) + }, + } + + var key string + var values []string + cmdLabelAdd := &cobra.Command{ + Use: "add", + Short: "Add label to send with alerts", + DisableAutoGenTag: true, + Run: func(cmd *cobra.Command, args []string) { + if _, ok := csConfig.API.Server.ConsoleConfig.LabelsToSend[key]; !ok { + csConfig.API.Server.ConsoleConfig.LabelsToSend[key] = make([]string, 0) + } + data := csConfig.API.Server.ConsoleConfig.LabelsToSend[key] + for _, val := range values { + if !inSlice(val, data) { + data = append(data, val) + } + csConfig.API.Server.ConsoleConfig.LabelsToSend[key] = data + } + if err := csConfig.API.Server.DumpLabelConfigFile(); err != nil { + log.Fatalf(err.Error()) + } + }, + } + cmdLabelAdd.Flags().StringVarP(&key, "key", "k", "", "The key of the different values to send") + cmdLabelAdd.Flags().StringSliceVar(&values, "value", []string{}, "The expr fields to associate with the key") + cmdLabelAdd.MarkFlagRequired("key") + cmdLabelAdd.MarkFlagRequired("value") + cmdLabel.AddCommand(cmdLabelAdd) + + cmdConsole.AddCommand(cmdLabel) + return cmdConsole } @@ -291,6 +338,19 @@ func SetConsoleOpts(args []string, wanted bool) { log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted) csConfig.API.Server.ConsoleConfig.ShareManualDecisions = types.BoolPtr(wanted) } + case csconfig.SEND_LABEL: + /*for each flag check if it's already set before setting it*/ + if csConfig.API.Server.ConsoleConfig.ShareLabel != nil { + if *csConfig.API.Server.ConsoleConfig.ShareLabel == wanted { + log.Infof("%s already set to %t", csconfig.SEND_LABEL, wanted) + } else { + log.Infof("%s set to %t", csconfig.SEND_LABEL, wanted) + *csConfig.API.Server.ConsoleConfig.ShareLabel = wanted + } + } else { + log.Infof("%s set to %t", csconfig.SEND_LABEL, wanted) + csConfig.API.Server.ConsoleConfig.ShareLabel = types.BoolPtr(wanted) + } default: log.Fatalf("unknown flag %s", arg) } diff --git a/config/console.yaml b/config/console.yaml index e83658d7b..77c86b000 100644 --- a/config/console.yaml +++ b/config/console.yaml @@ -1,3 +1,4 @@ share_manual_decisions: false share_custom: true share_tainted: true +share_labels: false \ No newline at end of file diff --git a/config/labels.yaml b/config/labels.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/debian/rules b/debian/rules index 5b956ccd7..ca6823f24 100755 --- a/debian/rules +++ b/debian/rules @@ -27,6 +27,7 @@ override_dh_auto_install: mkdir -p debian/crowdsec/etc/crowdsec mkdir -p debian/crowdsec/usr/share/crowdsec mkdir -p debian/crowdsec/etc/crowdsec/hub/ + mkdir -p debian/crowdsec/etc/crowdsec/console/ mkdir -p debian/crowdsec/usr/share/crowdsec/config @@ -45,5 +46,6 @@ override_dh_auto_install: cp config/simulation.yaml debian/crowdsec/etc/crowdsec/simulation.yaml cp config/profiles.yaml debian/crowdsec/etc/crowdsec/profiles.yaml cp config/console.yaml debian/crowdsec/etc/crowdsec/console.yaml + cp config/labels.yaml debian/crowdsec/etc/crowdsec/console/labels.yaml cp -a config/patterns debian/crowdsec/etc/crowdsec diff --git a/pkg/csconfig/console.go b/pkg/csconfig/console.go index f885f2dc8..d0306a82f 100644 --- a/pkg/csconfig/console.go +++ b/pkg/csconfig/console.go @@ -15,25 +15,31 @@ const ( SEND_CUSTOM_SCENARIOS = "custom" SEND_TAINTED_SCENARIOS = "tainted" SEND_MANUAL_SCENARIOS = "manual" + SEND_LABEL = "label" ) -var CONSOLE_CONFIGS = []string{SEND_CUSTOM_SCENARIOS, SEND_MANUAL_SCENARIOS, SEND_TAINTED_SCENARIOS} +var CONSOLE_CONFIGS = []string{SEND_CUSTOM_SCENARIOS, SEND_MANUAL_SCENARIOS, SEND_TAINTED_SCENARIOS, SEND_LABEL} var DefaultConsoleConfigFilePath = DefaultConfigPath("console.yaml") +var DefaultLabelsConfigFilePath = DefaultConfigPath("console", "labels.yaml") type ConsoleConfig struct { - ShareManualDecisions *bool `yaml:"share_manual_decisions"` - ShareTaintedScenarios *bool `yaml:"share_tainted"` - ShareCustomScenarios *bool `yaml:"share_custom"` + ShareManualDecisions *bool `yaml:"share_manual_decisions"` + ShareTaintedScenarios *bool `yaml:"share_tainted"` + ShareCustomScenarios *bool `yaml:"share_custom"` + ShareLabel *bool `yaml:"share_labels"` + LabelsToSend map[string][]string `yaml:"-"` } func (c *LocalApiServerCfg) LoadConsoleConfig() error { c.ConsoleConfig = &ConsoleConfig{} + c.ConsoleConfig.LabelsToSend = make(map[string][]string) if _, err := os.Stat(c.ConsoleConfigPath); err != nil && os.IsNotExist(err) { log.Debugf("no console configuration to load") c.ConsoleConfig.ShareCustomScenarios = types.BoolPtr(true) c.ConsoleConfig.ShareTaintedScenarios = types.BoolPtr(true) c.ConsoleConfig.ShareManualDecisions = types.BoolPtr(false) + c.ConsoleConfig.ShareLabel = types.BoolPtr(false) return nil } @@ -46,6 +52,15 @@ func (c *LocalApiServerCfg) LoadConsoleConfig() error { return fmt.Errorf("unmarshaling console config file '%s': %s", c.ConsoleConfigPath, err) } + yamlFile, err = ioutil.ReadFile(DefaultLabelsConfigFilePath) + if err != nil { + return fmt.Errorf("reading console config file '%s': %s", DefaultLabelsConfigFilePath, err) + } + err = yaml.Unmarshal(yamlFile, c.ConsoleConfig.LabelsToSend) + if err != nil { + return fmt.Errorf("unmarshaling labels console config file '%s': %s", DefaultLabelsConfigFilePath, err) + } + if c.ConsoleConfig.ShareCustomScenarios == nil { log.Debugf("no share_custom scenarios found, setting to true") c.ConsoleConfig.ShareCustomScenarios = types.BoolPtr(true) @@ -58,6 +73,12 @@ func (c *LocalApiServerCfg) LoadConsoleConfig() error { log.Debugf("no share_manual scenarios found, setting to false") c.ConsoleConfig.ShareManualDecisions = types.BoolPtr(false) } + + if c.ConsoleConfig.ShareLabel == nil { + log.Debugf("no 'label' found, setting to false") + c.ConsoleConfig.ShareLabel = types.BoolPtr(false) + } + log.Debugf("Console configuration '%s' loaded successfully", c.ConsoleConfigPath) return nil @@ -82,3 +103,18 @@ func (c *LocalApiServerCfg) DumpConsoleConfig() error { return nil } + +func (c *LocalApiServerCfg) DumpLabelConfigFile() error { + var out []byte + var err error + + if out, err = yaml.Marshal(c.ConsoleConfig.LabelsToSend); err != nil { + return errors.Wrapf(err, "while marshaling ConsoleConfig (for %s)", DefaultLabelsConfigFilePath) + } + + if err := os.WriteFile(DefaultLabelsConfigFilePath, out, 0600); err != nil { + return errors.Wrapf(err, "while dumping console config to %s", DefaultLabelsConfigFilePath) + } + + return nil +} diff --git a/pkg/leakybucket/overflows.go b/pkg/leakybucket/overflows.go index e7c98f623..5b308d591 100644 --- a/pkg/leakybucket/overflows.go +++ b/pkg/leakybucket/overflows.go @@ -232,6 +232,10 @@ func alertFormatSource(leaky *Leaky, queue *Queue) (map[string]models.Source, st return sources, source_type, nil } +func EventToLabel(Queue) { + +} + //NewAlert will generate a RuntimeAlert and its APIAlert(s) from a bucket that overflowed func NewAlert(leaky *Leaky, queue *Queue) (types.RuntimeAlert, error) { var runtimeAlert types.RuntimeAlert diff --git a/rpm/SPECS/crowdsec.spec b/rpm/SPECS/crowdsec.spec index 67f549852..0e0fad343 100644 --- a/rpm/SPECS/crowdsec.spec +++ b/rpm/SPECS/crowdsec.spec @@ -45,6 +45,7 @@ sed -i "s#/usr/local/lib/crowdsec/plugins/#%{_libdir}/%{name}/plugins/#g" config %install rm -rf %{buildroot} mkdir -p %{buildroot}/etc/crowdsec/hub +mkdir -p %{buildroot}/etc/crowdsec/console/ mkdir -p %{buildroot}/etc/crowdsec/patterns mkdir -p %{buildroot}%{_sharedstatedir}/%{name}/data mkdir -p %{buildroot}%{_presetdir} @@ -62,6 +63,7 @@ install -m 644 -D config/config.yaml %{buildroot}%{_sysconfdir}/crowdsec install -m 644 -D config/simulation.yaml %{buildroot}%{_sysconfdir}/crowdsec install -m 644 -D config/profiles.yaml %{buildroot}%{_sysconfdir}/crowdsec install -m 644 -D config/console.yaml %{buildroot}%{_sysconfdir}/crowdsec +install -m 644 -D config/console.yaml %{buildroot}%{_sysconfdir}/crowdsec/console/ install -m 644 -D %{SOURCE1} %{buildroot}%{_presetdir} install -m 551 plugins/notifications/slack/notification-slack %{buildroot}%{_libdir}/%{name}/plugins/ @@ -114,6 +116,7 @@ rm -rf %{buildroot} %config(noreplace) %{_sysconfdir}/%{name}/simulation.yaml %config(noreplace) %{_sysconfdir}/%{name}/profiles.yaml %config(noreplace) %{_sysconfdir}/%{name}/console.yaml +%config(noreplace) %{_sysconfdir}/%{name}/console/labels.yaml %config(noreplace) %{_presetdir}/80-%{name}.preset %config(noreplace) %{_sysconfdir}/%{name}/notifications/http.yaml %config(noreplace) %{_sysconfdir}/%{name}/notifications/slack.yaml diff --git a/wizard.sh b/wizard.sh index baf4d3ef8..141325349 100755 --- a/wizard.sh +++ b/wizard.sh @@ -24,6 +24,8 @@ CROWDSEC_CONFIG_PATH="${CROWDSEC_PATH}" CROWDSEC_LOG_FILE="/var/log/crowdsec.log" LAPI_LOG_FILE="/var/log/crowdsec_api.log" CROWDSEC_PLUGIN_DIR="${CROWDSEC_USR_DIR}/plugins" +CROWDSEC_CONSOLE_DIR="${CROWDSEC_PATH}/console" + CROWDSEC_BIN="./cmd/crowdsec/crowdsec" CSCLI_BIN="./cmd/crowdsec-cli/cscli" @@ -387,6 +389,8 @@ check_cs_version () { #install crowdsec and cscli install_crowdsec() { mkdir -p "${CROWDSEC_DATA_DIR}" + mkdir -p "${CROWDSEC_CONSOLE_DIR}" + (cd config && find patterns -type f -exec install -Dm 644 "{}" "${CROWDSEC_CONFIG_PATH}/{}" \; && cd ../) || exit mkdir -p "${CROWDSEC_CONFIG_PATH}/scenarios" || exit mkdir -p "${CROWDSEC_CONFIG_PATH}/postoverflows" || exit @@ -408,6 +412,7 @@ install_crowdsec() { install -v -m 644 -D ./config/profiles.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit install -v -m 644 -D ./config/simulation.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit install -v -m 644 -D ./config/"${CONSOLE_FILE}" "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit + install -v -m 644 -D ./config/labels.yaml "${CROWDSEC_CONSOLE_DIR}" 1> /dev/null || exit DATA=${CROWDSEC_DATA_DIR} CFG=${CROWDSEC_CONFIG_PATH} envsubst '$CFG $DATA' < ./config/user.yaml > ${CROWDSEC_CONFIG_PATH}"/user.yaml" || log_fatal "unable to generate user configuration file" if [[ ${DOCKER_MODE} == "false" ]]; then