diff --git a/config.go b/config.go index 47e89d676..6ab9b51d0 100644 --- a/config.go +++ b/config.go @@ -520,6 +520,10 @@ func getConfig() *types.Configuration { // it to 1000ms by default, override-able via OTLP_DURATION environment variable. v.SetDefault("OTLP.Traces.Duration", 1000) + v.SetDefault("Talon.Address", "") + v.SetDefault("Talon.MinimumPriority", "") + v.SetDefault("Talon.CheckCert", true) + v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.AutomaticEnv() if *configFile != "" { @@ -813,6 +817,8 @@ func getConfig() *types.Configuration { c.N8N.MinimumPriority = checkPriority(c.N8N.MinimumPriority) c.OpenObserve.MinimumPriority = checkPriority(c.OpenObserve.MinimumPriority) c.Dynatrace.MinimumPriority = checkPriority(c.Dynatrace.MinimumPriority) + c.SumoLogic.MinimumPriority = checkPriority(c.SumoLogic.MinimumPriority) + c.Talon.MinimumPriority = checkPriority(c.Talon.MinimumPriority) c.Slack.MessageFormatTemplate = getMessageFormatTemplate("Slack", c.Slack.MessageFormat) c.Rocketchat.MessageFormatTemplate = getMessageFormatTemplate("Rocketchat", c.Rocketchat.MessageFormat) diff --git a/config_example.yaml b/config_example.yaml index c99a54796..2aba56997 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -527,3 +527,8 @@ otlp: # OTEL_EXPORTER_OTLP_TIMEOUT: 10000 # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) # checkcert: true # Set if you want to skip TLS certificate validation (default: true) + +talon: + # address: "" # Falco talon address, if not empty, Falco Talon output is enabled + # checkcert: false # check if ssl certificate of the output is valid (default: true) + # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) \ No newline at end of file diff --git a/docs/outputs/talon.md b/docs/outputs/talon.md new file mode 100644 index 000000000..a8669e7f6 --- /dev/null +++ b/docs/outputs/talon.md @@ -0,0 +1,40 @@ +# Falco Talon + +- **Category**: Response engine +- **Website**: https://docs.falco-talon.org + +## Table of content + +- [Falco Talon](#falco-talon) + - [Table of content](#table-of-content) + - [Configuration](#configuration) + - [Example of config.yaml](#example-of-configyaml) + - [Additional info](#additional-info) + - [Screenshots](#screenshots) + +## Configuration + +| Setting | Env var | Default value | Description | +| ----------------------- | ----------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `talon.address` | `TALON_ADDRESS` | | Talon address, if not empty, Talon output is **enabled** | +| `talon.checkcert` | `TALON_CHECKCERT` | `true` | Check if ssl certificate of the output is valid | +| `talon.minimumpriority` | `TALON_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 +talon: + address: "" # Talon address, if not empty, Talon output is enabled + # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + # checkcert: true # check if ssl certificate of the output is valid (default: true) +``` + +## Additional info + +> [!WARNING] +> Falco Talon is active under development and this integration may change in the future to reflect this evolution. + +## Screenshots diff --git a/handlers.go b/handlers.go index 1651a4040..326087fd3 100644 --- a/handlers.go +++ b/handlers.go @@ -471,4 +471,8 @@ func forwardEvent(falcopayload types.FalcoPayload) { if config.OTLP.Traces.Endpoint != "" && (falcopayload.Priority >= types.Priority(config.OTLP.Traces.MinimumPriority)) && (falcopayload.Source == syscall || falcopayload.Source == syscalls) { go otlpClient.OTLPTracesPost(falcopayload) } + + if config.Talon.Address != "" && (falcopayload.Priority >= types.Priority(config.Talon.MinimumPriority) || falcopayload.Rule == testRule) { + go talonClient.TalonPost(falcopayload) + } } diff --git a/main.go b/main.go index 85a3e903f..dc0342465 100644 --- a/main.go +++ b/main.go @@ -78,6 +78,7 @@ var ( openObserveClient *outputs.Client dynatraceClient *outputs.Client otlpClient *outputs.Client + talonClient *outputs.Client statsdClient, dogstatsdClient *statsd.Client config *types.Configuration @@ -799,6 +800,16 @@ func init() { } } + if config.Talon.Address != "" { + var err error + talonClient, err = outputs.NewClient("Talon", config.Talon.Address, false, config.Talon.CheckCert, *initClientArgs) + if err != nil { + config.Talon.Address = "" + } else { + outputs.EnabledOutputs = append(outputs.EnabledOutputs, "Talon") + } + } + log.Printf("[INFO] : Falco Sidekick version: %s\n", GetVersionInfo().GitVersion) log.Printf("[INFO] : Enabled Outputs : %s\n", outputs.EnabledOutputs) diff --git a/outputs/talon.go b/outputs/talon.go new file mode 100644 index 000000000..06f895042 --- /dev/null +++ b/outputs/talon.go @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +package outputs + +import ( + "fmt" + "log" + + "github.com/falcosecurity/falcosidekick/types" +) + +// TalonPost posts event to an URL +func (c *Client) TalonPost(falcopayload types.FalcoPayload) { + c.Stats.Talon.Add(Total, 1) + + err := c.Post(falcopayload) + if err != nil { + go c.CountMetric(Outputs, 1, []string{"output:talon", "status:error"}) + c.Stats.Talon.Add(Error, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "talon", "status": Error}).Inc() + log.Printf("[ERROR] : Talon - %v\n", err.Error()) + return + } + + // Setting the success status + go c.CountMetric(Outputs, 1, []string{"output:talon", "status:ok"}) + c.Stats.Talon.Add(OK, 1) + fmt.Println("aaaaa") + c.PromStats.Outputs.With(map[string]string{"destination": "talon", "status": OK}).Inc() +} diff --git a/stats.go b/stats.go index 6f34b60e8..a73d7b699 100644 --- a/stats.go +++ b/stats.go @@ -86,6 +86,7 @@ func getInitStats() *types.Statistics { OpenObserve: getOutputNewMap("openobserve"), Dynatrace: getOutputNewMap("dynatrace"), OTLPTraces: getOutputNewMap("otlptraces"), + Talon: getOutputNewMap("talon"), } stats.Falco.Add(outputs.Emergency, 0) stats.Falco.Add(outputs.Alert, 0) diff --git a/types/types.go b/types/types.go index 5986d2aee..667d2476d 100644 --- a/types/types.go +++ b/types/types.go @@ -114,6 +114,7 @@ type Configuration struct { OpenObserve OpenObserveConfig Dynatrace DynatraceOutputConfig OTLP OTLPOutputConfig + Talon TalonOutputConfig } // InitClientArgs represent a client parameters for initialization @@ -799,6 +800,13 @@ type OTLPOutputConfig struct { Traces OTLPTraces } +// TalonOutputConfig represents parameters for Talon +type TalonOutputConfig struct { + Address string + CheckCert bool + MinimumPriority string +} + // Statistics is a struct to store stastics type Statistics struct { Requests *expvar.Map @@ -867,6 +875,7 @@ type Statistics struct { OpenObserve *expvar.Map Dynatrace *expvar.Map OTLPTraces *expvar.Map + Talon *expvar.Map } // PromStatistics is a struct to store prometheus metrics