diff --git a/config.go b/config.go index 6ab9b51d0..71dfa0a71 100644 --- a/config.go +++ b/config.go @@ -21,6 +21,397 @@ import ( "github.com/falcosecurity/falcosidekick/types" ) +// Max concurrent requests at a time per output, unlimited if set to 0 +// Setting it to 1 by default, because the previously +// many outputs were synchronized on headers locks, in all or some cases +// and that was limiting the the number of requests to 1 at a time. +const defaultMaxConcurrentHttpRequests = 1 + +// Common http outputs defaults +var commonHttpOutputDefaults = map[string]any{ + "MutualTLS": false, + "CheckCert": true, + // Max concurrent requests at a time per http-based output + "MaxConcurrentRequests": defaultMaxConcurrentHttpRequests, +} + +// Http based outputs that share the common http defaults above +var httpOutputDefaults = map[string]map[string]any{ + "Slack": { + "WebhookURL": "", + "Footer": "https://github.com/falcosecurity/falcosidekick", + "Username": "Falcosidekick", + "Channel": "", + "Icon": "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png", + "OutputFormat": "all", + "MessageFormat": "", + "MinimumPriority": "", + }, + "Rocketchat": { + "WebhookURL": "", + "Footer": "https://github.com/falcosecurity/falcosidekick", + "Username": "Falcosidekick", + "Icon": "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png", + "OutputFormat": "all", + "MessageFormat": "", + "MinimumPriority": "", + }, + "Mattermost": { + "WebhookURL": "", + "Footer": "https://github.com/falcosecurity/falcosidekick", + "Username": "Falcosidekick", + "Icon": "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png", + "OutputFormat": "all", + "MessageFormat": "", + "MinimumPriority": "", + }, + "Teams": { + "WebhookURL": "", + "ActivityImage": "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png", + "OutputFormat": "all", + "MinimumPriority": "", + }, + "Datadog": { + "APIKey": "", + "Host": "https://api.datadoghq.com", + "MinimumPriority": "", + }, + "Discord": { + "WebhookURL": "", + "MinimumPriority": "", + "Icon": "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png", + }, + "Alertmanager": { + "HostPort": "", + "MinimumPriority": "", + "Endpoint": "/api/v1/alerts", + "ExpiresAfter": 0, + "DropEventDefaultPriority": "critical", + "DropEventThresholds": "10000:critical: 1000:critical: 100:critical: 10:warning: 1:warning", + }, + "Elasticsearch": { + "HostPort": "", + "Index": "falco", + "Type": "_doc", + "MinimumPriority": "", + "Suffix": "daily", + "Username": "", + "Password": "", + "FlattenFields": false, + "CreateIndexTemplate": false, + "NumberOfShards": 3, + "NumberOfReplicas": 3, + }, + "Quickwit": { + "HostPort": "", + "Index": "falco", + "ApiEndpoint": "api/v1", + "Version": "0.7", + "AutoCreateIndex": false, + "MinimumPriority": "", + }, + "Influxdb": { + "HostPort": "", + "Database": "falco", + "Organization": "", + "Bucket": "falco", + "Precision": "ns", + "User": "", + "Password": "", + "Token": "", + "MinimumPriority": "", + }, + "Loki": { + "HostPort": "", + "User": "", + "APIKey": "", + "MinimumPriority": "", + "Tenant": "", + "Endpoint": "/loki/api/v1/push", + "ExtraLabels": "", + }, + "SumoLogic": { + "MinimumPriority": "", + "ReceiverURL": "", + "SourceCategory": "", + "SourceHost": "", + "Name": "", + }, + "STAN": { + "HostPort": "", + "ClusterID": "", + "ClientID": "", + }, + "NATS": { + "HostPort": "", + "ClusterID": "", + "ClientID": "", + }, + "Opsgenie": { + "Region": "us", + "APIKey": "", + "MinimumPriority": "", + }, + "Webhook": { + "Address": "", + "Method": "POST", + "MinimumPriority": "", + }, + "CloudEvents": { + "Address": "", + "MinimumPriority": "", + }, + "Googlechat": { + "WebhookURL": "", + "OutputFormat": "all", + "MessageFormat": "", + "MinimumPriority": "", + }, + "Cliq": { + "WebhookURL": "", + "Icon": "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png", + "OutputFormat": "all", + "UseEmoji": false, + "MessageFormat": "", + "MinimumPriority": "", + }, + "KafkaRest": { + "Address": "", + "Version": 2, + "MinimumPriority": "", + }, + "Pagerduty": { + "RoutingKey": "", + "Region": "us", + "MinimumPriority": "", + }, + "Kubeless": { + "Namespace": "", + "Function": "", + "Port": 8080, + "Kubeconfig": "", + "MinimumPriority": "", + }, + "Openfaas": { + "GatewayNamespace": "openfaas", + "GatewayService": "gateway", + "FunctionName": "", + "FunctionNamespace": "openfaas-fn", + "GatewayPort": 8080, + "Kubeconfig": "", + "MinimumPriority": "", + }, + "Fission": { + "RouterNamespace": "fission", + "RouterService": "router", + "RouterPort": 80, + "FunctionNamespace": "fission-function", + "Function": "", + "Kubeconfig": "", + "MinimumPriority": "", + "MutualTLS": false, + "CheckCert": true, + }, + "Webui": { + "URL": "", + }, + "Grafana": { + "HostPort": "", + "DashboardID": 0, + "PanelID": 0, + "APIKey": "", + "AllFieldsAsTags": false, + "MinimumPriority": "", + }, + "GrafanaOnCall": { + "WebhookURL": "", + "MinimumPriority": "", + }, + "Redis": { + "Address": "", + "Password": "", + "Database": 0, + "StorageType": "list", + "Key": "falco", + "MinimumPriority": "", + }, + "OpenObserve": { + "HostPort": "", + "OrganizationName": "default", + "StreamName": "falco", + "MinimumPriority": "", + "Username": "", + "Password": "", + }, +} + +// Other output defaults that do not need commonHttpOutputDefaults +var outputDefaults = map[string]map[string]any{ + "SMTP": { + "HostPort": "", + "Tls": true, + "From": "", + "To": "", + "OutputFormat": "html", + "MinimumPriority": "", + "AuthMechanism": "plain", + "User": "", + "Password": "", + "Token": "", + "Identity": "", + "Trace": "", + }, + "Statsd": { + "Forwarder": "", + "Namespace": "falcosidekick.", + }, + "Dogstatsd": { + "Forwarder": "", + "Namespace": "falcosidekick.", + "Tags": []string{}, + }, + "NodeRed": { + "Address": "", + "User": "", + "Password": "", + "MinimumPriority": "", + "CheckCert": true, + }, + "Kafka": { + "HostPort": "", + "Topic": "", + "MinimumPriority": "", + "SASL": "", + "TLS": false, + "Username": "", + "Password": "", + "Balancer": "round_robin", + "ClientID": "", + "Compression": "NONE", + "Async": false, + "RequiredACKs": "NONE", + "TopicCreation": false, + }, + "PolicyReport": { + "Enabled": false, + "Kubeconfig": "", + "MinimumPriority": "", + "MaxEvents": 1000, + "FalcoNamespace": "", + "PruneByPriority": false, + }, + "Rabbitmq": { + "URL": "", + "Queue": "", + "MinimumPriority": "", + }, + "Wavefront": { + "EndpointType": "", + "EndpointHost": "", + "EndpointToken": "", + "MetricName": "falco.alert", + "EndpointMetricPort": 2878, + "MinimumPriority": "", + "FlushIntervalSecods": 1, + "BatchSize": 10000, + }, + "Syslog": { + "Host": "", + "Port": "", + "Protocol": "", + "Format": "json", + "MinimumPriority": "", + }, + "MQTT": { + "Broker": "", + "Topic": "falco/events", + "QOS": 0, + "Retained": false, + "User": "", + "Password": "", + "CheckCert": true, + "MinimumPriority": "", + }, + "Zincsearch": { + "HostPort": "", + "Index": "falco", + "Username": "", + "Password": "", + "CheckCert": true, + "MinimumPriority": "", + }, + "Gotify": { + "HostPort": "", + "Token": "", + "Format": "markdown", + "CheckCert": true, + "MinimumPriority": "", + }, + "Tekton": { + "EventListener": "", + "MinimumPriority": "", + "CheckCert": true, + }, + "Spyderbat": { + "OrgUID": "", + "APIKey": "", + "APIUrl": "https://api.spyderbat.com", + "Source": "falcosidekick", + "SourceDescription": "", + "MinimumPriority": "", + }, + "TimescaleDB": { + "Host": "", + "Port": "5432", + "User": "postgres", + "Password": "postgres", + "Database": "falcosidekick", + "HypertableName": "falcosidekick_events", + "MinimumPriority": "", + }, + "N8n": { + "Address": "", + "User": "", + "Password": "", + "HeaderAuthName": "", + "HeaderAuthValue": "", + "MinimumPriority": "", + "CheckCert": true, + }, + "Telegram": { + "Token": "", + "ChatID": "", + "MinimumPriority": "", + "CheckCert": true, + }, + "Dynatrace": { + "APIToken": "", + "APIUrl": "", + "CheckCert": true, + "MinimumPriority": "", + }, + "Talon": { + "Address": "", + "MinimumPriority": "", + "CheckCert": true, + }, +} + +func init() { + for name, dst := range httpOutputDefaults { + // Apply common http output defaults to http output defaults + for k, v := range commonHttpOutputDefaults { + dst[k] = v + } + + // Merge http outputs defaults with other outputs defaults + if _, ok := outputDefaults[name]; ok { + panic(fmt.Sprintf("key %v already set in the output defaults", name)) + } + outputDefaults[name] = dst + } +} + func getConfig() *types.Configuration { c := &types.Configuration{ Customfields: make(map[string]string), @@ -67,117 +458,12 @@ func getConfig() *types.Configuration { v.SetDefault("TLSServer.CaCertFile", "/etc/certs/server/ca.crt") v.SetDefault("TLSServer.NoTLSPort", 2810) - v.SetDefault("Slack.WebhookURL", "") - v.SetDefault("Slack.Footer", "https://github.com/falcosecurity/falcosidekick") - v.SetDefault("Slack.Username", "Falcosidekick") - v.SetDefault("Slack.Channel", "") - v.SetDefault("Slack.Icon", "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png") - v.SetDefault("Slack.OutputFormat", "all") - v.SetDefault("Slack.MessageFormat", "") - v.SetDefault("Slack.MinimumPriority", "") - v.SetDefault("Slack.MutualTLS", false) - v.SetDefault("Slack.CheckCert", true) - - v.SetDefault("Rocketchat.WebhookURL", "") - v.SetDefault("Rocketchat.Footer", "https://github.com/falcosecurity/falcosidekick") - v.SetDefault("Rocketchat.Username", "Falcosidekick") - v.SetDefault("Rocketchat.Icon", "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png") - v.SetDefault("Rocketchat.OutputFormat", "all") - v.SetDefault("Rocketchat.MessageFormat", "") - v.SetDefault("Rocketchat.MinimumPriority", "") - v.SetDefault("Rocketchat.MutualTLS", false) - v.SetDefault("Rocketchat.CheckCert", true) - - v.SetDefault("Mattermost.WebhookURL", "") - v.SetDefault("Mattermost.Footer", "https://github.com/falcosecurity/falcosidekick") - v.SetDefault("Mattermost.Username", "Falcosidekick") - v.SetDefault("Mattermost.Icon", "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png") - v.SetDefault("Mattermost.OutputFormat", "all") - v.SetDefault("Mattermost.MessageFormat", "") - v.SetDefault("Mattermost.MinimumPriority", "") - v.SetDefault("Mattermost.MutualTLS", false) - v.SetDefault("Mattermost.CheckCert", true) - - v.SetDefault("Teams.WebhookURL", "") - v.SetDefault("Teams.ActivityImage", "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png") - v.SetDefault("Teams.OutputFormat", "all") - v.SetDefault("Teams.MinimumPriority", "") - v.SetDefault("Teams.MutualTLS", false) - v.SetDefault("Teams.CheckCert", true) - - v.SetDefault("Datadog.APIKey", "") - v.SetDefault("Datadog.Host", "https://api.datadoghq.com") - v.SetDefault("Datadog.MinimumPriority", "") - v.SetDefault("Datadog.MutualTLS", false) - v.SetDefault("Datadog.CheckCert", true) - - v.SetDefault("Discord.WebhookURL", "") - v.SetDefault("Discord.MinimumPriority", "") - v.SetDefault("Discord.Icon", "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png") - v.SetDefault("Discord.MutualTLS", false) - v.SetDefault("Discord.CheckCert", true) - - v.SetDefault("Alertmanager.HostPort", "") - v.SetDefault("Alertmanager.MinimumPriority", "") - v.SetDefault("Alertmanager.MutualTls", false) - v.SetDefault("Alertmanager.CheckCert", true) - v.SetDefault("Alertmanager.Endpoint", "/api/v1/alerts") - v.SetDefault("Alertmanager.ExpiresAfter", 0) - v.SetDefault("Alertmanager.DropEventDefaultPriority", "critical") - v.SetDefault("Alertmanager.DropEventThresholds", "10000:critical, 1000:critical, 100:critical, 10:warning, 1:warning") - - v.SetDefault("Elasticsearch.HostPort", "") - v.SetDefault("Elasticsearch.Index", "falco") - v.SetDefault("Elasticsearch.Type", "_doc") - v.SetDefault("Elasticsearch.MinimumPriority", "") - v.SetDefault("Elasticsearch.Suffix", "daily") - v.SetDefault("Elasticsearch.MutualTls", false) - v.SetDefault("Elasticsearch.CheckCert", true) - v.SetDefault("Elasticsearch.Username", "") - v.SetDefault("Elasticsearch.Password", "") - v.SetDefault("Elasticsearch.FlattenFields", false) - v.SetDefault("Elasticsearch.CreateIndexTemplate", false) - v.SetDefault("Elasticsearch.NumberOfShards", 3) - v.SetDefault("Elasticsearch.NumberOfReplicas", 3) - - v.SetDefault("Quickwit.HostPort", "") - v.SetDefault("Quickwit.Index", "falco") - v.SetDefault("Quickwit.ApiEndpoint", "api/v1") - v.SetDefault("Quickwit.Version", "0.7") - v.SetDefault("Quickwit.AutoCreateIndex", false) - v.SetDefault("Quickwit.MinimumPriority", "") - v.SetDefault("Quickwit.MutualTls", false) - v.SetDefault("Quickwit.CheckCert", true) - - v.SetDefault("Influxdb.HostPort", "") - v.SetDefault("Influxdb.Database", "falco") - v.SetDefault("Influxdb.Organization", "") - v.SetDefault("Influxdb.Bucket", "falco") - v.SetDefault("Influxdb.Precision", "ns") - v.SetDefault("Influxdb.User", "") - v.SetDefault("Influxdb.Password", "") - v.SetDefault("Influxdb.Token", "") - v.SetDefault("Influxdb.MinimumPriority", "") - v.SetDefault("Influxdb.MutualTls", false) - v.SetDefault("Influxdb.CheckCert", true) - - v.SetDefault("Loki.HostPort", "") - v.SetDefault("Loki.User", "") - v.SetDefault("Loki.APIKey", "") - v.SetDefault("Loki.MinimumPriority", "") - v.SetDefault("Loki.MutualTLS", false) - v.SetDefault("Loki.CheckCert", true) - v.SetDefault("Loki.Tenant", "") - v.SetDefault("Loki.Endpoint", "/loki/api/v1/push") - v.SetDefault("Loki.ExtraLabels", "") - - v.SetDefault("SumoLogic.MinimumPriority", "") - v.SetDefault("SumoLogic.ReceiverURL", "") - v.SetDefault("SumoLogic.SourceCategory", "") - v.SetDefault("SumoLogic.SourceHost", "") - v.SetDefault("SumoLogic.Name", "") - v.SetDefault("SumoLogic.CheckCert", true) - v.SetDefault("SumoLogic.MutualTLS", false) + // Set outputs defaults + for prefix, m := range outputDefaults { + for key, val := range m { + v.SetDefault(prefix+"."+key, val) + } + } v.SetDefault("AWS.AccessKeyID", "") v.SetDefault("AWS.SecretAccessKey", "") @@ -219,63 +505,8 @@ func getConfig() *types.Configuration { v.SetDefault("AWS.Kinesis.StreamName", "") v.SetDefault("AWS.Kinesis.MinimumPriority", "") - v.SetDefault("SMTP.HostPort", "") - v.SetDefault("SMTP.Tls", true) - v.SetDefault("SMTP.From", "") - v.SetDefault("SMTP.To", "") - v.SetDefault("SMTP.OutputFormat", "html") - v.SetDefault("SMTP.MinimumPriority", "") - v.SetDefault("SMTP.AuthMechanism", "plain") - v.SetDefault("SMTP.User", "") - v.SetDefault("SMTP.Password", "") - v.SetDefault("SMTP.Token", "") - v.SetDefault("SMTP.Identity", "") - v.SetDefault("SMTP.Trace", "") - - v.SetDefault("STAN.HostPort", "") - v.SetDefault("STAN.ClusterID", "") - v.SetDefault("STAN.ClientID", "") - v.SetDefault("STAN.MutualTls", false) - v.SetDefault("STAN.CheckCert", true) - - v.SetDefault("NATS.HostPort", "") - v.SetDefault("NATS.ClusterID", "") - v.SetDefault("NATS.ClientID", "") - v.SetDefault("NATS.MutualTls", false) - v.SetDefault("NATS.CheckCert", true) - - v.SetDefault("Opsgenie.Region", "us") - v.SetDefault("Opsgenie.APIKey", "") - v.SetDefault("Opsgenie.MinimumPriority", "") - v.SetDefault("Opsgenie.MutualTLS", false) - v.SetDefault("Opsgenie.CheckCert", true) - - v.SetDefault("Statsd.Forwarder", "") - v.SetDefault("Statsd.Namespace", "falcosidekick.") - v.SetDefault("Prometheus.ExtraLabels", "") - v.SetDefault("Dogstatsd.Forwarder", "") - v.SetDefault("Dogstatsd.Namespace", "falcosidekick.") - v.SetDefault("Dogstatsd.Tags", []string{}) - - v.SetDefault("Webhook.Address", "") - v.SetDefault("Webhook.Method", "POST") - v.SetDefault("Webhook.MinimumPriority", "") - v.SetDefault("Webhook.MutualTls", false) - v.SetDefault("Webhook.CheckCert", true) - - v.SetDefault("NodeRed.Address", "") - v.SetDefault("NodeRed.User", "") - v.SetDefault("NodeRed.Password", "") - v.SetDefault("NodeRed.MinimumPriority", "") - v.SetDefault("NodeRed.CheckCert", true) - - v.SetDefault("CloudEvents.Address", "") - v.SetDefault("CloudEvents.MinimumPriority", "") - v.SetDefault("CloudEvents.MutualTls", false) - v.SetDefault("CloudEvents.CheckCert", true) - v.SetDefault("Azure.eventHub.Namespace", "") v.SetDefault("Azure.eventHub.Name", "") v.SetDefault("Azure.eventHub.MinimumPriority", "") @@ -297,118 +528,6 @@ func getConfig() *types.Configuration { v.SetDefault("GCP.CloudRun.JWT", "") v.SetDefault("GCP.CloudRun.MinimumPriority", "") - v.SetDefault("Googlechat.WebhookURL", "") - v.SetDefault("Googlechat.OutputFormat", "all") - v.SetDefault("Googlechat.MessageFormat", "") - v.SetDefault("Googlechat.MinimumPriority", "") - v.SetDefault("Googlechat.MutualTls", false) - v.SetDefault("Googlechat.CheckCert", true) - - v.SetDefault("Cliq.WebhookURL", "") - v.SetDefault("Cliq.Icon", "https://raw.githubusercontent.com/falcosecurity/falcosidekick/master/imgs/falcosidekick_color.png") - v.SetDefault("Cliq.OutputFormat", "all") - v.SetDefault("Cliq.UseEmoji", false) - v.SetDefault("Cliq.MessageFormat", "") - v.SetDefault("Cliq.MinimumPriority", "") - v.SetDefault("Cliq.MutualTls", false) - v.SetDefault("Cliq.CheckCert", true) - - v.SetDefault("Kafka.HostPort", "") - v.SetDefault("Kafka.Topic", "") - v.SetDefault("Kafka.MinimumPriority", "") - v.SetDefault("Kafka.SASL", "") - v.SetDefault("Kafka.TLS", false) - v.SetDefault("Kafka.Username", "") - v.SetDefault("Kafka.Password", "") - v.SetDefault("Kafka.Balancer", "round_robin") - v.SetDefault("Kafka.ClientID", "") - v.SetDefault("Kafka.Compression", "NONE") - v.SetDefault("Kafka.Async", false) - v.SetDefault("Kafka.RequiredACKs", "NONE") - v.SetDefault("Kafka.TopicCreation", false) - - v.SetDefault("KafkaRest.Address", "") - v.SetDefault("KafkaRest.Version", 2) - v.SetDefault("KafkaRest.MinimumPriority", "") - v.SetDefault("KafkaRest.MutualTls", false) - v.SetDefault("KafkaRest.CheckCert", true) - - v.SetDefault("Pagerduty.RoutingKey", "") - v.SetDefault("Pagerduty.Region", "us") - v.SetDefault("Pagerduty.MinimumPriority", "") - v.SetDefault("Pagerduty.MutualTls", false) - v.SetDefault("Pagerduty.CheckCert", true) - - v.SetDefault("Kubeless.Namespace", "") - v.SetDefault("Kubeless.Function", "") - v.SetDefault("Kubeless.Port", 8080) - v.SetDefault("Kubeless.Kubeconfig", "") - v.SetDefault("Kubeless.MinimumPriority", "") - v.SetDefault("Kubeless.MutualTls", false) - v.SetDefault("Kubeless.CheckCert", true) - - v.SetDefault("Openfaas.GatewayNamespace", "openfaas") - v.SetDefault("Openfaas.GatewayService", "gateway") - v.SetDefault("Openfaas.FunctionName", "") - v.SetDefault("Openfaas.FunctionNamespace", "openfaas-fn") - v.SetDefault("Openfaas.GatewayPort", 8080) - v.SetDefault("Openfaas.Kubeconfig", "") - v.SetDefault("Openfaas.MinimumPriority", "") - v.SetDefault("Openfaas.MutualTls", false) - v.SetDefault("Openfaas.CheckCert", true) - - v.SetDefault("Fission.RouterNamespace", "fission") - v.SetDefault("Fission.RouterService", "router") - v.SetDefault("Fission.RouterPort", 80) - v.SetDefault("Fission.FunctionNamespace", "fission-function") - v.SetDefault("Fission.Function", "") - v.SetDefault("Fission.Kubeconfig", "") - v.SetDefault("Fission.MinimumPriority", "") - v.SetDefault("Fission.MutualTls", false) - v.SetDefault("Fission.CheckCert", true) - - v.SetDefault("Webui.URL", "") - v.SetDefault("Webui.MutualTls", false) - v.SetDefault("Webui.CheckCert", true) - - v.SetDefault("PolicyReport.Enabled", false) - v.SetDefault("PolicyReport.Kubeconfig", "") - v.SetDefault("PolicyReport.MinimumPriority", "") - v.SetDefault("PolicyReport.MaxEvents", 1000) - v.SetDefault("PolicyReport.FalcoNamespace", "") - v.SetDefault("PolicyReport.PruneByPriority", false) - - v.SetDefault("Rabbitmq.URL", "") - v.SetDefault("Rabbitmq.Queue", "") - v.SetDefault("Rabbitmq.MinimumPriority", "") - - v.SetDefault("Wavefront.EndpointType", "") - v.SetDefault("Wavefront.EndpointHost", "") - v.SetDefault("Wavefront.EndpointToken", "") - v.SetDefault("Wavefront.MetricName", "falco.alert") - v.SetDefault("Wavefront.EndpointMetricPort", 2878) - v.SetDefault("Wavefront.MinimumPriority", "") - v.SetDefault("Wavefront.FlushIntervalSecods", 1) - v.SetDefault("Wavefront.BatchSize", 10000) - - v.SetDefault("Grafana.HostPort", "") - v.SetDefault("Grafana.DashboardID", 0) - v.SetDefault("Grafana.PanelID", 0) - v.SetDefault("Grafana.APIKey", "") - v.SetDefault("Grafana.AllFieldsAsTags", false) - v.SetDefault("Grafana.MinimumPriority", "") - v.SetDefault("Grafana.MutualTls", false) - v.SetDefault("Grafana.CheckCert", true) - - v.SetDefault("GrafanaOnCall.WebhookURL", "") - v.SetDefault("GrafanaOnCall.MinimumPriority", "") - v.SetDefault("GrafanaOnCall.MutualTls", false) - v.SetDefault("GrafanaOnCall.CheckCert", true) - - v.SetDefault("Grafana.MinimumPriority", "") - v.SetDefault("Grafana.MutualTls", false) - v.SetDefault("Grafana.CheckCert", true) - v.SetDefault("Yandex.AccessKeyID", "") v.SetDefault("Yandex.SecretAccessKey", "") v.SetDefault("Yandex.Region", "ru-central1") @@ -422,89 +541,6 @@ func getConfig() *types.Configuration { v.SetDefault("Yandex.DataStreams.StreamName", "") v.SetDefault("Yandex.DataStreams.MinimumPriority", "") - v.SetDefault("Syslog.Host", "") - v.SetDefault("Syslog.Port", "") - v.SetDefault("Syslog.Protocol", "") - v.SetDefault("Syslog.Format", "json") - v.SetDefault("Syslog.MinimumPriority", "") - - v.SetDefault("MQTT.Broker", "") - v.SetDefault("MQTT.Topic", "falco/events") - v.SetDefault("MQTT.QOS", 0) - v.SetDefault("MQTT.Retained", false) - v.SetDefault("MQTT.User", "") - v.SetDefault("MQTT.Password", "") - v.SetDefault("MQTT.CheckCert", true) - v.SetDefault("MQTT.MinimumPriority", "") - - v.SetDefault("Zincsearch.HostPort", "") - v.SetDefault("Zincsearch.Index", "falco") - v.SetDefault("Zincsearch.Username", "") - v.SetDefault("Zincsearch.Password", "") - v.SetDefault("Zincsearch.CheckCert", true) - v.SetDefault("Zincsearch.MinimumPriority", "") - - v.SetDefault("Gotify.HostPort", "") - v.SetDefault("Gotify.Token", "") - v.SetDefault("Gotify.Format", "markdown") - v.SetDefault("Gotify.CheckCert", true) - v.SetDefault("Gotify.MinimumPriority", "") - - v.SetDefault("Tekton.EventListener", "") - v.SetDefault("Tekton.MinimumPriority", "") - v.SetDefault("Tekton.CheckCert", true) - - v.SetDefault("Spyderbat.OrgUID", "") - v.SetDefault("Spyderbat.APIKey", "") - v.SetDefault("Spyderbat.APIUrl", "https://api.spyderbat.com") - v.SetDefault("Spyderbat.Source", "falcosidekick") - v.SetDefault("Spyderbat.SourceDescription", "") - v.SetDefault("Spyderbat.MinimumPriority", "") - - v.SetDefault("TimescaleDB.Host", "") - v.SetDefault("TimescaleDB.Port", "5432") - v.SetDefault("TimescaleDB.User", "postgres") - v.SetDefault("TimescaleDB.Password", "postgres") - v.SetDefault("TimescaleDB.Database", "falcosidekick") - v.SetDefault("TimescaleDB.HypertableName", "falcosidekick_events") - v.SetDefault("TimescaleDB.MinimumPriority", "") - - v.SetDefault("Redis.Address", "") - v.SetDefault("Redis.Password", "") - v.SetDefault("Redis.Database", 0) - v.SetDefault("Redis.StorageType", "list") - v.SetDefault("Redis.Key", "falco") - v.SetDefault("Redis.MinimumPriority", "") - v.SetDefault("Redis.MutualTls", false) - v.SetDefault("Redis.CheckCert", true) - - v.SetDefault("N8n.Address", "") - v.SetDefault("N8n.User", "") - v.SetDefault("N8n.Password", "") - v.SetDefault("N8n.HeaderAuthName", "") - v.SetDefault("N8n.HeaderAuthValue", "") - v.SetDefault("N8n.MinimumPriority", "") - v.SetDefault("N8n.CheckCert", true) - - v.SetDefault("Telegram.Token", "") - v.SetDefault("Telegram.ChatID", "") - v.SetDefault("Telegram.MinimumPriority", "") - v.SetDefault("Telegram.CheckCert", true) - - v.SetDefault("OpenObserve.HostPort", "") - v.SetDefault("OpenObserve.OrganizationName", "default") - v.SetDefault("OpenObserve.StreamName", "falco") - v.SetDefault("OpenObserve.MinimumPriority", "") - v.SetDefault("OpenObserve.MutualTls", false) - v.SetDefault("OpenObserve.CheckCert", true) - v.SetDefault("OpenObserve.Username", "") - v.SetDefault("OpenObserve.Password", "") - - v.SetDefault("Dynatrace.APIToken", "") - v.SetDefault("Dynatrace.APIUrl", "") - v.SetDefault("Dynatrace.CheckCert", true) - v.SetDefault("Dynatrace.MinimumPriority", "") - v.SetDefault("OTLP.Traces.Endpoint", "") v.SetDefault("OTLP.Traces.Protocol", "http/json") // NOTE: we don't need to parse the OTLP.Traces.Headers field, as use it to @@ -520,10 +556,6 @@ 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 != "" { diff --git a/main.go b/main.go index dc0342465..cb5431827 100644 --- a/main.go +++ b/main.go @@ -147,7 +147,7 @@ func init() { if config.Slack.WebhookURL != "" { var err error - slackClient, err = outputs.NewClient("Slack", config.Slack.WebhookURL, config.Slack.MutualTLS, config.Slack.CheckCert, *initClientArgs) + slackClient, err = outputs.NewClient("Slack", config.Slack.WebhookURL, config.Slack.CommonConfig, *initClientArgs) if err != nil { config.Slack.WebhookURL = "" } else { @@ -157,7 +157,7 @@ func init() { if config.Cliq.WebhookURL != "" { var err error - cliqClient, err = outputs.NewClient("Cliq", config.Cliq.WebhookURL, config.Cliq.MutualTLS, config.Cliq.CheckCert, *initClientArgs) + cliqClient, err = outputs.NewClient("Cliq", config.Cliq.WebhookURL, config.Cliq.CommonConfig, *initClientArgs) if err != nil { config.Cliq.WebhookURL = "" } else { @@ -167,7 +167,7 @@ func init() { if config.Rocketchat.WebhookURL != "" { var err error - rocketchatClient, err = outputs.NewClient("Rocketchat", config.Rocketchat.WebhookURL, config.Rocketchat.MutualTLS, config.Rocketchat.CheckCert, *initClientArgs) + rocketchatClient, err = outputs.NewClient("Rocketchat", config.Rocketchat.WebhookURL, config.Rocketchat.CommonConfig, *initClientArgs) if err != nil { config.Rocketchat.WebhookURL = "" } else { @@ -177,7 +177,7 @@ func init() { if config.Mattermost.WebhookURL != "" { var err error - mattermostClient, err = outputs.NewClient("Mattermost", config.Mattermost.WebhookURL, config.Mattermost.MutualTLS, config.Mattermost.CheckCert, *initClientArgs) + mattermostClient, err = outputs.NewClient("Mattermost", config.Mattermost.WebhookURL, config.Mattermost.CommonConfig, *initClientArgs) if err != nil { config.Mattermost.WebhookURL = "" } else { @@ -187,7 +187,7 @@ func init() { if config.Teams.WebhookURL != "" { var err error - teamsClient, err = outputs.NewClient("Teams", config.Teams.WebhookURL, config.Teams.MutualTLS, config.Teams.CheckCert, *initClientArgs) + teamsClient, err = outputs.NewClient("Teams", config.Teams.WebhookURL, config.Teams.CommonConfig, *initClientArgs) if err != nil { config.Teams.WebhookURL = "" } else { @@ -198,7 +198,7 @@ func init() { if config.Datadog.APIKey != "" { var err error endpointUrl := fmt.Sprintf("%s?api_key=%s", config.Datadog.Host+outputs.DatadogPath, config.Datadog.APIKey) - datadogClient, err = outputs.NewClient("Datadog", endpointUrl, config.Datadog.MutualTLS, config.Datadog.CheckCert, *initClientArgs) + datadogClient, err = outputs.NewClient("Datadog", endpointUrl, config.Datadog.CommonConfig, *initClientArgs) if err != nil { config.Datadog.APIKey = "" } else { @@ -208,7 +208,7 @@ func init() { if config.Discord.WebhookURL != "" { var err error - discordClient, err = outputs.NewClient("Discord", config.Discord.WebhookURL, config.Discord.MutualTLS, config.Discord.CheckCert, *initClientArgs) + discordClient, err = outputs.NewClient("Discord", config.Discord.WebhookURL, config.Discord.CommonConfig, *initClientArgs) if err != nil { config.Discord.WebhookURL = "" } else { @@ -219,7 +219,7 @@ func init() { if config.Alertmanager.HostPort != "" { var err error endpointUrl := fmt.Sprintf("%s%s", config.Alertmanager.HostPort, config.Alertmanager.Endpoint) - alertmanagerClient, err = outputs.NewClient("AlertManager", endpointUrl, config.Alertmanager.MutualTLS, config.Alertmanager.CheckCert, *initClientArgs) + alertmanagerClient, err = outputs.NewClient("AlertManager", endpointUrl, config.Alertmanager.CommonConfig, *initClientArgs) if err != nil { config.Alertmanager.HostPort = "" } else { @@ -230,7 +230,7 @@ func init() { if config.Elasticsearch.HostPort != "" { var err error endpointUrl := fmt.Sprintf("%s/%s/%s", config.Elasticsearch.HostPort, config.Elasticsearch.Index, config.Elasticsearch.Type) - elasticsearchClient, err = outputs.NewClient("Elasticsearch", endpointUrl, config.Elasticsearch.MutualTLS, config.Elasticsearch.CheckCert, *initClientArgs) + elasticsearchClient, err = outputs.NewClient("Elasticsearch", endpointUrl, config.Elasticsearch.CommonConfig, *initClientArgs) if err != nil { config.Elasticsearch.HostPort = "" } else { @@ -251,7 +251,7 @@ func init() { var err error endpointUrl := fmt.Sprintf("%s/%s/%s/ingest", config.Quickwit.HostPort, config.Quickwit.ApiEndpoint, config.Quickwit.Index) - quickwitClient, err = outputs.NewClient("Quickwit", endpointUrl, config.Quickwit.MutualTLS, config.Quickwit.CheckCert, *initClientArgs) + quickwitClient, err = outputs.NewClient("Quickwit", endpointUrl, config.Quickwit.CommonConfig, *initClientArgs) if err == nil && config.Quickwit.AutoCreateIndex { err = quickwitClient.AutoCreateQuickwitIndex(*initClientArgs) } @@ -265,7 +265,7 @@ func init() { if config.Loki.HostPort != "" { var err error - lokiClient, err = outputs.NewClient("Loki", config.Loki.HostPort+config.Loki.Endpoint, config.Loki.MutualTLS, config.Loki.CheckCert, *initClientArgs) + lokiClient, err = outputs.NewClient("Loki", config.Loki.HostPort+config.Loki.Endpoint, config.Loki.CommonConfig, *initClientArgs) if err != nil { config.Loki.HostPort = "" } else { @@ -275,7 +275,7 @@ func init() { if config.SumoLogic.ReceiverURL != "" { var err error - sumologicClient, err = outputs.NewClient("SumoLogic", config.SumoLogic.ReceiverURL, false, config.SumoLogic.CheckCert, *initClientArgs) + sumologicClient, err = outputs.NewClient("SumoLogic", config.SumoLogic.ReceiverURL, config.SumoLogic.CommonConfig, *initClientArgs) if err != nil { config.SumoLogic.ReceiverURL = "" } else { @@ -285,7 +285,7 @@ func init() { if config.Nats.HostPort != "" { var err error - natsClient, err = outputs.NewClient("NATS", config.Nats.HostPort, config.Nats.MutualTLS, config.Nats.CheckCert, *initClientArgs) + natsClient, err = outputs.NewClient("NATS", config.Nats.HostPort, config.Nats.CommonConfig, *initClientArgs) if err != nil { config.Nats.HostPort = "" } else { @@ -295,7 +295,7 @@ func init() { if config.Stan.HostPort != "" && config.Stan.ClusterID != "" && config.Stan.ClientID != "" { var err error - stanClient, err = outputs.NewClient("STAN", config.Stan.HostPort, config.Stan.MutualTLS, config.Stan.CheckCert, *initClientArgs) + stanClient, err = outputs.NewClient("STAN", config.Stan.HostPort, config.Stan.CommonConfig, *initClientArgs) if err != nil { config.Stan.HostPort = "" config.Stan.ClusterID = "" @@ -320,7 +320,7 @@ func init() { } var err error - influxdbClient, err = outputs.NewClient("Influxdb", url, config.Influxdb.MutualTLS, config.Influxdb.CheckCert, *initClientArgs) + influxdbClient, err = outputs.NewClient("Influxdb", url, config.Influxdb.CommonConfig, *initClientArgs) if err != nil { config.Influxdb.HostPort = "" } else { @@ -402,7 +402,7 @@ func init() { if strings.ToLower(config.Opsgenie.Region) == "eu" { url = "https://api.eu.opsgenie.com/v2/alerts" } - opsgenieClient, err = outputs.NewClient("Opsgenie", url, config.Opsgenie.MutualTLS, config.Opsgenie.CheckCert, *initClientArgs) + opsgenieClient, err = outputs.NewClient("Opsgenie", url, config.Opsgenie.CommonConfig, *initClientArgs) if err != nil { config.Opsgenie.APIKey = "" } else { @@ -412,7 +412,7 @@ func init() { if config.Webhook.Address != "" { var err error - webhookClient, err = outputs.NewClient("Webhook", config.Webhook.Address, config.Webhook.MutualTLS, config.Webhook.CheckCert, *initClientArgs) + webhookClient, err = outputs.NewClient("Webhook", config.Webhook.Address, config.Webhook.CommonConfig, *initClientArgs) if err != nil { config.Webhook.Address = "" } else { @@ -422,7 +422,7 @@ func init() { if config.NodeRed.Address != "" { var err error - noderedClient, err = outputs.NewClient("NodeRed", config.NodeRed.Address, false, config.NodeRed.CheckCert, *initClientArgs) + noderedClient, err = outputs.NewClient("NodeRed", config.NodeRed.Address, config.NodeRed.CommonConfig, *initClientArgs) if err != nil { config.NodeRed.Address = "" } else { @@ -432,7 +432,7 @@ func init() { if config.CloudEvents.Address != "" { var err error - cloudeventsClient, err = outputs.NewClient("CloudEvents", config.CloudEvents.Address, config.CloudEvents.MutualTLS, config.CloudEvents.CheckCert, *initClientArgs) + cloudeventsClient, err = outputs.NewClient("CloudEvents", config.CloudEvents.Address, config.CloudEvents.CommonConfig, *initClientArgs) if err != nil { config.CloudEvents.Address = "" } else { @@ -478,7 +478,7 @@ func init() { var err error var outputName = "GCPCloudRun" - gcpCloudRunClient, err = outputs.NewClient(outputName, config.GCP.CloudRun.Endpoint, false, false, *initClientArgs) + gcpCloudRunClient, err = outputs.NewClient(outputName, config.GCP.CloudRun.Endpoint, types.CommonConfig{}, *initClientArgs) if err != nil { config.GCP.CloudRun.Endpoint = "" @@ -489,7 +489,7 @@ func init() { if config.Googlechat.WebhookURL != "" { var err error - googleChatClient, err = outputs.NewClient("Googlechat", config.Googlechat.WebhookURL, config.Googlechat.MutualTLS, config.Googlechat.CheckCert, *initClientArgs) + googleChatClient, err = outputs.NewClient("Googlechat", config.Googlechat.WebhookURL, config.Googlechat.CommonConfig, *initClientArgs) if err != nil { config.Googlechat.WebhookURL = "" } else { @@ -509,7 +509,7 @@ func init() { if config.KafkaRest.Address != "" { var err error - kafkaRestClient, err = outputs.NewClient("KafkaRest", config.KafkaRest.Address, config.KafkaRest.MutualTLS, config.KafkaRest.CheckCert, *initClientArgs) + kafkaRestClient, err = outputs.NewClient("KafkaRest", config.KafkaRest.Address, config.KafkaRest.CommonConfig, *initClientArgs) if err != nil { config.KafkaRest.Address = "" } else { @@ -522,7 +522,7 @@ func init() { var url = "https://events.pagerduty.com/v2/enqueue" var outputName = "Pagerduty" - pagerdutyClient, err = outputs.NewClient(outputName, url, config.Pagerduty.MutualTLS, config.Pagerduty.CheckCert, *initClientArgs) + pagerdutyClient, err = outputs.NewClient(outputName, url, config.Pagerduty.CommonConfig, *initClientArgs) if err != nil { config.Pagerduty.RoutingKey = "" @@ -545,7 +545,7 @@ func init() { if config.WebUI.URL != "" { var err error - webUIClient, err = outputs.NewClient("WebUI", config.WebUI.URL, config.WebUI.MutualTLS, config.WebUI.CheckCert, *initClientArgs) + webUIClient, err = outputs.NewClient("WebUI", config.WebUI.URL, config.WebUI.CommonConfig, *initClientArgs) if err != nil { config.WebUI.URL = "" } else { @@ -575,7 +575,7 @@ func init() { if config.Tekton.EventListener != "" { var err error - tektonClient, err = outputs.NewClient("Tekton", config.Tekton.EventListener, config.Tekton.MutualTLS, config.Tekton.CheckCert, *initClientArgs) + tektonClient, err = outputs.NewClient("Tekton", config.Tekton.EventListener, config.Tekton.CommonConfig, *initClientArgs) if err != nil { log.Printf("[ERROR] : Tekton - %v\n", err) } else { @@ -618,7 +618,7 @@ func init() { var err error var outputName = "Grafana" endpointUrl := fmt.Sprintf("%s/api/annotations", config.Grafana.HostPort) - grafanaClient, err = outputs.NewClient(outputName, endpointUrl, config.Grafana.MutualTLS, config.Grafana.CheckCert, *initClientArgs) + grafanaClient, err = outputs.NewClient(outputName, endpointUrl, config.Grafana.CommonConfig, *initClientArgs) if err != nil { config.Grafana.HostPort = "" config.Grafana.APIKey = "" @@ -630,7 +630,7 @@ func init() { if config.GrafanaOnCall.WebhookURL != "" { var err error var outputName = "GrafanaOnCall" - grafanaOnCallClient, err = outputs.NewClient(outputName, config.GrafanaOnCall.WebhookURL, config.GrafanaOnCall.MutualTLS, config.GrafanaOnCall.CheckCert, *initClientArgs) + grafanaOnCallClient, err = outputs.NewClient(outputName, config.GrafanaOnCall.WebhookURL, config.GrafanaOnCall.CommonConfig, *initClientArgs) if err != nil { config.GrafanaOnCall.WebhookURL = "" } else { @@ -689,7 +689,7 @@ func init() { if config.Zincsearch.HostPort != "" { var err error endpointUrl := fmt.Sprintf("%s/api/%s/_doc", config.Zincsearch.HostPort, config.Zincsearch.Index) - zincsearchClient, err = outputs.NewClient("Zincsearch", endpointUrl, false, config.Zincsearch.CheckCert, *initClientArgs) + zincsearchClient, err = outputs.NewClient("Zincsearch", endpointUrl, types.CommonConfig{CheckCert: config.Zincsearch.CheckCert}, *initClientArgs) if err != nil { config.Zincsearch.HostPort = "" } else { @@ -700,7 +700,7 @@ func init() { if config.Gotify.HostPort != "" { var err error endpointUrl := fmt.Sprintf("%s/message", config.Gotify.HostPort) - gotifyClient, err = outputs.NewClient("Gotify", endpointUrl, false, config.Gotify.CheckCert, *initClientArgs) + gotifyClient, err = outputs.NewClient("Gotify", endpointUrl, types.CommonConfig{CheckCert: config.Gotify.CheckCert}, *initClientArgs) if err != nil { config.Gotify.HostPort = "" } else { @@ -744,7 +744,7 @@ func init() { var err error var urlFormat = "https://api.telegram.org/bot%s/sendMessage" - telegramClient, err = outputs.NewClient("Telegram", fmt.Sprintf(urlFormat, config.Telegram.Token), false, config.Telegram.CheckCert, *initClientArgs) + telegramClient, err = outputs.NewClient("Telegram", fmt.Sprintf(urlFormat, config.Telegram.Token), types.CommonConfig{CheckCert: config.Telegram.CheckCert}, *initClientArgs) if err != nil { config.Telegram.ChatID = "" @@ -758,7 +758,7 @@ func init() { if config.N8N.Address != "" { var err error - n8nClient, err = outputs.NewClient("n8n", config.N8N.Address, false, config.N8N.CheckCert, *initClientArgs) + n8nClient, err = outputs.NewClient("n8n", config.N8N.Address, types.CommonConfig{CheckCert: config.N8N.CheckCert}, *initClientArgs) if err != nil { config.N8N.Address = "" } else { @@ -769,7 +769,7 @@ func init() { if config.OpenObserve.HostPort != "" { var err error endpointUrl := fmt.Sprintf("%s/api/%s/%s/_multi", config.OpenObserve.HostPort, config.OpenObserve.OrganizationName, config.OpenObserve.StreamName) - openObserveClient, err = outputs.NewClient("OpenObserve", endpointUrl, config.OpenObserve.MutualTLS, config.OpenObserve.CheckCert, *initClientArgs) + openObserveClient, err = outputs.NewClient("OpenObserve", endpointUrl, config.OpenObserve.CommonConfig, *initClientArgs) if err != nil { config.OpenObserve.HostPort = "" } else { @@ -780,7 +780,7 @@ func init() { if config.Dynatrace.APIToken != "" && config.Dynatrace.APIUrl != "" { var err error dynatraceApiUrl := strings.TrimRight(config.Dynatrace.APIUrl, "/") + "/v2/logs/ingest" - dynatraceClient, err = outputs.NewClient("Dynatrace", dynatraceApiUrl, false, config.Dynatrace.CheckCert, *initClientArgs) + dynatraceClient, err = outputs.NewClient("Dynatrace", dynatraceApiUrl, types.CommonConfig{CheckCert: config.Dynatrace.CheckCert}, *initClientArgs) if err != nil { config.Dynatrace.APIToken = "" config.Dynatrace.APIUrl = "" @@ -802,7 +802,7 @@ func init() { if config.Talon.Address != "" { var err error - talonClient, err = outputs.NewClient("Talon", config.Talon.Address, false, config.Talon.CheckCert, *initClientArgs) + talonClient, err = outputs.NewClient("Talon", config.Talon.Address, types.CommonConfig{CheckCert: config.Talon.CheckCert}, *initClientArgs) if err != nil { config.Talon.Address = "" } else { diff --git a/outputs/alertmanager.go b/outputs/alertmanager.go index 0abafd7ef..ea4437595 100644 --- a/outputs/alertmanager.go +++ b/outputs/alertmanager.go @@ -5,6 +5,7 @@ package outputs import ( "encoding/json" "log" + "net/http" "regexp" "sort" "strconv" @@ -130,13 +131,12 @@ func newAlertmanagerPayload(falcopayload types.FalcoPayload, config *types.Confi // AlertmanagerPost posts event to AlertManager func (c *Client) AlertmanagerPost(falcopayload types.FalcoPayload) { c.Stats.Alertmanager.Add(Total, 1) - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - for i, j := range c.Config.Alertmanager.CustomHeaders { - c.AddHeader(i, j) - } - err := c.Post(newAlertmanagerPayload(falcopayload, c.Config)) + err := c.Post(newAlertmanagerPayload(falcopayload, c.Config), func(req *http.Request) { + for i, j := range c.Config.Alertmanager.CustomHeaders { + req.Header.Set(i, j) + } + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:alertmanager", "status:error"}) c.Stats.Alertmanager.Add(Error, 1) diff --git a/outputs/client.go b/outputs/client.go index c7b1e283d..bd95ec44f 100644 --- a/outputs/client.go +++ b/outputs/client.go @@ -5,14 +5,15 @@ package outputs import ( "bytes" "compress/gzip" + "context" "crypto/tls" "crypto/x509" - "encoding/base64" "encoding/json" "errors" "fmt" "io" "log" + "math" "net/http" "net/url" "os" @@ -20,6 +21,7 @@ import ( "strings" "sync" + "golang.org/x/sync/semaphore" crdClient "sigs.k8s.io/wg-policy-prototypes/policy-report/pkg/generated/v1alpha2/clientset/versioned" gcpfunctions "cloud.google.com/go/functions/apiv1" @@ -92,19 +94,10 @@ const MutualTLSCacertFilename = "/ca.crt" const HttpPost = "POST" const HttpPut = "PUT" -// Headers to add to the client before sending the request -type Header struct { - Key string - Value string -} - // Client communicates with the different API. type Client struct { OutputType string EndpointURL *url.URL - MutualTLSEnabled bool - CheckCert bool - HeaderList []Header ContentType string ShutDownFunc func() Config *types.Configuration @@ -115,8 +108,6 @@ type Client struct { DogstatsdClient *statsd.Client GCPTopicClient *pubsub.Topic GCPCloudFunctionsClient *gcpfunctions.CloudFunctionsClient - // FIXME: this lock requires a per-output usage lock currently if headers are used -- needs to be refactored - httpClientLock sync.Mutex GCSStorageClient *storage.Client KafkaProducer *kafka.Writer @@ -132,11 +123,15 @@ type Client struct { // cached http.Client httpcli *http.Client // lock for http client creation - mx sync.Mutex + mx sync.Mutex + cfg types.CommonConfig + + initOnce sync.Once + sem *semaphore.Weighted } // InitClient returns a new output.Client for accessing the different API. -func NewClient(outputType string, defaultEndpointURL string, mutualTLSEnabled bool, checkCert bool, params types.InitClientArgs) (*Client, error) { +func NewClient(outputType string, defaultEndpointURL string, cfg types.CommonConfig, params types.InitClientArgs) (*Client, error) { reg := regexp.MustCompile(`(http|nats)(s?)://.*`) if !reg.MatchString(defaultEndpointURL) { log.Printf("[ERROR] : %v - %v\n", outputType, "Bad Endpoint") @@ -151,22 +146,34 @@ func NewClient(outputType string, defaultEndpointURL string, mutualTLSEnabled bo log.Printf("[ERROR] : %v - %v\n", outputType, err.Error()) return nil, ErrClientCreation } - return &Client{OutputType: outputType, EndpointURL: endpointURL, MutualTLSEnabled: mutualTLSEnabled, CheckCert: checkCert, HeaderList: []Header{}, ContentType: DefaultContentType, Config: params.Config, Stats: params.Stats, PromStats: params.PromStats, StatsdClient: params.StatsdClient, DogstatsdClient: params.DogstatsdClient}, nil + return &Client{ + cfg: cfg, + OutputType: outputType, + EndpointURL: endpointURL, + ContentType: DefaultContentType, + Config: params.Config, + Stats: params.Stats, + PromStats: params.PromStats, + StatsdClient: params.StatsdClient, + DogstatsdClient: params.DogstatsdClient, + }, nil } +type RequestOptionFunc func(req *http.Request) + // Get get a payload from Output with GET http method. -func (c *Client) Get() error { - return c.sendRequest("GET", nil) +func (c *Client) Get(opts ...RequestOptionFunc) error { + return c.sendRequest("GET", nil, opts...) } // Post sends event (payload) to Output with POST http method. -func (c *Client) Post(payload interface{}) error { - return c.sendRequest("POST", payload) +func (c *Client) Post(payload interface{}, opts ...RequestOptionFunc) error { + return c.sendRequest("POST", payload, opts...) } // Put sends event (payload) to Output with PUT http method. -func (c *Client) Put(payload interface{}) error { - return c.sendRequest("PUT", payload) +func (c *Client) Put(payload interface{}, opts ...RequestOptionFunc) error { + return c.sendRequest("PUT", payload, opts...) } // Get the response body as inlined string @@ -185,7 +192,18 @@ func getInlinedBodyAsString(resp *http.Response) string { } // Post sends event (payload) to Output. -func (c *Client) sendRequest(method string, payload interface{}) error { +func (c *Client) sendRequest(method string, payload interface{}, opts ...RequestOptionFunc) error { + // Initialize the semaphore once here + // because currently there are multiple code paths + // where the client is created directly without using NewClient constructor + c.initOnce.Do(func() { + if c.cfg.MaxConcurrentRequests == 0 { + c.sem = semaphore.NewWeighted(math.MaxInt64) + } else { + c.sem = semaphore.NewWeighted(int64(c.cfg.MaxConcurrentRequests)) + } + }) + // defer + recover to catch panic if output doesn't respond defer func(c *Client) { if err := recover(); err != nil { @@ -239,13 +257,25 @@ func (c *Client) sendRequest(method string, payload interface{}) error { req.Header.Add(ContentTypeHeaderKey, c.ContentType) req.Header.Add(UserAgentHeaderKey, UserAgentHeaderValue) - for _, headerObj := range c.HeaderList { - req.Header.Set(headerObj.Key, headerObj.Value) + // Call request options functions + // Allows the clients to adjust request as needed + for _, opt := range opts { + opt(req) + } + + // Using the background context for now + // TODO: Eventually pass the proper context to sendRequest, and pass it to NewRequest call as well + // in order to make the requests cancellable + ctx := context.Background() + err = c.sem.Acquire(ctx, 1) + if err != nil { + log.Printf("[ERROR] : %v - %v\n", c.OutputType, err.Error()) + return err } + defer c.sem.Release(1) resp, err := client.Do(req) if err != nil { - c.HeaderList = []Header{} log.Printf("[ERROR] : %v - %v\n", c.OutputType, err.Error()) go c.CountMetric("outputs", 1, []string{"output:" + strings.ToLower(c.OutputType), "status:connectionrefused"}) return err @@ -253,7 +283,6 @@ func (c *Client) sendRequest(method string, payload interface{}) error { defer resp.Body.Close() // Clear out headers - they will be set for the next request. - c.HeaderList = []Header{} go c.CountMetric("outputs", 1, []string{"output:" + strings.ToLower(c.OutputType), "status:" + strings.ToLower(http.StatusText(resp.StatusCode))}) switch resp.StatusCode { @@ -348,7 +377,7 @@ func (c *Client) configureTransport() (*http.Transport, error) { customTransport.TLSClientConfig.RootCAs.AppendCertsFromPEM(caCert) } - if c.MutualTLSEnabled { + if c.cfg.MutualTLS { // Load client cert var MutualTLSClientCertPath, MutualTLSClientKeyPath, MutualTLSClientCaCertPath string if c.Config.MutualTLSClient.CertFile != "" { @@ -380,7 +409,7 @@ func (c *Client) configureTransport() (*http.Transport, error) { customTransport.TLSClientConfig.Certificates = []tls.Certificate{cert} } else { // With MutualTLS enabled, the check cert flag is ignored - if !c.CheckCert { + if !c.cfg.CheckCert { customTransport.TLSClientConfig = &tls.Config{ InsecureSkipVerify: true, // #nosec G402 This is only set as a result of explicit configuration } @@ -388,18 +417,3 @@ func (c *Client) configureTransport() (*http.Transport, error) { } return customTransport, nil } - -// BasicAuth adds an HTTP Basic Authentication compliant header to the Client. -func (c *Client) BasicAuth(username, password string) { - // Check out RFC7617 for the specifics on this code. - // https://datatracker.ietf.org/doc/html/rfc7617 - // This might break I18n, but we can cross that bridge when we come to it. - userPass := username + ":" + password - b64UserPass := base64.StdEncoding.EncodeToString([]byte(userPass)) - c.AddHeader(AuthorizationHeaderKey, "Basic "+b64UserPass) -} - -// AddHeader adds an HTTP Header to the Client. -func (c *Client) AddHeader(key, value string) { - c.HeaderList = append(c.HeaderList, Header{Key: key, Value: value}) -} diff --git a/outputs/client_test.go b/outputs/client_test.go index 58ba78396..ce52ed35e 100644 --- a/outputs/client_test.go +++ b/outputs/client_test.go @@ -43,11 +43,11 @@ func TestNewClient(t *testing.T) { PromStats: promStats, } - testClientOutput := Client{OutputType: "test", EndpointURL: u, MutualTLSEnabled: false, CheckCert: true, HeaderList: []Header{}, ContentType: "application/json; charset=utf-8", Config: config, Stats: stats, PromStats: promStats} - _, err := NewClient("test", "localhost/%*$¨^!/:;", false, true, *initClientArgs) + testClientOutput := Client{OutputType: "test", EndpointURL: u, cfg: types.CommonConfig{CheckCert: true}, ContentType: "application/json; charset=utf-8", Config: config, Stats: stats, PromStats: promStats} + _, err := NewClient("test", "localhost/%*$¨^!/:;", types.CommonConfig{CheckCert: true}, *initClientArgs) require.NotNil(t, err) - nc, err := NewClient("test", "http://localhost", false, true, *initClientArgs) + nc, err := NewClient("test", "http://localhost", types.CommonConfig{CheckCert: true}, *initClientArgs) require.Nil(t, err) require.Equal(t, &testClientOutput, nc) } @@ -91,7 +91,7 @@ func TestPost(t *testing.T) { Stats: &types.Statistics{}, PromStats: &types.PromStatistics{}, } - nc, err := NewClient("", ts.URL+i, false, true, *initClientArgs) + nc, err := NewClient("", ts.URL+i, types.CommonConfig{CheckCert: true}, *initClientArgs) require.Nil(t, err) require.NotEmpty(t, nc) @@ -111,13 +111,13 @@ func TestAddHeader(t *testing.T) { Stats: &types.Statistics{}, PromStats: &types.PromStatistics{}, } - nc, err := NewClient("", ts.URL, false, true, *initClientArgs) + nc, err := NewClient("", ts.URL, types.CommonConfig{CheckCert: true}, *initClientArgs) require.Nil(t, err) require.NotEmpty(t, nc) - nc.AddHeader(headerKey, headerVal) - - nc.Post("") + nc.Post("", func(req *http.Request) { + req.Header.Set(headerKey, headerVal) + }) } func TestAddBasicAuth(t *testing.T) { @@ -167,13 +167,13 @@ func TestAddBasicAuth(t *testing.T) { Stats: &types.Statistics{}, PromStats: &types.PromStatistics{}, } - nc, err := NewClient("", ts.URL, false, true, *initClientArgs) + nc, err := NewClient("", ts.URL, types.CommonConfig{CheckCert: true}, *initClientArgs) require.Nil(t, err) require.NotEmpty(t, nc) - nc.BasicAuth(username, password) - - nc.Post("") + nc.Post("", func(req *http.Request) { + req.SetBasicAuth(username, password) + }) } func TestHeadersResetAfterReq(t *testing.T) { @@ -188,17 +188,17 @@ func TestHeadersResetAfterReq(t *testing.T) { Stats: &types.Statistics{}, PromStats: &types.PromStatistics{}, } - nc, err := NewClient("", ts.URL, false, true, *initClientArgs) + nc, err := NewClient("", ts.URL, types.CommonConfig{CheckCert: true}, *initClientArgs) require.Nil(t, err) require.NotEmpty(t, nc) - nc.AddHeader(headerKey, headerVal) - - nc.Post("") - - nc.AddHeader(headerKey, headerVal) + nc.Post("", func(req *http.Request) { + req.Header.Set(headerKey, headerVal) + }) - nc.Post("") + nc.Post("", func(req *http.Request) { + req.Header.Set(headerKey, headerVal) + }) } func TestMutualTlsPost(t *testing.T) { @@ -239,7 +239,7 @@ func TestMutualTlsPost(t *testing.T) { Stats: &types.Statistics{}, PromStats: &types.PromStatistics{}, } - nc, err := NewClient("", server.URL+Status200, true, true, *initClientArgs) + nc, err := NewClient("", server.URL+Status200, types.CommonConfig{MutualTLS: true, CheckCert: true}, *initClientArgs) require.Nil(t, err) require.NotEmpty(t, nc) diff --git a/outputs/cliq.go b/outputs/cliq.go index 329c5fda8..1580edf7d 100644 --- a/outputs/cliq.go +++ b/outputs/cliq.go @@ -6,6 +6,7 @@ import ( "bytes" "fmt" "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -164,10 +165,9 @@ func newCliqPayload(falcopayload types.FalcoPayload, config *types.Configuration func (c *Client) CliqPost(falcopayload types.FalcoPayload) { c.Stats.Cliq.Add(Total, 1) - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader(ContentTypeHeaderKey, "application/json") - err := c.Post(newCliqPayload(falcopayload, c.Config)) + err := c.Post(newCliqPayload(falcopayload, c.Config), func(req *http.Request) { + req.Header.Set(ContentTypeHeaderKey, "application/json") + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:cliq", "status:error"}) c.Stats.Cliq.Add(Error, 1) diff --git a/outputs/dynatrace.go b/outputs/dynatrace.go index 576077a22..3f9cc113b 100644 --- a/outputs/dynatrace.go +++ b/outputs/dynatrace.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "regexp" "strconv" "time" @@ -114,11 +115,9 @@ func (c *Client) DynatracePost(falcopayload types.FalcoPayload) { c.ContentType = DynatraceContentType - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader("Authorization", "Api-Token "+c.Config.Dynatrace.APIToken) - - err := c.Post(newDynatracePayload(falcopayload).Payload) + err := c.Post(newDynatracePayload(falcopayload).Payload, func(req *http.Request) { + req.Header.Set("Authorization", "Api-Token "+c.Config.Dynatrace.APIToken) + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:dynatrace", "status:error"}) c.Stats.Dynatrace.Add(Error, 1) diff --git a/outputs/elasticsearch.go b/outputs/elasticsearch.go index 878d37476..ceaa2555b 100644 --- a/outputs/elasticsearch.go +++ b/outputs/elasticsearch.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "net/url" "regexp" "strings" @@ -56,15 +57,6 @@ func (c *Client) ElasticsearchPost(falcopayload types.FalcoPayload) { } c.EndpointURL = endpointURL - if c.Config.Elasticsearch.Username != "" && c.Config.Elasticsearch.Password != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.BasicAuth(c.Config.Elasticsearch.Username, c.Config.Elasticsearch.Password) - } - - for i, j := range c.Config.Elasticsearch.CustomHeaders { - c.AddHeader(i, j) - } payload := eSPayload{FalcoPayload: falcopayload, Timestamp: falcopayload.Time} if c.Config.Elasticsearch.FlattenFields || c.Config.Elasticsearch.CreateIndexTemplate { @@ -74,7 +66,17 @@ func (c *Client) ElasticsearchPost(falcopayload types.FalcoPayload) { } } - err = c.Post(payload) + reqOpt := func(req *http.Request) { + if c.Config.Elasticsearch.Username != "" && c.Config.Elasticsearch.Password != "" { + req.SetBasicAuth(c.Config.Elasticsearch.Username, c.Config.Elasticsearch.Password) + } + + for i, j := range c.Config.Elasticsearch.CustomHeaders { + req.Header.Set(i, j) + } + } + + err = c.Post(payload, reqOpt) if err != nil { var mappingErr mappingError if err2 := json.Unmarshal([]byte(err.Error()), &mappingErr); err2 != nil { @@ -99,9 +101,8 @@ func (c *Client) ElasticsearchPost(falcopayload types.FalcoPayload) { delete(payload.OutputFields, i) } } - fmt.Println(payload.OutputFields) log.Printf("[INFO] : %v - %v\n", c.OutputType, "attempt to POST again the payload without the wrong field") - err = c.Post(payload) + err = c.Post(payload, reqOpt) if err != nil { c.setElasticSearchErrorMetrics() return diff --git a/outputs/fission.go b/outputs/fission.go index a8bcb3c8e..3bbf738f6 100644 --- a/outputs/fission.go +++ b/outputs/fission.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "strconv" "github.com/DataDog/datadog-go/statsd" @@ -44,6 +45,7 @@ func NewFissionClient(config *types.Configuration, stats *types.Statistics, prom StatsdClient: statsdClient, DogstatsdClient: dogstatsdClient, KubernetesClient: clientset, + cfg: config.Fission.CommonConfig, }, nil } @@ -56,7 +58,7 @@ func NewFissionClient(config *types.Configuration, stats *types.Statistics, prom StatsdClient: statsdClient, } - return NewClient(Fission, endpointUrl, config.Fission.MutualTLS, config.Fission.CheckCert, *initClientArgs) + return NewClient(Fission, endpointUrl, config.Fission.CommonConfig, *initClientArgs) } // FissionCall . @@ -84,12 +86,11 @@ func (c *Client) FissionCall(falcopayload types.FalcoPayload) { } log.Printf("[INFO] : %s - Function Response : %v\n", Fission, string(rawbody)) } else { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader(FissionEventIDKey, uuid.New().String()) c.ContentType = FissionContentType - err := c.Post(falcopayload) + err := c.Post(falcopayload, func(req *http.Request) { + req.Header.Set(FissionEventIDKey, uuid.New().String()) + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:Fission", "status:error"}) c.Stats.Fission.Add(Error, 1) diff --git a/outputs/gcpcloudrun.go b/outputs/gcpcloudrun.go index 34247d8ea..c163b9242 100644 --- a/outputs/gcpcloudrun.go +++ b/outputs/gcpcloudrun.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -12,13 +13,11 @@ import ( func (c *Client) CloudRunFunctionPost(falcopayload types.FalcoPayload) { c.Stats.GCPCloudRun.Add(Total, 1) - if c.Config.GCP.CloudRun.JWT != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader(AuthorizationHeaderKey, Bearer+" "+c.Config.GCP.CloudRun.JWT) - } - - err := c.Post(falcopayload) + err := c.Post(falcopayload, func(req *http.Request) { + if c.Config.GCP.CloudRun.JWT != "" { + req.Header.Set(AuthorizationHeaderKey, Bearer+" "+c.Config.GCP.CloudRun.JWT) + } + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:gcpcloudrun", "status:error"}) c.Stats.GCPCloudRun.Add(Error, 1) diff --git a/outputs/gotify.go b/outputs/gotify.go index 015b9c716..654c02723 100644 --- a/outputs/gotify.go +++ b/outputs/gotify.go @@ -6,6 +6,7 @@ import ( "bytes" "encoding/json" "log" + "net/http" "strings" textTemplate "text/template" @@ -93,13 +94,11 @@ func newGotifyPayload(falcopayload types.FalcoPayload, config *types.Configurati func (c *Client) GotifyPost(falcopayload types.FalcoPayload) { c.Stats.Gotify.Add(Total, 1) - if c.Config.Gotify.Token != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader("X-Gotify-Key", c.Config.Gotify.Token) - } - - err := c.Post(newGotifyPayload(falcopayload, c.Config)) + err := c.Post(newGotifyPayload(falcopayload, c.Config), func(req *http.Request) { + if c.Config.Gotify.Token != "" { + req.Header.Set("X-Gotify-Key", c.Config.Gotify.Token) + } + }) if err != nil { c.setGotifyErrorMetrics() log.Printf("[ERROR] : Gotify - %v\n", err) diff --git a/outputs/grafana.go b/outputs/grafana.go index cc4afd63a..7bc9af5d3 100644 --- a/outputs/grafana.go +++ b/outputs/grafana.go @@ -5,6 +5,7 @@ package outputs import ( "fmt" "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -78,14 +79,13 @@ func newGrafanaOnCallPayload(falcopayload types.FalcoPayload) grafanaOnCallPaylo func (c *Client) GrafanaPost(falcopayload types.FalcoPayload) { c.Stats.Grafana.Add(Total, 1) c.ContentType = GrafanaContentType - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader("Authorization", Bearer+" "+c.Config.Grafana.APIKey) - for i, j := range c.Config.Grafana.CustomHeaders { - c.AddHeader(i, j) - } - err := c.Post(newGrafanaPayload(falcopayload, c.Config)) + err := c.Post(newGrafanaPayload(falcopayload, c.Config), func(req *http.Request) { + req.Header.Set("Authorization", Bearer+" "+c.Config.Grafana.APIKey) + for i, j := range c.Config.Grafana.CustomHeaders { + req.Header.Set(i, j) + } + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:grafana", "status:error"}) c.Stats.Grafana.Add(Error, 1) @@ -103,13 +103,13 @@ func (c *Client) GrafanaPost(falcopayload types.FalcoPayload) { func (c *Client) GrafanaOnCallPost(falcopayload types.FalcoPayload) { c.Stats.GrafanaOnCall.Add(Total, 1) c.ContentType = GrafanaContentType - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - for i, j := range c.Config.GrafanaOnCall.CustomHeaders { - c.AddHeader(i, j) - } - err := c.Post(newGrafanaOnCallPayload(falcopayload)) + err := c.Post(newGrafanaOnCallPayload(falcopayload), func(req *http.Request) { + for i, j := range c.Config.GrafanaOnCall.CustomHeaders { + req.Header.Set(i, j) + } + }) + if err != nil { go c.CountMetric(Outputs, 1, []string{"output:grafanaoncall", "status:error"}) c.Stats.Grafana.Add(Error, 1) diff --git a/outputs/influxdb.go b/outputs/influxdb.go index 2d6f206c9..ecb5612c1 100644 --- a/outputs/influxdb.go +++ b/outputs/influxdb.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "strings" "github.com/falcosecurity/falcosidekick/types" @@ -40,15 +41,13 @@ func newInfluxdbPayload(falcopayload types.FalcoPayload) influxdbPayload { func (c *Client) InfluxdbPost(falcopayload types.FalcoPayload) { c.Stats.Influxdb.Add(Total, 1) - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader("Accept", "application/json") + err := c.Post(newInfluxdbPayload(falcopayload), func(req *http.Request) { + req.Header.Set("Accept", "application/json") - if c.Config.Influxdb.Token != "" { - c.AddHeader("Authorization", "Token "+c.Config.Influxdb.Token) - } - - err := c.Post(newInfluxdbPayload(falcopayload)) + if c.Config.Influxdb.Token != "" { + req.Header.Set("Authorization", "Token "+c.Config.Influxdb.Token) + } + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:influxdb", "status:error"}) c.Stats.Influxdb.Add(Error, 1) diff --git a/outputs/kubeless.go b/outputs/kubeless.go index 2a962a597..c8400c77f 100644 --- a/outputs/kubeless.go +++ b/outputs/kubeless.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "strconv" "github.com/DataDog/datadog-go/statsd" @@ -44,6 +45,7 @@ func NewKubelessClient(config *types.Configuration, stats *types.Statistics, pro StatsdClient: statsdClient, DogstatsdClient: dogstatsdClient, KubernetesClient: clientset, + cfg: config.Kubeless.CommonConfig, }, nil } @@ -56,7 +58,7 @@ func NewKubelessClient(config *types.Configuration, stats *types.Statistics, pro StatsdClient: statsdClient, } - return NewClient("Kubeless", endpointUrl, config.Kubeless.MutualTLS, config.Kubeless.CheckCert, *initClientArgs) + return NewClient("Kubeless", endpointUrl, config.Kubeless.CommonConfig, *initClientArgs) } // KubelessCall . @@ -83,14 +85,13 @@ func (c *Client) KubelessCall(falcopayload types.FalcoPayload) { } log.Printf("[INFO] : Kubeless - Function Response : %v\n", string(rawbody)) } else { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader(KubelessEventIDKey, uuid.New().String()) - c.AddHeader(KubelessEventTypeKey, KubelessEventTypeValue) - c.AddHeader(KubelessEventNamespaceKey, c.Config.Kubeless.Namespace) c.ContentType = KubelessContentType - err := c.Post(falcopayload) + err := c.Post(falcopayload, func(req *http.Request) { + req.Header.Set(KubelessEventIDKey, uuid.New().String()) + req.Header.Set(KubelessEventTypeKey, KubelessEventTypeValue) + req.Header.Set(KubelessEventNamespaceKey, c.Config.Kubeless.Namespace) + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:kubeless", "status:error"}) c.Stats.Kubeless.Add(Error, 1) diff --git a/outputs/loki.go b/outputs/loki.go index c2228b657..e6cd8281d 100644 --- a/outputs/loki.go +++ b/outputs/loki.go @@ -5,6 +5,7 @@ package outputs import ( "fmt" "log" + "net/http" "sort" "strings" @@ -66,27 +67,21 @@ func newLokiPayload(falcopayload types.FalcoPayload, config *types.Configuration }} } -func (c *Client) configureTenant() { - if c.Config.Loki.Tenant != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader("X-Scope-OrgID", c.Config.Loki.Tenant) +func lokiConfigureTenant(cfg *types.Configuration, req *http.Request) { + if cfg.Loki.Tenant != "" { + req.Header.Set("X-Scope-OrgID", cfg.Loki.Tenant) } } -func (c *Client) configureAuth() { - if c.Config.Loki.User != "" && c.Config.Loki.APIKey != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.BasicAuth(c.Config.Loki.User, c.Config.Loki.APIKey) +func lokiConfigureAuth(cfg *types.Configuration, req *http.Request) { + if cfg.Loki.User != "" && cfg.Loki.APIKey != "" { + req.SetBasicAuth(cfg.Loki.User, cfg.Loki.APIKey) } } -func (c *Client) configureCustomHeaders() { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - for i, j := range c.Config.Loki.CustomHeaders { - c.AddHeader(i, j) +func lokiConfigureCustomHeaders(cfg *types.Configuration, req *http.Request) { + for i, j := range cfg.Loki.CustomHeaders { + req.Header.Set(i, j) } } @@ -95,11 +90,12 @@ func (c *Client) LokiPost(falcopayload types.FalcoPayload) { c.Stats.Loki.Add(Total, 1) c.ContentType = LokiContentType - c.configureTenant() - c.configureAuth() - c.configureCustomHeaders() + err := c.Post(newLokiPayload(falcopayload, c.Config), func(req *http.Request) { + lokiConfigureTenant(c.Config, req) + lokiConfigureAuth(c.Config, req) + lokiConfigureCustomHeaders(c.Config, req) + }) - err := c.Post(newLokiPayload(falcopayload, c.Config)) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:loki", "status:error"}) c.Stats.Loki.Add(Error, 1) diff --git a/outputs/n8n.go b/outputs/n8n.go index 75c6d13c2..f5e829c6b 100644 --- a/outputs/n8n.go +++ b/outputs/n8n.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -12,19 +13,15 @@ import ( func (c *Client) N8NPost(falcopayload types.FalcoPayload) { c.Stats.N8N.Add(Total, 1) - if c.Config.N8N.User != "" && c.Config.N8N.Password != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.BasicAuth(c.Config.N8N.User, c.Config.N8N.Password) - } - - if c.Config.N8N.HeaderAuthName != "" && c.Config.N8N.HeaderAuthValue != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader(c.Config.N8N.HeaderAuthName, c.Config.N8N.HeaderAuthValue) - } + err := c.Post(falcopayload, func(req *http.Request) { + if c.Config.N8N.User != "" && c.Config.N8N.Password != "" { + req.SetBasicAuth(c.Config.N8N.User, c.Config.N8N.Password) + } - err := c.Post(falcopayload) + if c.Config.N8N.HeaderAuthName != "" && c.Config.N8N.HeaderAuthValue != "" { + req.Header.Set(c.Config.N8N.HeaderAuthName, c.Config.N8N.HeaderAuthValue) + } + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:n8n", "status:error"}) c.Stats.N8N.Add(Error, 1) diff --git a/outputs/nodered.go b/outputs/nodered.go index 662e6fbe2..9428c8041 100644 --- a/outputs/nodered.go +++ b/outputs/nodered.go @@ -3,8 +3,8 @@ package outputs import ( - "encoding/base64" "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -13,19 +13,15 @@ import ( func (c *Client) NodeRedPost(falcopayload types.FalcoPayload) { c.Stats.NodeRed.Add(Total, 1) - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - if c.Config.NodeRed.User != "" && c.Config.NodeRed.Password != "" { - c.AddHeader("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(c.Config.NodeRed.User+":"+c.Config.NodeRed.Password))) - } + err := c.Post(falcopayload, func(req *http.Request) { + if c.Config.NodeRed.User != "" && c.Config.NodeRed.Password != "" { + req.SetBasicAuth(c.Config.NodeRed.User, c.Config.NodeRed.Password) + } - if len(c.Config.NodeRed.CustomHeaders) != 0 { for i, j := range c.Config.NodeRed.CustomHeaders { - c.AddHeader(i, j) + req.Header.Set(i, j) } - } - - err := c.Post(falcopayload) + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:nodered", "status:error"}) c.Stats.NodeRed.Add(Error, 1) diff --git a/outputs/openfaas.go b/outputs/openfaas.go index 650ec6342..238e90a99 100644 --- a/outputs/openfaas.go +++ b/outputs/openfaas.go @@ -48,7 +48,7 @@ func NewOpenfaasClient(config *types.Configuration, stats *types.Statistics, pro StatsdClient: statsdClient, } - return NewClient(Openfaas, endpointUrl, config.Openfaas.MutualTLS, config.Openfaas.CheckCert, *initClientArgs) + return NewClient(Openfaas, endpointUrl, config.Openfaas.CommonConfig, *initClientArgs) } // OpenfaasCall . diff --git a/outputs/openobserve.go b/outputs/openobserve.go index 041dd146d..c68839fff 100644 --- a/outputs/openobserve.go +++ b/outputs/openobserve.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -12,17 +13,16 @@ import ( func (c *Client) OpenObservePost(falcopayload types.FalcoPayload) { c.Stats.OpenObserve.Add(Total, 1) - if c.Config.OpenObserve.Username != "" && c.Config.OpenObserve.Password != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.BasicAuth(c.Config.OpenObserve.Username, c.Config.OpenObserve.Password) - } - - for i, j := range c.Config.OpenObserve.CustomHeaders { - c.AddHeader(i, j) - } + err := c.Post(falcopayload, func(req *http.Request) { + if c.Config.OpenObserve.Username != "" && c.Config.OpenObserve.Password != "" { + req.SetBasicAuth(c.Config.OpenObserve.Username, c.Config.OpenObserve.Password) + } - if err := c.Post(falcopayload); err != nil { + for i, j := range c.Config.OpenObserve.CustomHeaders { + req.Header.Set(i, j) + } + }) + if err != nil { c.setOpenObserveErrorMetrics() log.Printf("[ERROR] : OpenObserve - %v\n", err) return diff --git a/outputs/opsgenie.go b/outputs/opsgenie.go index 65c3f21bf..c963a863a 100644 --- a/outputs/opsgenie.go +++ b/outputs/opsgenie.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "strings" "github.com/falcosecurity/falcosidekick/types" @@ -64,11 +65,10 @@ func newOpsgeniePayload(falcopayload types.FalcoPayload) opsgeniePayload { // OpsgeniePost posts event to OpsGenie func (c *Client) OpsgeniePost(falcopayload types.FalcoPayload) { c.Stats.Opsgenie.Add(Total, 1) - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader(AuthorizationHeaderKey, "GenieKey "+c.Config.Opsgenie.APIKey) - err := c.Post(newOpsgeniePayload(falcopayload)) + err := c.Post(newOpsgeniePayload(falcopayload), func(req *http.Request) { + req.Header.Set(AuthorizationHeaderKey, "GenieKey "+c.Config.Opsgenie.APIKey) + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:opsgenie", "status:error"}) c.Stats.Opsgenie.Add(Error, 1) diff --git a/outputs/otlp.go b/outputs/otlp.go index a5c06578a..af093c2f2 100644 --- a/outputs/otlp.go +++ b/outputs/otlp.go @@ -29,7 +29,7 @@ func NewOtlpTracesClient(config *types.Configuration, stats *types.Statistics, p PromStats: promStats, StatsdClient: statsdClient, } - otlpClient, err := NewClient("OTLPTraces", config.OTLP.Traces.Endpoint, false, false, *initClientArgs) + otlpClient, err := NewClient("OTLPTraces", config.OTLP.Traces.Endpoint, types.CommonConfig{}, *initClientArgs) if err != nil { return nil, err } diff --git a/outputs/otlp_test.go b/outputs/otlp_test.go index 13ec52cde..308905ef7 100644 --- a/outputs/otlp_test.go +++ b/outputs/otlp_test.go @@ -91,7 +91,7 @@ func TestOtlpNewTrace(t *testing.T) { PromStats: promStats, } - client, _ := NewClient("OTLP", "http://localhost:4317", false, false, *initClientArgs) + client, _ := NewClient("OTLP", "http://localhost:4317", types.CommonConfig{}, *initClientArgs) // Test newTrace() span, err := client.newTrace(c.fp) require.Nil(t, err) diff --git a/outputs/quickwit.go b/outputs/quickwit.go index 4ff52f4de..c06713ff5 100644 --- a/outputs/quickwit.go +++ b/outputs/quickwit.go @@ -5,6 +5,7 @@ package outputs import ( "fmt" "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -48,7 +49,7 @@ func (c *Client) checkQuickwitIndexAlreadyExists(args types.InitClientArgs) bool config := args.Config.Quickwit endpointUrl := fmt.Sprintf("%s/%s/indexes/%s/describe", config.HostPort, config.ApiEndpoint, config.Index) - quickwitCheckClient, err := NewClient("QuickwitCheckAlreadyExists", endpointUrl, config.MutualTLS, config.CheckCert, args) + quickwitCheckClient, err := NewClient("QuickwitCheckAlreadyExists", endpointUrl, config.CommonConfig, args) if err != nil { return false } @@ -68,7 +69,7 @@ func (c *Client) AutoCreateQuickwitIndex(args types.InitClientArgs) error { } endpointUrl := fmt.Sprintf("%s/%s/indexes", config.HostPort, config.ApiEndpoint) - quickwitInitClient, err := NewClient("QuickwitInit", endpointUrl, config.MutualTLS, config.CheckCert, args) + quickwitInitClient, err := NewClient("QuickwitInit", endpointUrl, config.CommonConfig, args) if err != nil { return err } @@ -158,19 +159,15 @@ func (c *Client) AutoCreateQuickwitIndex(args types.InitClientArgs) error { func (c *Client) QuickwitPost(falcopayload types.FalcoPayload) { c.Stats.Quickwit.Add(Total, 1) - if len(c.Config.Quickwit.CustomHeaders) != 0 { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - for i, j := range c.Config.Quickwit.CustomHeaders { - c.AddHeader(i, j) - } - } - if c.Config.Debug { log.Printf("[DEBUG] : Quickwit - ingesting payload: %v\n", falcopayload) } - err := c.Post(falcopayload) + err := c.Post(falcopayload, func(req *http.Request) { + for i, j := range c.Config.Quickwit.CustomHeaders { + req.Header.Set(i, j) + } + }) if err != nil { go c.CountMetric(Outputs, 1, []string{"output:quickwit", "status:error"}) diff --git a/outputs/spyderbat.go b/outputs/spyderbat.go index 174d134ba..6b783f960 100644 --- a/outputs/spyderbat.go +++ b/outputs/spyderbat.go @@ -212,30 +212,27 @@ func NewSpyderbatClient(config *types.Configuration, stats *types.Statistics, pr return nil, ErrClientCreation } return &Client{ - OutputType: "Spyderbat", - EndpointURL: endpointURL, - MutualTLSEnabled: false, - CheckCert: true, - ContentType: "application/ndjson", - Config: config, - Stats: stats, - PromStats: promStats, - StatsdClient: statsdClient, - DogstatsdClient: dogstatsdClient, + OutputType: "Spyderbat", + EndpointURL: endpointURL, + cfg: types.CommonConfig{MutualTLS: false, CheckCert: true, MaxConcurrentRequests: 1}, + ContentType: "application/ndjson", + Config: config, + Stats: stats, + PromStats: promStats, + StatsdClient: statsdClient, + DogstatsdClient: dogstatsdClient, }, nil } func (c *Client) SpyderbatPost(falcopayload types.FalcoPayload) { c.Stats.Spyderbat.Add(Total, 1) - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.AddHeader("Authorization", "Bearer "+c.Config.Spyderbat.APIKey) - c.AddHeader("Content-Encoding", "gzip") - payload, err := newSpyderbatPayload(falcopayload) if err == nil { - err = c.Post(payload) + err = c.Post(payload, func(req *http.Request) { + req.Header.Set("Authorization", "Bearer "+c.Config.Spyderbat.APIKey) + req.Header.Set("Content-Encoding", "gzip") + }) } if err != nil { go c.CountMetric(Outputs, 1, []string{"output:spyderbat", "status:error"}) diff --git a/outputs/sumologic.go b/outputs/sumologic.go index c3bd96a5c..a262f5bdc 100644 --- a/outputs/sumologic.go +++ b/outputs/sumologic.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "net/url" "github.com/falcosecurity/falcosidekick/types" @@ -22,19 +23,20 @@ func (c *Client) SumoLogicPost(falcopayload types.FalcoPayload) { c.EndpointURL = endpointURL - if c.Config.SumoLogic.SourceCategory != "" { - c.AddHeader("X-Sumo-Category", c.Config.SumoLogic.SourceCategory) - } + err = c.Post(falcopayload, func(req *http.Request) { + if c.Config.SumoLogic.SourceCategory != "" { + req.Header.Set("X-Sumo-Category", c.Config.SumoLogic.SourceCategory) + } - if c.Config.SumoLogic.SourceHost != "" { - c.AddHeader("X-Sumo-Host", c.Config.SumoLogic.SourceHost) - } + if c.Config.SumoLogic.SourceHost != "" { + req.Header.Set("X-Sumo-Host", c.Config.SumoLogic.SourceHost) + } - if c.Config.SumoLogic.Name != "" { - c.AddHeader("X-Sumo-Name", c.Config.SumoLogic.Name) - } + if c.Config.SumoLogic.Name != "" { + req.Header.Set("X-Sumo-Name", c.Config.SumoLogic.Name) + } + }) - err = c.Post(falcopayload) if err != nil { c.setSumoLogicErrorMetrics() log.Printf("[ERROR] : %x - %v\n", c.OutputType, err) diff --git a/outputs/webhook.go b/outputs/webhook.go index 7ef96a1c1..d2fe23105 100644 --- a/outputs/webhook.go +++ b/outputs/webhook.go @@ -4,6 +4,7 @@ package outputs import ( "log" + "net/http" "strings" "github.com/falcosecurity/falcosidekick/types" @@ -13,18 +14,16 @@ import ( func (c *Client) WebhookPost(falcopayload types.FalcoPayload) { c.Stats.Webhook.Add(Total, 1) - if len(c.Config.Webhook.CustomHeaders) != 0 { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() + optfn := func(req *http.Request) { for i, j := range c.Config.Webhook.CustomHeaders { - c.AddHeader(i, j) + req.Header.Set(i, j) } } var err error if strings.ToUpper(c.Config.Webhook.Method) == HttpPut { - err = c.Put(falcopayload) + err = c.Put(falcopayload, optfn) } else { - err = c.Post(falcopayload) + err = c.Post(falcopayload, optfn) } if err != nil { diff --git a/outputs/zincsearch.go b/outputs/zincsearch.go index 04745e5d5..30a1911e4 100644 --- a/outputs/zincsearch.go +++ b/outputs/zincsearch.go @@ -3,8 +3,8 @@ package outputs import ( - "fmt" "log" + "net/http" "github.com/falcosecurity/falcosidekick/types" ) @@ -13,14 +13,11 @@ import ( func (c *Client) ZincsearchPost(falcopayload types.FalcoPayload) { c.Stats.Zincsearch.Add(Total, 1) - if c.Config.Zincsearch.Username != "" && c.Config.Zincsearch.Password != "" { - c.httpClientLock.Lock() - defer c.httpClientLock.Unlock() - c.BasicAuth(c.Config.Zincsearch.Username, c.Config.Zincsearch.Password) - } - - fmt.Println(c.EndpointURL) - err := c.Post(falcopayload) + err := c.Post(falcopayload, func(req *http.Request) { + if c.Config.Zincsearch.Username != "" && c.Config.Zincsearch.Password != "" { + req.SetBasicAuth(c.Config.Zincsearch.Username, c.Config.Zincsearch.Password) + } + }) if err != nil { c.setZincsearchErrorMetrics() log.Printf("[ERROR] : Zincsearch - %v\n", err) diff --git a/types/types.go b/types/types.go index 667d2476d..8dfc691e4 100644 --- a/types/types.go +++ b/types/types.go @@ -149,8 +149,15 @@ type TLSServer struct { NoTLSPaths []string } +type CommonConfig struct { + CheckCert bool + MutualTLS bool + MaxConcurrentRequests uint16 // Max concurrent requests at a time, unlimited if 0 +} + // SlackOutputConfig represents parameters for Slack type SlackOutputConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string Channel string Footer string @@ -160,12 +167,11 @@ type SlackOutputConfig struct { MinimumPriority string MessageFormat string MessageFormatTemplate *template.Template - CheckCert bool - MutualTLS bool } // CliqOutputConfig represents parameters for Zoho Cliq type CliqOutputConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string Icon string OutputFormat string @@ -173,12 +179,11 @@ type CliqOutputConfig struct { MessageFormat string MessageFormatTemplate *template.Template UseEmoji bool - CheckCert bool - MutualTLS bool } // RocketchatOutputConfig . type RocketchatOutputConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string Footer string Icon string @@ -187,12 +192,11 @@ type RocketchatOutputConfig struct { MinimumPriority string MessageFormat string MessageFormatTemplate *template.Template - CheckCert bool - MutualTLS bool } // MattermostOutputConfig represents parameters for Mattermost type MattermostOutputConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string Footer string Icon string @@ -201,8 +205,6 @@ type MattermostOutputConfig struct { MinimumPriority string MessageFormat string MessageFormatTemplate *template.Template - CheckCert bool - MutualTLS bool } type WavefrontOutputConfig struct { @@ -217,29 +219,26 @@ type WavefrontOutputConfig struct { } type teamsOutputConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string ActivityImage string OutputFormat string MinimumPriority string - CheckCert bool - MutualTLS bool } type datadogOutputConfig struct { + CommonConfig `mapstructure:",squash"` APIKey string Host string MinimumPriority string - CheckCert bool - MutualTLS bool } // DiscordOutputConfig . type DiscordOutputConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string MinimumPriority string Icon string - CheckCert bool - MutualTLS bool } type ThresholdConfig struct { @@ -248,10 +247,9 @@ type ThresholdConfig struct { } type AlertmanagerOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string MinimumPriority string - CheckCert bool - MutualTLS bool Endpoint string ExpiresAfter int ExtraLabels map[string]string @@ -264,6 +262,7 @@ type AlertmanagerOutputConfig struct { } type ElasticsearchOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string Index string Type string @@ -275,24 +274,22 @@ type ElasticsearchOutputConfig struct { CreateIndexTemplate bool NumberOfShards int NumberOfReplicas int - CheckCert bool - MutualTLS bool CustomHeaders map[string]string } type QuickwitOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string ApiEndpoint string Index string Version string CustomHeaders map[string]string MinimumPriority string - CheckCert bool - MutualTLS bool AutoCreateIndex bool } type influxdbOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string Database string Organization string @@ -302,17 +299,14 @@ type influxdbOutputConfig struct { Password string Token string MinimumPriority string - CheckCert bool - MutualTLS bool } type LokiOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string User string APIKey string MinimumPriority string - CheckCert bool - MutualTLS bool Tenant string Endpoint string ExtraLabels string @@ -321,13 +315,12 @@ type LokiOutputConfig struct { } type SumoLogicOutputConfig struct { + CommonConfig `mapstructure:",squash"` MinimumPriority string ReceiverURL string SourceCategory string SourceHost string Name string - CheckCert bool - MutualTLS bool } type prometheusOutputConfig struct { @@ -336,19 +329,17 @@ type prometheusOutputConfig struct { } type natsOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string MinimumPriority string - CheckCert bool - MutualTLS bool } type stanOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string ClusterID string ClientID string MinimumPriority string - CheckCert bool - MutualTLS bool } type awsOutputConfig struct { @@ -434,40 +425,37 @@ type smtpOutputConfig struct { } type opsgenieOutputConfig struct { + CommonConfig `mapstructure:",squash"` Region string APIKey string MinimumPriority string - CheckCert bool - MutualTLS bool } // WebhookOutputConfig represents parameters for Webhook type WebhookOutputConfig struct { + CommonConfig `mapstructure:",squash"` Address string Method string CustomHeaders map[string]string MinimumPriority string - CheckCert bool - MutualTLS bool } // NodeRedOutputConfig represents parameters for Node-RED type NodeRedOutputConfig struct { + CommonConfig `mapstructure:",squash"` Address string User string Password string CustomHeaders map[string]string MinimumPriority string - CheckCert bool } // CloudEventsOutputConfig represents parameters for CloudEvents type CloudEventsOutputConfig struct { + CommonConfig `mapstructure:",squash"` Address string Extensions map[string]string MinimumPriority string - CheckCert bool - MutualTLS bool } type statsdOutputConfig struct { @@ -521,13 +509,12 @@ type gcpStorage struct { // GooglechatConfig represents parameters for Google chat type GooglechatConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string OutputFormat string MinimumPriority string MessageFormat string MessageFormatTemplate *template.Template - CheckCert bool - MutualTLS bool } type kafkaConfig struct { @@ -547,32 +534,30 @@ type kafkaConfig struct { } type KafkaRestConfig struct { + CommonConfig `mapstructure:",squash"` Address string Version int MinimumPriority string - CheckCert bool - MutualTLS bool } type PagerdutyConfig struct { + CommonConfig `mapstructure:",squash"` RoutingKey string Region string MinimumPriority string - CheckCert bool - MutualTLS bool } type kubelessConfig struct { + CommonConfig `mapstructure:",squash"` Namespace string Function string Port int Kubeconfig string MinimumPriority string - CheckCert bool - MutualTLS bool } type openfaasConfig struct { + CommonConfig `mapstructure:",squash"` GatewayNamespace string GatewayService string FunctionName string @@ -580,22 +565,18 @@ type openfaasConfig struct { GatewayPort int Kubeconfig string MinimumPriority string - CheckCert bool - MutualTLS bool } type tektonConfig struct { + CommonConfig `mapstructure:",squash"` EventListener string MinimumPriority string - CheckCert bool - MutualTLS bool } // WebUIOutputConfig represents parameters for WebUI type WebUIOutputConfig struct { - URL string - CheckCert bool - MutualTLS bool + CommonConfig `mapstructure:",squash"` + URL string } // PolicyReportConfig represents parameters for policyreport @@ -617,22 +598,20 @@ type RabbitmqConfig struct { // GrafanaOutputConfig represents parameters for Grafana type GrafanaOutputConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string APIKey string DashboardID int PanelID int AllFieldsAsTags bool - CheckCert bool - MutualTLS bool MinimumPriority string CustomHeaders map[string]string } // GrafanaOnCallOutputConfig represents parameters for Grafana OnCall type GrafanaOnCallOutputConfig struct { + CommonConfig `mapstructure:",squash"` WebhookURL string - CheckCert bool - MutualTLS bool MinimumPriority string CustomHeaders map[string]string } @@ -682,14 +661,13 @@ type MQTTConfig struct { // fissionConfig represents config parameters for Fission type fissionConfig struct { + CommonConfig `mapstructure:",squash"` RouterNamespace string RouterService string RouterPort int Function string KubeConfig string MinimumPriority string - CheckCert bool - MutualTLS bool } // zincsearchOutputConfig represents config parameters for Zincsearch @@ -771,14 +749,13 @@ type DynatraceOutputConfig struct { // OpenObserveConfig represents config parameters for OpenObserve type OpenObserveConfig struct { + CommonConfig `mapstructure:",squash"` HostPort string OrganizationName string StreamName string MinimumPriority string Username string Password string - CheckCert bool - MutualTLS bool CustomHeaders map[string]string }