|
@@ -15,7 +15,6 @@ import (
|
|
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/fatih/color"
|
|
"github.com/go-openapi/strfmt"
|
|
"github.com/go-openapi/strfmt"
|
|
- "github.com/pkg/errors"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/cobra"
|
|
"gopkg.in/tomb.v2"
|
|
"gopkg.in/tomb.v2"
|
|
@@ -28,14 +27,12 @@ import (
|
|
"github.com/crowdsecurity/crowdsec/pkg/csprofiles"
|
|
"github.com/crowdsecurity/crowdsec/pkg/csprofiles"
|
|
)
|
|
)
|
|
|
|
|
|
-
|
|
|
|
type NotificationsCfg struct {
|
|
type NotificationsCfg struct {
|
|
Config csplugin.PluginConfig `json:"plugin_config"`
|
|
Config csplugin.PluginConfig `json:"plugin_config"`
|
|
Profiles []*csconfig.ProfileCfg `json:"associated_profiles"`
|
|
Profiles []*csconfig.ProfileCfg `json:"associated_profiles"`
|
|
ids []uint
|
|
ids []uint
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
func NewNotificationsCmd() *cobra.Command {
|
|
func NewNotificationsCmd() *cobra.Command {
|
|
var cmdNotifications = &cobra.Command{
|
|
var cmdNotifications = &cobra.Command{
|
|
Use: "notifications [action]",
|
|
Use: "notifications [action]",
|
|
@@ -57,7 +54,6 @@ func NewNotificationsCmd() *cobra.Command {
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
cmdNotifications.AddCommand(NewNotificationsListCmd())
|
|
cmdNotifications.AddCommand(NewNotificationsListCmd())
|
|
cmdNotifications.AddCommand(NewNotificationsInspectCmd())
|
|
cmdNotifications.AddCommand(NewNotificationsInspectCmd())
|
|
cmdNotifications.AddCommand(NewNotificationsReinjectCmd())
|
|
cmdNotifications.AddCommand(NewNotificationsReinjectCmd())
|
|
@@ -65,18 +61,17 @@ func NewNotificationsCmd() *cobra.Command {
|
|
return cmdNotifications
|
|
return cmdNotifications
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
func getNotificationsConfiguration() (map[string]NotificationsCfg, error) {
|
|
func getNotificationsConfiguration() (map[string]NotificationsCfg, error) {
|
|
pcfgs := map[string]csplugin.PluginConfig{}
|
|
pcfgs := map[string]csplugin.PluginConfig{}
|
|
wf := func(path string, info fs.FileInfo, err error) error {
|
|
wf := func(path string, info fs.FileInfo, err error) error {
|
|
if info == nil {
|
|
if info == nil {
|
|
- return errors.Wrapf(err, "error while traversing directory %s", path)
|
|
|
|
|
|
+ return fmt.Errorf("error while traversing directory %s: %w", path, err)
|
|
}
|
|
}
|
|
name := filepath.Join(csConfig.ConfigPaths.NotificationDir, info.Name()) //Avoid calling info.Name() twice
|
|
name := filepath.Join(csConfig.ConfigPaths.NotificationDir, info.Name()) //Avoid calling info.Name() twice
|
|
if (strings.HasSuffix(name, "yaml") || strings.HasSuffix(name, "yml")) && !(info.IsDir()) {
|
|
if (strings.HasSuffix(name, "yaml") || strings.HasSuffix(name, "yml")) && !(info.IsDir()) {
|
|
ts, err := csplugin.ParsePluginConfigFile(name)
|
|
ts, err := csplugin.ParsePluginConfigFile(name)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrapf(err, "Loading notifification plugin configuration with %s", name)
|
|
|
|
|
|
+ return fmt.Errorf("loading notifification plugin configuration with %s: %w", name, err)
|
|
}
|
|
}
|
|
for _, t := range ts {
|
|
for _, t := range ts {
|
|
pcfgs[t.Name] = t
|
|
pcfgs[t.Name] = t
|
|
@@ -86,14 +81,14 @@ func getNotificationsConfiguration() (map[string]NotificationsCfg, error) {
|
|
}
|
|
}
|
|
|
|
|
|
if err := filepath.Walk(csConfig.ConfigPaths.NotificationDir, wf); err != nil {
|
|
if err := filepath.Walk(csConfig.ConfigPaths.NotificationDir, wf); err != nil {
|
|
- return nil, errors.Wrap(err, "Loading notifification plugin configuration")
|
|
|
|
|
|
+ return nil, fmt.Errorf("while loading notifification plugin configuration: %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
// A bit of a tricky stuf now: reconcile profiles and notification plugins
|
|
// A bit of a tricky stuf now: reconcile profiles and notification plugins
|
|
ncfgs := map[string]NotificationsCfg{}
|
|
ncfgs := map[string]NotificationsCfg{}
|
|
profiles, err := csprofiles.NewProfile(csConfig.API.Server.Profiles)
|
|
profiles, err := csprofiles.NewProfile(csConfig.API.Server.Profiles)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return nil, errors.Wrap(err, "Cannot extract profiles from configuration")
|
|
|
|
|
|
+ return nil, fmt.Errorf("while extracting profiles from configuration: %w", err)
|
|
}
|
|
}
|
|
for profileID, profile := range profiles {
|
|
for profileID, profile := range profiles {
|
|
loop:
|
|
loop:
|
|
@@ -129,7 +124,6 @@ func getNotificationsConfiguration() (map[string]NotificationsCfg, error) {
|
|
return ncfgs, nil
|
|
return ncfgs, nil
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
func NewNotificationsListCmd() *cobra.Command {
|
|
func NewNotificationsListCmd() *cobra.Command {
|
|
var cmdNotificationsList = &cobra.Command{
|
|
var cmdNotificationsList = &cobra.Command{
|
|
Use: "list",
|
|
Use: "list",
|
|
@@ -141,7 +135,7 @@ func NewNotificationsListCmd() *cobra.Command {
|
|
RunE: func(cmd *cobra.Command, arg []string) error {
|
|
RunE: func(cmd *cobra.Command, arg []string) error {
|
|
ncfgs, err := getNotificationsConfiguration()
|
|
ncfgs, err := getNotificationsConfiguration()
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrap(err, "Can't build profiles configuration")
|
|
|
|
|
|
+ return fmt.Errorf("can't build profiles configuration: %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
if csConfig.Cscli.Output == "human" {
|
|
if csConfig.Cscli.Output == "human" {
|
|
@@ -149,14 +143,14 @@ func NewNotificationsListCmd() *cobra.Command {
|
|
} else if csConfig.Cscli.Output == "json" {
|
|
} else if csConfig.Cscli.Output == "json" {
|
|
x, err := json.MarshalIndent(ncfgs, "", " ")
|
|
x, err := json.MarshalIndent(ncfgs, "", " ")
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.New("failed to marshal notification configuration")
|
|
|
|
|
|
+ return fmt.Errorf("failed to marshal notification configuration: %w", err)
|
|
}
|
|
}
|
|
fmt.Printf("%s", string(x))
|
|
fmt.Printf("%s", string(x))
|
|
} else if csConfig.Cscli.Output == "raw" {
|
|
} else if csConfig.Cscli.Output == "raw" {
|
|
csvwriter := csv.NewWriter(os.Stdout)
|
|
csvwriter := csv.NewWriter(os.Stdout)
|
|
err := csvwriter.Write([]string{"Name", "Type", "Profile name"})
|
|
err := csvwriter.Write([]string{"Name", "Type", "Profile name"})
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrap(err, "failed to write raw header")
|
|
|
|
|
|
+ return fmt.Errorf("failed to write raw header: %w", err)
|
|
}
|
|
}
|
|
for _, b := range ncfgs {
|
|
for _, b := range ncfgs {
|
|
profilesList := []string{}
|
|
profilesList := []string{}
|
|
@@ -165,7 +159,7 @@ func NewNotificationsListCmd() *cobra.Command {
|
|
}
|
|
}
|
|
err := csvwriter.Write([]string{b.Config.Name, b.Config.Type, strings.Join(profilesList, ", ")})
|
|
err := csvwriter.Write([]string{b.Config.Name, b.Config.Type, strings.Join(profilesList, ", ")})
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrap(err, "failed to write raw content")
|
|
|
|
|
|
+ return fmt.Errorf("failed to write raw content: %w", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
csvwriter.Flush()
|
|
csvwriter.Flush()
|
|
@@ -177,7 +171,6 @@ func NewNotificationsListCmd() *cobra.Command {
|
|
return cmdNotificationsList
|
|
return cmdNotificationsList
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
func NewNotificationsInspectCmd() *cobra.Command {
|
|
func NewNotificationsInspectCmd() *cobra.Command {
|
|
var cmdNotificationsInspect = &cobra.Command{
|
|
var cmdNotificationsInspect = &cobra.Command{
|
|
Use: "inspect",
|
|
Use: "inspect",
|
|
@@ -195,14 +188,14 @@ func NewNotificationsInspectCmd() *cobra.Command {
|
|
pluginName := arg[0]
|
|
pluginName := arg[0]
|
|
|
|
|
|
if pluginName == "" {
|
|
if pluginName == "" {
|
|
- errors.New("Please provide a plugin name to inspect")
|
|
|
|
|
|
+ return fmt.Errorf("please provide a plugin name to inspect")
|
|
}
|
|
}
|
|
ncfgs, err := getNotificationsConfiguration()
|
|
ncfgs, err := getNotificationsConfiguration()
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrap(err, "Can't build profiles configuration")
|
|
|
|
|
|
+ return fmt.Errorf("can't build profiles configuration: %w", err)
|
|
}
|
|
}
|
|
if cfg, ok = ncfgs[pluginName]; !ok {
|
|
if cfg, ok = ncfgs[pluginName]; !ok {
|
|
- return errors.New("The provided plugin name doesn't exist or isn't active")
|
|
|
|
|
|
+ return fmt.Errorf("plugin '%s' does not exist or is not active", pluginName)
|
|
}
|
|
}
|
|
|
|
|
|
if csConfig.Cscli.Output == "human" || csConfig.Cscli.Output == "raw" {
|
|
if csConfig.Cscli.Output == "human" || csConfig.Cscli.Output == "raw" {
|
|
@@ -216,7 +209,7 @@ func NewNotificationsInspectCmd() *cobra.Command {
|
|
} else if csConfig.Cscli.Output == "json" {
|
|
} else if csConfig.Cscli.Output == "json" {
|
|
x, err := json.MarshalIndent(cfg, "", " ")
|
|
x, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.New("failed to marshal notification configuration")
|
|
|
|
|
|
+ return fmt.Errorf("failed to marshal notification configuration: %w", err)
|
|
}
|
|
}
|
|
fmt.Printf("%s", string(x))
|
|
fmt.Printf("%s", string(x))
|
|
}
|
|
}
|
|
@@ -227,7 +220,6 @@ func NewNotificationsInspectCmd() *cobra.Command {
|
|
return cmdNotificationsInspect
|
|
return cmdNotificationsInspect
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
func NewNotificationsReinjectCmd() *cobra.Command {
|
|
func NewNotificationsReinjectCmd() *cobra.Command {
|
|
var remediation bool
|
|
var remediation bool
|
|
var alertOverride string
|
|
var alertOverride string
|
|
@@ -250,26 +242,26 @@ cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"not
|
|
)
|
|
)
|
|
if len(args) != 1 {
|
|
if len(args) != 1 {
|
|
printHelp(cmd)
|
|
printHelp(cmd)
|
|
- return errors.New("Wrong number of argument: there should be one argument")
|
|
|
|
|
|
+ return fmt.Errorf("wrong number of argument: there should be one argument")
|
|
}
|
|
}
|
|
|
|
|
|
//first: get the alert
|
|
//first: get the alert
|
|
id, err := strconv.Atoi(args[0])
|
|
id, err := strconv.Atoi(args[0])
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.New(fmt.Sprintf("bad alert id %s", args[0]))
|
|
|
|
|
|
+ return fmt.Errorf("bad alert id %s", args[0])
|
|
}
|
|
}
|
|
if err := csConfig.LoadAPIClient(); err != nil {
|
|
if err := csConfig.LoadAPIClient(); err != nil {
|
|
- return errors.Wrapf(err, "loading api client")
|
|
|
|
|
|
+ return fmt.Errorf("loading api client: %w", err)
|
|
}
|
|
}
|
|
if csConfig.API.Client == nil {
|
|
if csConfig.API.Client == nil {
|
|
- return errors.New("There is no configuration on 'api_client:'")
|
|
|
|
|
|
+ return fmt.Errorf("missing configuration on 'api_client:'")
|
|
}
|
|
}
|
|
if csConfig.API.Client.Credentials == nil {
|
|
if csConfig.API.Client.Credentials == nil {
|
|
- return errors.New(fmt.Sprintf("Please provide credentials for the API in '%s'", csConfig.API.Client.CredentialsFilePath))
|
|
|
|
|
|
+ return fmt.Errorf("missing API credentials in '%s'", csConfig.API.Client.CredentialsFilePath)
|
|
}
|
|
}
|
|
apiURL, err := url.Parse(csConfig.API.Client.Credentials.URL)
|
|
apiURL, err := url.Parse(csConfig.API.Client.Credentials.URL)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrapf(err, "error parsing the URL of the API")
|
|
|
|
|
|
+ return fmt.Errorf("error parsing the URL of the API: %w", err)
|
|
}
|
|
}
|
|
client, err := apiclient.NewClient(&apiclient.Config{
|
|
client, err := apiclient.NewClient(&apiclient.Config{
|
|
MachineID: csConfig.API.Client.Credentials.Login,
|
|
MachineID: csConfig.API.Client.Credentials.Login,
|
|
@@ -279,16 +271,16 @@ cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"not
|
|
VersionPrefix: "v1",
|
|
VersionPrefix: "v1",
|
|
})
|
|
})
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrapf(err, "error creating the client for the API")
|
|
|
|
|
|
+ return fmt.Errorf("error creating the client for the API: %w", err)
|
|
}
|
|
}
|
|
alert, _, err := client.Alerts.GetByID(context.Background(), id)
|
|
alert, _, err := client.Alerts.GetByID(context.Background(), id)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrapf(err, fmt.Sprintf("can't find alert with id %s", args[0]))
|
|
|
|
|
|
+ return fmt.Errorf("can't find alert with id %s: %w", args[0], err)
|
|
}
|
|
}
|
|
|
|
|
|
if alertOverride != "" {
|
|
if alertOverride != "" {
|
|
if err = json.Unmarshal([]byte(alertOverride), alert); err != nil {
|
|
if err = json.Unmarshal([]byte(alertOverride), alert); err != nil {
|
|
- return errors.Wrapf(err, "Can't unmarshal the data given in the alert flag")
|
|
|
|
|
|
+ return fmt.Errorf("can't unmarshal data in the alert flag: %w", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !remediation {
|
|
if !remediation {
|
|
@@ -298,7 +290,7 @@ cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"not
|
|
// second we start plugins
|
|
// second we start plugins
|
|
err = pluginBroker.Init(csConfig.PluginConfig, csConfig.API.Server.Profiles, csConfig.ConfigPaths)
|
|
err = pluginBroker.Init(csConfig.PluginConfig, csConfig.API.Server.Profiles, csConfig.ConfigPaths)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrapf(err, "Can't initialize plugins")
|
|
|
|
|
|
+ return fmt.Errorf("can't initialize plugins: %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
pluginTomb.Go(func() error {
|
|
pluginTomb.Go(func() error {
|
|
@@ -310,13 +302,13 @@ cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"not
|
|
|
|
|
|
profiles, err := csprofiles.NewProfile(csConfig.API.Server.Profiles)
|
|
profiles, err := csprofiles.NewProfile(csConfig.API.Server.Profiles)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrap(err, "Cannot extract profiles from configuration")
|
|
|
|
|
|
+ return fmt.Errorf("cannot extract profiles from configuration: %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
for id, profile := range profiles {
|
|
for id, profile := range profiles {
|
|
_, matched, err := profile.EvaluateProfile(alert)
|
|
_, matched, err := profile.EvaluateProfile(alert)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return errors.Wrapf(err, "can't evaluate profile %s", profile.Cfg.Name)
|
|
|
|
|
|
+ return fmt.Errorf("can't evaluate profile %s: %w", profile.Cfg.Name, err)
|
|
}
|
|
}
|
|
if !matched {
|
|
if !matched {
|
|
log.Infof("The profile %s didn't match", profile.Cfg.Name)
|
|
log.Infof("The profile %s didn't match", profile.Cfg.Name)
|
|
@@ -344,7 +336,7 @@ cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"not
|
|
}
|
|
}
|
|
|
|
|
|
// time.Sleep(2 * time.Second) // There's no mechanism to ensure notification has been sent
|
|
// time.Sleep(2 * time.Second) // There's no mechanism to ensure notification has been sent
|
|
- pluginTomb.Kill(errors.New("terminating"))
|
|
|
|
|
|
+ pluginTomb.Kill(fmt.Errorf("terminating"))
|
|
pluginTomb.Wait()
|
|
pluginTomb.Wait()
|
|
return nil
|
|
return nil
|
|
},
|
|
},
|