diff --git a/README.md b/README.md index 5edfb6418..f8128b1cc 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Follow the links to get the configuration of each output. - [**Rocketchat**](https://github.com/falcosecurity/falcosidekick/blob/master/docs/outputs/rocketchat.md) - [**Mattermost**](https://github.com/falcosecurity/falcosidekick/blob/master/docs/outputs/mattermost.md) - [**Teams**](https://github.com/falcosecurity/falcosidekick/blob/master/docs/outputs/teams.md) +- [**Webex**](https://github.com/falcosecurity/falcosidekick/blob/master/docs/outputs/webex.md) - [**Discord**](https://github.com/falcosecurity/falcosidekick/blob/master/docs/outputs/discord.md) - [**Google Chat**](https://github.com/falcosecurity/falcosidekick/blob/master/docs/outputs/googlechat.md) - [**Zoho Cliq**](https://github.com/falcosecurity/falcosidekick/blob/master/docs/outputs/cliq.md) diff --git a/config.go b/config.go index 04f356771..ee610c8ad 100644 --- a/config.go +++ b/config.go @@ -71,6 +71,10 @@ var httpOutputDefaults = map[string]map[string]any{ "OutputFormat": "all", "MinimumPriority": "", }, + "Webex": { + "WebhookURL": "", + "MinimumPriority": "", + }, "Datadog": { "APIKey": "", "Host": "https://api.datadoghq.com", @@ -807,6 +811,7 @@ func getConfig() *types.Configuration { c.Rocketchat.MinimumPriority = checkPriority(c.Rocketchat.MinimumPriority) c.Mattermost.MinimumPriority = checkPriority(c.Mattermost.MinimumPriority) c.Teams.MinimumPriority = checkPriority(c.Teams.MinimumPriority) + c.Webex.MinimumPriority = checkPriority(c.Webex.MinimumPriority) c.Datadog.MinimumPriority = checkPriority(c.Datadog.MinimumPriority) c.Alertmanager.MinimumPriority = checkPriority(c.Alertmanager.MinimumPriority) c.Alertmanager.DropEventDefaultPriority = checkPriority(c.Alertmanager.DropEventDefaultPriority) diff --git a/config_example.yaml b/config_example.yaml index 8dd8de6e6..55c7b62be 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -65,6 +65,10 @@ teams: outputformat: "all" # all (default), text, facts minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) +webex: + # webhookurl: "" # Webex WebhookURL, if not empty, Teams Webex is enabled + # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + datadog: # apikey: "" # Datadog API Key, if not empty, Datadog output is enabled # host: "" # Datadog host. Override if you are on the Datadog EU site. Defaults to american site with "https://api.datadoghq.com" diff --git a/docs/outputs/images/webex.png b/docs/outputs/images/webex.png new file mode 100644 index 000000000..5c76318a4 Binary files /dev/null and b/docs/outputs/images/webex.png differ diff --git a/docs/outputs/webex.md b/docs/outputs/webex.md new file mode 100644 index 000000000..008a3e105 --- /dev/null +++ b/docs/outputs/webex.md @@ -0,0 +1,35 @@ +# Webex + +- **Category**: Chat/Messaging +- **Website**: https://webex.com + +## Table of content + +- [Teams](#teams) + - [Table of content](#table-of-content) + - [Configuration](#configuration) + - [Example of config.yaml](#example-of-configyaml) + - [Screenshots](#screenshots) + +## Configuration + +| Setting | Env var | Default value | Description | +| ----------------------- | ----------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `webex.webhookurl` | `WEBEX_WEBHOOKURL` | | Teams WebhookURL, if not empty, Webex output is **enabled** | +| `webex.minimumpriority` | `WEBEX_MINIMUMPRIORITY` | `""` (= `debug`) | Minimum priority of event for using this output, order is `emergency,alert,critical,error,warning,notice,informational,debug or ""` | + + +> [!NOTE] +The Env var values override the settings from yaml file. + +## Example of config.yaml + +```yaml +webex: + webhookurl: "" # Webex WebhookURL, if not empty, Webex output is enabled + # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) +``` +## Screenshots + +![webex example](images/webex.png) + diff --git a/handlers.go b/handlers.go index 326087fd3..bd3b6bef4 100644 --- a/handlers.go +++ b/handlers.go @@ -251,6 +251,9 @@ func forwardEvent(falcopayload types.FalcoPayload) { go teamsClient.TeamsPost(falcopayload) } + if config.Webex.WebhookURL != "" && (falcopayload.Priority >= types.Priority(config.Webex.MinimumPriority) || falcopayload.Rule == testRule) { + go webexClient.WebexPost(falcopayload) + } if config.Datadog.APIKey != "" && (falcopayload.Priority >= types.Priority(config.Datadog.MinimumPriority) || falcopayload.Rule == testRule) { go datadogClient.DatadogPost(falcopayload) } diff --git a/main.go b/main.go index 47f142449..559c49d43 100644 --- a/main.go +++ b/main.go @@ -32,6 +32,7 @@ var ( rocketchatClient *outputs.Client mattermostClient *outputs.Client teamsClient *outputs.Client + webexClient *outputs.Client datadogClient *outputs.Client discordClient *outputs.Client alertmanagerClient *outputs.Client @@ -195,6 +196,16 @@ func init() { } } + if config.Webex.WebhookURL != "" { + var err error + webexClient, err = outputs.NewClient("Webex", config.Webex.WebhookURL, config.Webex.CommonConfig, *initClientArgs) + if err != nil { + config.Webex.WebhookURL = "" + } else { + outputs.EnabledOutputs = append(outputs.EnabledOutputs, "Webex") + } + } + if config.Datadog.APIKey != "" { var err error endpointUrl := fmt.Sprintf("%s?api_key=%s", config.Datadog.Host+outputs.DatadogPath, config.Datadog.APIKey) diff --git a/outputs/webex.go b/outputs/webex.go new file mode 100644 index 000000000..3660ab9a0 --- /dev/null +++ b/outputs/webex.go @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +package outputs + +import ( + "bytes" + "log" + "text/template" + + "github.com/falcosecurity/falcosidekick/types" +) + +var md string = `# Falco Rule '{{ .Rule }}' + +### {{ .Output }} + +Additional informations: + * Hostname: {{ .Hostname }} + * Source: {{ .Source }} + * Priority: {{ .Priority }} + * Tags: + {{ range $t := .Tags }} + * {{ $t }} + {{ end }} + * Fields: + {{ range $key, $value := .OutputFields }} + * {{ $key }}: {{ $value }} + {{ end }} +` + +// Load the md template +var webexTmpl, _ = template.New("markdown").Parse(md) + +// the format is {"markdown":"..."} +type webexPayload struct { + Markdown string `json:"markdown"` +} + +func newWebexPayload(falcopayload types.FalcoPayload) webexPayload { + var tpl bytes.Buffer + + if err := webexTmpl.Execute(&tpl, falcopayload); err != nil { + log.Printf("[ERROR] : Webex Template - %v\n", err) + + } + t := webexPayload{ + Markdown: tpl.String(), + } + + return t +} + +// WebexPost sends event to a Webex Room through a Webhook +func (c *Client) WebexPost(falcopayload types.FalcoPayload) { + c.Stats.Webex.Add(Total, 1) + + err := c.Post(newWebexPayload(falcopayload)) + + if err != nil { + go c.CountMetric(Outputs, 1, []string{"output:webex", "status:error"}) + c.Stats.Webhook.Add(Error, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "webex", "status": Error}).Inc() + log.Printf("[ERROR] : Webex - %v\n", err.Error()) + return + } + + // Setting the success status + go c.CountMetric(Outputs, 1, []string{"output:webex", "status:ok"}) + c.Stats.Webhook.Add(OK, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "webex", "status": OK}).Inc() +} diff --git a/stats.go b/stats.go index a73d7b699..1b39f62dc 100644 --- a/stats.go +++ b/stats.go @@ -29,6 +29,7 @@ func getInitStats() *types.Statistics { Rocketchat: getOutputNewMap("rocketchat"), Mattermost: getOutputNewMap("mattermost"), Teams: getOutputNewMap("teams"), + Webex: getOutputNewMap("webex"), Datadog: getOutputNewMap("datadog"), Discord: getOutputNewMap("discord"), Alertmanager: getOutputNewMap("alertmanager"), diff --git a/types/types.go b/types/types.go index e82a5670a..840ed817d 100644 --- a/types/types.go +++ b/types/types.go @@ -67,6 +67,7 @@ type Configuration struct { Mattermost MattermostOutputConfig Rocketchat RocketchatOutputConfig Teams teamsOutputConfig + Webex WebexOutputConfig Datadog datadogOutputConfig Discord DiscordOutputConfig Alertmanager AlertmanagerOutputConfig @@ -226,6 +227,12 @@ type teamsOutputConfig struct { MinimumPriority string } +type WebexOutputConfig struct { + CommonConfig `mapstructure:",squash"` + WebhookURL string + MinimumPriority string +} + type datadogOutputConfig struct { CommonConfig `mapstructure:",squash"` APIKey string @@ -831,6 +838,7 @@ type Statistics struct { Statsd *expvar.Map Dogstatsd *expvar.Map Webhook *expvar.Map + Webex *expvar.Map AzureEventHub *expvar.Map GCPPubSub *expvar.Map GCPStorage *expvar.Map