From 6b2b70456b067b645f025d0bd4c9616f96ec7a94 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Thu, 14 Jan 2021 16:24:42 -0800 Subject: [PATCH] adding cloudevents Signed-off-by: Scott Nichols --- .gitignore | 2 + README.md | 556 ++++++++++++++++++++++++++--------------- config.go | 15 ++ config_example.yaml | 6 + go.mod | 1 + handlers.go | 4 + main.go | 11 + outputs/client.go | 26 +- outputs/cloudevents.go | 56 +++++ stats.go | 1 + types/types.go | 9 + 11 files changed, 474 insertions(+), 213 deletions(-) create mode 100644 outputs/cloudevents.go diff --git a/.gitignore b/.gitignore index f6af33033..43236e667 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ falcosidekick .idea *.swp /hack/tools/bin/* + +tmp/ diff --git a/README.md b/README.md index 53d4e5319..ceddf8251 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,18 @@ # Falcosidekick - - ![falcosidekick](https://github.com/falcosecurity/falcosidekick/raw/master/imgs/falcosidekick_color.png) -![release](https://flat.badgen.net/github/release/falcosecurity/falcosidekick/latest?color=green) ![last commit](https://flat.badgen.net/github/last-commit/falcosecurity/falcosidekick) ![licence](https://flat.badgen.net/badge/license/MIT/blue) ![docker pulls](https://flat.badgen.net/docker/pulls/falcosecurity/falcosidekick?icon=docker) [![falcosidekick](https://circleci.com/gh/falcosecurity/falcosidekick.svg?style=shield)](https://circleci.com/gh/falcosecurity/falcosidekick) +![release](https://flat.badgen.net/github/release/falcosecurity/falcosidekick/latest?color=green) +![last commit](https://flat.badgen.net/github/last-commit/falcosecurity/falcosidekick) +![licence](https://flat.badgen.net/badge/license/MIT/blue) +![docker pulls](https://flat.badgen.net/docker/pulls/falcosecurity/falcosidekick?icon=docker) +[![falcosidekick](https://circleci.com/gh/falcosecurity/falcosidekick.svg?style=shield)](https://circleci.com/gh/falcosecurity/falcosidekick) ## Description -A simple daemon for enhancing available outputs for [Falco](https://sysdig.com/opensource/falco/). It takes a falco's event and forwards it to different outputs. +A simple daemon for enhancing available outputs for +[Falco](https://sysdig.com/opensource/falco/). It takes a falco's event and +forwards it to different outputs. It works as a single endpoint for as many as you want `falco` instances : @@ -18,38 +22,42 @@ It works as a single endpoint for as many as you want `falco` instances : Currently available outputs are : -* [**Slack**](https://slack.com) -* [**Rocketchat**](https://rocket.chat/) -* [**Mattermost**](https://mattermost.com/) -* [**Teams**](https://products.office.com/en-us/microsoft-teams/group-chat-software) -* [**Datadog**](https://www.datadoghq.com/) -* [**Discord**](https://www.discord.com/) -* [**AlertManager**](https://prometheus.io/docs/alerting/alertmanager/) -* [**Elasticsearch**](https://www.elastic.co/) -* [**Loki**](https://grafana.com/oss/loki) -* [**NATS**](https://nats.io/) -* [**STAN (NATS Streaming)**](https://docs.nats.io/nats-streaming-concepts/intro) -* [**Influxdb**](https://www.influxdata.com/products/influxdb-overview/) -* [**AWS Lambda**](https://aws.amazon.com/lambda/features/) -* [**AWS SQS**](https://aws.amazon.com/sqs/features/) -* [**AWS SNS**](https://aws.amazon.com/sns/features/) -* [**AWS CloudWatchLogs**](https://aws.amazon.com/cloudwatch/features/) -* **SMTP** (email) -* [**Opsgenie**](https://www.opsgenie.com/) -* [**StatsD**](https://github.com/statsd/statsd) (for monitoring of `falcosidekick`) -* [**DogStatsD**](https://docs.datadoghq.com/developers/dogstatsd/?tab=go) (for monitoring of `falcosidekick`) -* **Webhook** -* [**Azure Event Hubs**](https://azure.microsoft.com/en-in/services/event-hubs/) -* [**Prometheus**](https://prometheus.io/) (for both events and monitoring of `falcosidekick`) -* [**GCP PubSub**](https://cloud.google.com/pubsub) -* [**Google Chat**](https://workspace.google.com/products/chat/) -* [**Apache Kafka**](https://kafka.apache.org/) -* [**PagerDuty**](https://pagerduty.com/) -* [**Kubeless**](https://kubeless.io/) +- [**Slack**](https://slack.com) +- [**Rocketchat**](https://rocket.chat/) +- [**Mattermost**](https://mattermost.com/) +- [**Teams**](https://products.office.com/en-us/microsoft-teams/group-chat-software) +- [**Datadog**](https://www.datadoghq.com/) +- [**Discord**](https://www.discord.com/) +- [**AlertManager**](https://prometheus.io/docs/alerting/alertmanager/) +- [**Elasticsearch**](https://www.elastic.co/) +- [**Loki**](https://grafana.com/oss/loki) +- [**NATS**](https://nats.io/) +- [**STAN (NATS Streaming)**](https://docs.nats.io/nats-streaming-concepts/intro) +- [**Influxdb**](https://www.influxdata.com/products/influxdb-overview/) +- [**AWS Lambda**](https://aws.amazon.com/lambda/features/) +- [**AWS SQS**](https://aws.amazon.com/sqs/features/) +- [**AWS SNS**](https://aws.amazon.com/sns/features/) +- [**AWS CloudWatchLogs**](https://aws.amazon.com/cloudwatch/features/) +- **SMTP** (email) +- [**Opsgenie**](https://www.opsgenie.com/) +- [**StatsD**](https://github.com/statsd/statsd) (for monitoring of + `falcosidekick`) +- [**DogStatsD**](https://docs.datadoghq.com/developers/dogstatsd/?tab=go) (for + monitoring of `falcosidekick`) +- **Webhook** +- [**Azure Event Hubs**](https://azure.microsoft.com/en-in/services/event-hubs/) +- [**Prometheus**](https://prometheus.io/) (for both events and monitoring of + `falcosidekick`) +- [**GCP PubSub**](https://cloud.google.com/pubsub) +- [**Google Chat**](https://workspace.google.com/products/chat/) +- [**Apache Kafka**](https://kafka.apache.org/) +- [**PagerDuty**](https://pagerduty.com/) +- [**Kubeless**](https://kubeless.io/) ## Usage -Run the daemon as any other daemon in your architecture (systemd, k8s daemonset, swarm service, ...) +Run the daemon as any other daemon in your architecture (systemd, k8s daemonset, +swarm service, ...) ### With docker @@ -59,7 +67,9 @@ docker run -d -p 2801:2801 -e SLACK_WEBHOOKURL=XXXX -e DATADOG_APIKEY=XXXX falco ### With Helm -See [https://github.com/falcosecurity/charts/blob/master/falcosidekick/README.md](https://github.com/falcosecurity/charts/blob/master/falcosidekick/README.md) +See +[https://github.com/falcosecurity/charts/blob/master/falcosidekick/README.md](https://github.com/falcosecurity/charts/blob/master/falcosidekick/README.md) + ```bash helm repo add falcosecurity https://falcosecurity.github.io/charts helm repo update @@ -71,7 +81,7 @@ helm install falcosidekick --set config.debug=true falcosecurity/falcosidekick #### with falco.yaml -If managing *falco.yaml* manually, set this: +If managing _falco.yaml_ manually, set this: ```yaml json_output: true @@ -83,7 +93,8 @@ http_output: #### with Helm -If installing `falco` with `Helm`, set this (adapted to your environment) in your *values.yaml* : +If installing `falco` with `Helm`, set this (adapted to your environment) in +your _values.yaml_ : ```yaml jsonOutput: true @@ -106,7 +117,8 @@ programOutput: ### Configuration -Configuration is made by *file (yaml)* and *env vars*, both can be used but *env vars* override values from *file*. +Configuration is made by _file (yaml)_ and _env vars_, both can be used but _env +vars_ override values from _file_. #### YAML File @@ -128,7 +140,8 @@ slack: #username: "" # Slack username (default: Falcosidekick) outputformat: "all" # all (default), text, fields minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) - messageformat: "Alert : rule *{{ .Rule }}* triggered by user *{{ index .OutputFields \"user.name\" }}*" # a Go template to format Slack Text above Attachment, displayed in addition to the output from `SLACK_OUTPUTFORMAT`, see [Slack Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before Attachment. + messageformat: 'Alert : rule *{{ .Rule }}* triggered by user *{{ index + .OutputFields "user.name" }}*' # a Go template to format Slack Text above Attachment, displayed in addition to the output from `SLACK_OUTPUTFORMAT`, see [Slack Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before Attachment. rocketchat: webhookurl: "" # Rocketchat WebhookURL (ex: http://XXXX/hooks/YYYY), if not empty, Rocketchat output is enabled @@ -241,9 +254,9 @@ webhook: azure: # eventHub: - # name: "" # The name of the Hub, if not empty, EventHub output is enabled - # namespace: "" # The name of the space the Hub is part of - # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + # name: "" # The name of the Hub, if not empty, EventHub output is enabled + # namespace: "" # The name of the space the Hub is part of + # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) discord: webhookurl: "" # discord WebhookURL (ex: https://discord.com/api/webhooks/xxxxxxxxxx...), if not empty, Discord output is enabled @@ -261,7 +274,8 @@ googlechat: webhookurl: "" # Google Chat WebhookURL (ex: https://chat.googleapis.com/v1/spaces/XXXXXX/YYYYYY), if not empty, Google Chat output is enabled # outputformat: "" # all (default), text # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) - messageformat: "Alert : rule *{{ .Rule }}* triggered by user *{{ index .OutputFields \"user.name\" }}*" # a Go template to format Google Chat Text above Attachment, displayed in addition to the output from `GOOGLECHAT_OUTPUTFORMAT`, see [Slack Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before Attachment. + messageformat: 'Alert : rule *{{ .Rule }}* triggered by user *{{ index + .OutputFields "user.name" }}*' # a Go template to format Google Chat Text above Attachment, displayed in addition to the output from `GOOGLECHAT_OUTPUTFORMAT`, see [Slack Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before Attachment. kafka: hostport: "" # Apache Kafka Host:Port (ex: localhost:9092). Defaults to port 9092 if no port is specified after the domain, if not empty, Kafka output is enabled @@ -296,124 +310,247 @@ Flags: #### Env vars -Configuration of the daemon can be made also by *env vars*, these values override these from *yaml file*. - -The *env vars* "match" field names in *yaml file with this structure (**take care of lower/uppercases**) : `yaml: a.b --> envvar: A_B` : - -* **LISTENPORT** : port to listen for daemon (default: `2801`) -* **DEBUG** : if *true* all outputs will print in stdout the payload they send (default: false) -* **CUSTOMFIELDS** : a list of comma separated custom fields to add to falco events, syntax is "key:value,key:value" -* **CHECKCERT**: check if ssl certificate of the output is valid (default: `true`) -* **SLACK_WEBHOOKURL** : Slack Webhook URL (ex: https://hooks.slack.com/services/XXXX/YYYY/ZZZZ), if not `empty`, Slack output is *enabled* -* **SLACK_FOOTER** : Slack footer -* **SLACK_ICON** : Slack icon (avatar) -* **SLACK_USERNAME** : Slack username (default: `Falcosidekick`) -* **SLACK_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed in Slack), `fields` (only fields are displayed in Slack) -* **SLACK_MINIMUMPRIORITY** : minimum priority of event for using use this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **SLACK_MESSAGEFORMAT** : a Go template to format Slack Text above Attachment, displayed in addition to the output from `SLACK_OUTPUTFORMAT`, see [Slack Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before Attachment. -* **ROCKETCHAT_WEBHOOKURL** : Rocketchat Webhook URL (ex: https://XXXX/hooks/YYYY), if not `empty`, Rocketchat output is *enabled* -* **ROCKETCHAT_ICON** : Rocketchat icon (avatar) -* **ROCKETCHAT_USERNAME** : Rocketchat username (default: `Falcosidekick`) -* **ROCKETCHAT_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed in Rocketchat), `fields` (only fields are displayed in Rocketchat) -* **ROCKETCHAT_MINIMUMPRIORITY** : minimum priority of event for using use this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **ROCKETCHAT_MESSAGEFORMAT** : a Go template to format Rocketchat Text above Attachment, displayed in addition to the output from `ROCKETCHAT_OUTPUTFORMAT`, see [Slack Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before Attachment. -* **MATTERMOST_WEBHOOKURL** : Mattermost Webhook URL (ex: https://XXXX/hooks/YYYY), if not `empty`, Mattermost output is *enabled* -* **MATTERMOST_FOOTER** : Mattermost footer -* **MATTERMOST_ICON** : Mattermost icon (avatar) -* **MATTERMOST_USERNAME** : Mattermost username (default: `Falcosidekick`) -* **MATTERMOST_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed in Mattermost), `fields` (only fields are displayed in Mattermost) -* **MATTERMOST_MINIMUMPRIORITY** : minimum priority of event for using use this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **MATTERMOST_MESSAGEFORMAT** : a Go template to format Mattermost Text above Attachment, displayed in addition to the output from `MATTERMOST_OUTPUTFORMAT`, see [Mattermost Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before Attachment. -* **TEAMS_WEBHOOKURL** : Teams Webhook URL (ex: https://outlook.office.com/webhook/XXXXXX/IncomingWebhook/YYYYYY"), if not `empty`, Teams output is *enabled* -* **TEAMS_ACTIVITYIMAGE** : Teams section image -* **TEAMS_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed in Teams), `facts` (only facts are displayed in Teams) -* **TEAMS_MINIMUMPRIORITY** : minimum priority of event for using use this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **DATADOG_APIKEY** : Datadog API Key, if not `empty`, Datadog output is *enabled* -* **DATADOG_HOST** : Datadog host. Override if you are on the Datadog EU site. Defaults to american site with "https://api.datadoghq.com" -* **DATADOG_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **DISCORD_WEBHOOKURL** : Discord WebhookURL (ex: https://discord.com/api/webhooks/xxxxxxxxxx...), if not empty, Discord output is *enabled* -* **DISCORD_ICON** : Discord icon (avatar) -* **DISCORD_MINIMUMPRIORITY** : minimum priority of event for using use this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **ALERTMANAGER_HOSTPORT** : AlertManager http://host:port, if not `empty`, AlertManager is *enabled* -* **ALERTMANAGER_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **ELASTICSEARCH_HOSTPORT** : Elasticsearch http://host:port, if not `empty`, Elasticsearch is *enabled* -* **ELASTICSEARCH_INDEX** : Elasticsearch index (default: falco) -* **ELASTICSEARCH_TYPE** : Elasticsearch document type (default: event) -* **ELASTICSEARCH_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **ELASTICSEARCH_SUFFIX** : date suffix for index rotation : `daily` (default), `monthly`, `annually`, `none` -* **INFLUXDB_HOSTPORT** : Influxdb http://host:port, if not `empty`, Influxdb is *enabled* -* **INFLUXDB_DATABASE** : Influxdb database (default: falco) -* **INFLUXDB_USER** : user to use if auth is enabled in Influxdb -* **INFLUXDB_PASSWORD** : user to use if auth is enabled in Influxdb -* **INFLUXDB_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **LOKI_HOSTPORT** : Loki http://host:port, if not `empty`, Loki is *enabled* -* **LOKI_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **NATS_HOSTPORT** : NATS "nats://host:port", if not `empty`, NATS is *enabled* -* **NATS_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **STAN_HOSTPORT** : NATS "nats://host:port", if not `empty`, STAN is *enabled* -* **STAN_CLUSTERID** : Cluster name, if not `empty`, STAN is *enabled* -* **STAN_CLIENTID** : Client ID to use, if not `empty`, STAN is *enabled* -* **STAN_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **AWS_ACCESSKEYID** : AWS Access Key Id (optional if you use EC2 Instance Profile) -* **AWS_SECRETACCESSKEY** : AWS Secret Access Key (optional if you use EC2 Instance Profile) -* **AWS_REGION** : AWS Region (optional if you use EC2 Instance Profile) -* **AWS_LAMBDA_FUNCTIONNAME** : AWS Lambda Function Name, if not empty, AWS Lambda output is *enabled* -* **AWS_LAMBDA_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **AWS_SQS_URL** : AWS SQS Queue URL, if not empty, AWS SQS output is *enabled* -* **AWS_SQS_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **AWS_SNS_TOPICARN** : AWS SNS TopicARN, if not empty, AWS SNS output is *enabled* -* **AWS_SNS_RAWJSON** : Send Raw JSON or parse it (default: false) -* **AWS_SNS_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **AWS_CLOUDWATCHLOGS_LOGGROUP** : AWS CloudWatch Logs Group name, if not empty, CloudWatch Logs output is enabled -* **AWS_CLOUDWATCHLOGS_LOGSTREAM** : AWS CloudWatch Logs Stream name, if empty, FalcoSideKick will try to create a log stream -* **AWS_CLOUDWATCHLOGS_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **SMTP_HOSTPORT** : "host:port" address of SMTP server, if not empty, SMTP output is *enabled* -* **SMTP_USER** : user to access SMTP server -* **SMTP_PASSWORD** : password to access SMTP server -* **SMTP_FROM** : Sender address (mandatory if SMTP output is enabled) -* **SMTP_TO** : comma-separated list of Recipident addresses, can't be empty (mandatory if SMTP output is enabled) -* **SMTP_OUTPUTFORMAT** : "" # html (default), text -* **SMTP_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **OPSGENIE_APIKEY** : Opsgenie API Key, if not empty, Opsgenie output is *enabled* -* **OPSGENIE_REGION** : "" # (us|eu) region of your domain (default is 'us') -* **OPSGENIE_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **STATSD_FORWARDER**: The address for the StatsD forwarder, in the form http://host:port, if not empty StatsD is *enabled* -* **STATSD_NAMESPACE**: A prefix for all metrics (default: "falcosidekick.") -* **DOGSTATSD_FORWARDER**: The address for the DogStatsD forwarder, in the form http://host:port, if not empty DogStatsD is *enabled* -* **DOGSTATSD_NAMESPACE**: A prefix for all metrics (default: falcosidekick."") -* **DOGSTATSD_TAGS**: A comma-separated list of tags to add to all metrics -* **WEBHOOK_ADDRESS** : "" # Webhook address, if not empty, Webhook output is *enabled* -* **WEBHOOK_CUSTOMHEADERS** : a list of comma separated custom headers to add, syntax is "key:value,key:value" -* **WEBHOOK_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **AZURE_EVENTHUB_NAME**: Name of the Hub, if not empty, EventHub is *enabled* -* **AZURE_EVENTHUB_NAMESPACE**: Name of the space the Hub is in -* **AZURE_EVENTHUB_MINIMUMPRIORITY**: minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **GCP_CREDENTIALS**: The base64-encoded JSON key file for the GCP service account -* **GCP_PUBSUB_PROJECTID**: The GCP Project ID containing the Pub/Sub Topic -* **GCP_PUBSUB_TOPIC**: The name of the Pub/Sub topic -* **GCP_PUBSUB_MINIMUMPRIORITY**: minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **GOOGLECHAT_WEBHOOKURL** : Google Chat URL (ex: https://chat.googleapis.com/v1/spaces/XXXXXX/YYYYYY), if not `empty`, Google Chat output is *enabled* -* **GOOGLECHAT_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed in Google Chat) -* **GOOGLECHAT_MINIMUMPRIORITY** : minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **GOOGLECHAT_MESSAGEFORMAT** : a Go template to format Google Chat Text above Attachment, displayed in addition to the output from `GOOGLECHAT_OUTPUTFORMAT`, see [Slack Message Formatting](#slack-message-formatting) in the README for details. If empty, no Text is displayed before sections. -* **KAFKA_HOSTPORT**: The Host:Port of the Kafka (ex: localhost:9092), if not empty, Kafka is *enabled* -* **KAFKA_TOPIC**: The name of the Kafka topic -* **KAFKA_PARTITION**: The number of the Kafka partition -* **KAFKA_MINIMUMPRIORITY**: minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **PAGERDUTY_APIKEY**: Pagerduty API Key, if not empty, Pagerduty output is *enabled* -* **PAGERDUTY_SERVICE**: Service to create an incident (mandatory) -* **PAGERDUTY_ASSIGNEE**: A list of comma separated users to assign. Cannot be provided if `PAGERDUTY_ESCALATION_POLICY` is already specified. If not empty, Pagerduty is *enabled* -* **PAGERDUTY_ESCALATION_POLICY**: Escalation policy to assign. Cannot be provided if `PAGERDUTY_ASSIGNEE` is already specified.If not empty, Pagerduty is *enabled* -* **PAGERDUTY_MINIMUMPRIORITY**: minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` -* **KUBELESS_FUNCTION**: Name of Kubeless function, if not empty, Kubeless is *enabled* -* **KUBELESS_NAMESPACE**: Namespace of Kubeless function (mandatory) -* **KUBELESS_PORT**: Port of service of Kubeless function (default is `8080`) -* **KUBELESS_KUBECONFIG**: Kubeconfig file to use (only if falcoside is running outside the cluster) -* **KUBELESS_MINIMUMPRIORITY**: "debug" # minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +Configuration of the daemon can be made also by _env vars_, these values +override these from _yaml file_. + +The _env vars_ "match" field names in \*yaml file with this structure (**take +care of lower/uppercases**) : `yaml: a.b --> envvar: A_B` : + +- **LISTENPORT** : port to listen for daemon (default: `2801`) +- **DEBUG** : if _true_ all outputs will print in stdout the payload they send + (default: false) +- **CUSTOMFIELDS** : a list of comma separated custom fields to add to falco + events, syntax is "key:value,key:value" +- **CHECKCERT**: check if ssl certificate of the output is valid (default: + `true`) +- **SLACK_WEBHOOKURL** : Slack Webhook URL (ex: + https://hooks.slack.com/services/XXXX/YYYY/ZZZZ), if not `empty`, Slack output + is _enabled_ +- **SLACK_FOOTER** : Slack footer +- **SLACK_ICON** : Slack icon (avatar) +- **SLACK_USERNAME** : Slack username (default: `Falcosidekick`) +- **SLACK_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed in + Slack), `fields` (only fields are displayed in Slack) +- **SLACK_MINIMUMPRIORITY** : minimum priority of event for using use this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **SLACK_MESSAGEFORMAT** : a Go template to format Slack Text above Attachment, + displayed in addition to the output from `SLACK_OUTPUTFORMAT`, see + [Slack Message Formatting](#slack-message-formatting) in the README for + details. If empty, no Text is displayed before Attachment. +- **ROCKETCHAT_WEBHOOKURL** : Rocketchat Webhook URL (ex: + https://XXXX/hooks/YYYY), if not `empty`, Rocketchat output is _enabled_ +- **ROCKETCHAT_ICON** : Rocketchat icon (avatar) +- **ROCKETCHAT_USERNAME** : Rocketchat username (default: `Falcosidekick`) +- **ROCKETCHAT_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed + in Rocketchat), `fields` (only fields are displayed in Rocketchat) +- **ROCKETCHAT_MINIMUMPRIORITY** : minimum priority of event for using use this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **ROCKETCHAT_MESSAGEFORMAT** : a Go template to format Rocketchat Text above + Attachment, displayed in addition to the output from + `ROCKETCHAT_OUTPUTFORMAT`, see + [Slack Message Formatting](#slack-message-formatting) in the README for + details. If empty, no Text is displayed before Attachment. +- **MATTERMOST_WEBHOOKURL** : Mattermost Webhook URL (ex: + https://XXXX/hooks/YYYY), if not `empty`, Mattermost output is _enabled_ +- **MATTERMOST_FOOTER** : Mattermost footer +- **MATTERMOST_ICON** : Mattermost icon (avatar) +- **MATTERMOST_USERNAME** : Mattermost username (default: `Falcosidekick`) +- **MATTERMOST_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed + in Mattermost), `fields` (only fields are displayed in Mattermost) +- **MATTERMOST_MINIMUMPRIORITY** : minimum priority of event for using use this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **MATTERMOST_MESSAGEFORMAT** : a Go template to format Mattermost Text above + Attachment, displayed in addition to the output from + `MATTERMOST_OUTPUTFORMAT`, see + [Mattermost Message Formatting](#slack-message-formatting) in the README for + details. If empty, no Text is displayed before Attachment. +- **TEAMS_WEBHOOKURL** : Teams Webhook URL (ex: + https://outlook.office.com/webhook/XXXXXX/IncomingWebhook/YYYYYY"), if not + `empty`, Teams output is _enabled_ +- **TEAMS_ACTIVITYIMAGE** : Teams section image +- **TEAMS_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed in + Teams), `facts` (only facts are displayed in Teams) +- **TEAMS_MINIMUMPRIORITY** : minimum priority of event for using use this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **DATADOG_APIKEY** : Datadog API Key, if not `empty`, Datadog output is + _enabled_ +- **DATADOG_HOST** : Datadog host. Override if you are on the Datadog EU site. + Defaults to american site with "https://api.datadoghq.com" +- **DATADOG_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **DISCORD_WEBHOOKURL** : Discord WebhookURL (ex: + https://discord.com/api/webhooks/xxxxxxxxxx...), if not empty, Discord output + is _enabled_ +- **DISCORD_ICON** : Discord icon (avatar) +- **DISCORD_MINIMUMPRIORITY** : minimum priority of event for using use this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **ALERTMANAGER_HOSTPORT** : AlertManager http://host:port, if not `empty`, + AlertManager is _enabled_ +- **ALERTMANAGER_MINIMUMPRIORITY** : minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **ELASTICSEARCH_HOSTPORT** : Elasticsearch http://host:port, if not `empty`, + Elasticsearch is _enabled_ +- **ELASTICSEARCH_INDEX** : Elasticsearch index (default: falco) +- **ELASTICSEARCH_TYPE** : Elasticsearch document type (default: event) +- **ELASTICSEARCH_MINIMUMPRIORITY** : minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **ELASTICSEARCH_SUFFIX** : date suffix for index rotation : `daily` (default), + `monthly`, `annually`, `none` +- **INFLUXDB_HOSTPORT** : Influxdb http://host:port, if not `empty`, Influxdb is + _enabled_ +- **INFLUXDB_DATABASE** : Influxdb database (default: falco) +- **INFLUXDB_USER** : user to use if auth is enabled in Influxdb +- **INFLUXDB_PASSWORD** : user to use if auth is enabled in Influxdb +- **INFLUXDB_MINIMUMPRIORITY** : minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **LOKI_HOSTPORT** : Loki http://host:port, if not `empty`, Loki is _enabled_ +- **LOKI_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **NATS_HOSTPORT** : NATS "nats://host:port", if not `empty`, NATS is _enabled_ +- **NATS_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **STAN_HOSTPORT** : NATS "nats://host:port", if not `empty`, STAN is _enabled_ +- **STAN_CLUSTERID** : Cluster name, if not `empty`, STAN is _enabled_ +- **STAN_CLIENTID** : Client ID to use, if not `empty`, STAN is _enabled_ +- **STAN_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **AWS_ACCESSKEYID** : AWS Access Key Id (optional if you use EC2 Instance + Profile) +- **AWS_SECRETACCESSKEY** : AWS Secret Access Key (optional if you use EC2 + Instance Profile) +- **AWS_REGION** : AWS Region (optional if you use EC2 Instance Profile) +- **AWS_LAMBDA_FUNCTIONNAME** : AWS Lambda Function Name, if not empty, AWS + Lambda output is _enabled_ +- **AWS_LAMBDA_MINIMUMPRIORITY** : minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **AWS_SQS_URL** : AWS SQS Queue URL, if not empty, AWS SQS output is _enabled_ +- **AWS_SQS_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **AWS_SNS_TOPICARN** : AWS SNS TopicARN, if not empty, AWS SNS output is + _enabled_ +- **AWS_SNS_RAWJSON** : Send Raw JSON or parse it (default: false) +- **AWS_SNS_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **AWS_CLOUDWATCHLOGS_LOGGROUP** : AWS CloudWatch Logs Group name, if not + empty, CloudWatch Logs output is enabled +- **AWS_CLOUDWATCHLOGS_LOGSTREAM** : AWS CloudWatch Logs Stream name, if empty, + FalcoSideKick will try to create a log stream +- **AWS_CLOUDWATCHLOGS_MINIMUMPRIORITY** : minimum priority of event for using + this output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **SMTP_HOSTPORT** : "host:port" address of SMTP server, if not empty, SMTP + output is _enabled_ +- **SMTP_USER** : user to access SMTP server +- **SMTP_PASSWORD** : password to access SMTP server +- **SMTP_FROM** : Sender address (mandatory if SMTP output is enabled) +- **SMTP_TO** : comma-separated list of Recipident addresses, can't be empty + (mandatory if SMTP output is enabled) +- **SMTP_OUTPUTFORMAT** : "" # html (default), text +- **SMTP_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **OPSGENIE_APIKEY** : Opsgenie API Key, if not empty, Opsgenie output is + _enabled_ +- **OPSGENIE_REGION** : (us|eu) region of your domain (default is 'us') +- **OPSGENIE_MINIMUMPRIORITY** : minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **STATSD_FORWARDER**: The address for the StatsD forwarder, in the form + http://host:port, if not empty StatsD is _enabled_ +- **STATSD_NAMESPACE**: A prefix for all metrics (default: "falcosidekick.") +- **DOGSTATSD_FORWARDER**: The address for the DogStatsD forwarder, in the form + http://host:port, if not empty DogStatsD is _enabled_ +- **DOGSTATSD_NAMESPACE**: A prefix for all metrics (default: falcosidekick."") +- **DOGSTATSD_TAGS**: A comma-separated list of tags to add to all metrics +- **WEBHOOK_ADDRESS** : Webhook address, if not empty, Webhook output is + _enabled_ +- **WEBHOOK_CUSTOMHEADERS** : a list of comma separated custom headers to add, + syntax is "key:value,key:value" +- **WEBHOOK_MINIMUMPRIORITY** : minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **CLOUDEVENTS_ADDRESS** : CloudEvents consumer address, if not empty, + CloudEvents output is _enabled_ +- **CLOUDEVENTS_EXTENSIONS** : a list of comma separated extensions to add, + syntax is "key:value,key:value" +- **CLOUDEVENTS_MINIMUMPRIORITY** : minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **AZURE_EVENTHUB_NAME**: Name of the Hub, if not empty, EventHub is _enabled_ +- **AZURE_EVENTHUB_NAMESPACE**: Name of the space the Hub is in +- **AZURE_EVENTHUB_MINIMUMPRIORITY**: minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **GCP_CREDENTIALS**: The base64-encoded JSON key file for the GCP service + account +- **GCP_PUBSUB_PROJECTID**: The GCP Project ID containing the Pub/Sub Topic +- **GCP_PUBSUB_TOPIC**: The name of the Pub/Sub topic +- **GCP_PUBSUB_MINIMUMPRIORITY**: minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **GOOGLECHAT_WEBHOOKURL** : Google Chat URL (ex: + https://chat.googleapis.com/v1/spaces/XXXXXX/YYYYYY), if not `empty`, Google + Chat output is _enabled_ +- **GOOGLECHAT_OUTPUTFORMAT** : `all` (default), `text` (only text is displayed + in Google Chat) +- **GOOGLECHAT_MINIMUMPRIORITY** : minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **GOOGLECHAT_MESSAGEFORMAT** : a Go template to format Google Chat Text above + Attachment, displayed in addition to the output from + `GOOGLECHAT_OUTPUTFORMAT`, see + [Slack Message Formatting](#slack-message-formatting) in the README for + details. If empty, no Text is displayed before sections. +- **KAFKA_HOSTPORT**: The Host:Port of the Kafka (ex: localhost:9092), if not + empty, Kafka is _enabled_ +- **KAFKA_TOPIC**: The name of the Kafka topic +- **KAFKA_PARTITION**: The number of the Kafka partition +- **KAFKA_MINIMUMPRIORITY**: minimum priority of event for using this output, + order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **PAGERDUTY_APIKEY**: Pagerduty API Key, if not empty, Pagerduty output is + _enabled_ +- **PAGERDUTY_SERVICE**: Service to create an incident (mandatory) +- **PAGERDUTY_ASSIGNEE**: A list of comma separated users to assign. Cannot be + provided if `PAGERDUTY_ESCALATION_POLICY` is already specified. If not empty, + Pagerduty is _enabled_ +- **PAGERDUTY_ESCALATION_POLICY**: Escalation policy to assign. Cannot be + provided if `PAGERDUTY_ASSIGNEE` is already specified.If not empty, Pagerduty + is _enabled_ +- **PAGERDUTY_MINIMUMPRIORITY**: minimum priority of event for using this + output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **KUBELESS_FUNCTION**: Name of Kubeless function, if not empty, Kubeless is + _enabled_ +- **KUBELESS_NAMESPACE**: Namespace of Kubeless function (mandatory) +- **KUBELESS_PORT**: Port of service of Kubeless function (default is `8080`) +- **KUBELESS_KUBECONFIG**: Kubeconfig file to use (only if falcoside is running + outside the cluster) +- **KUBELESS_MINIMUMPRIORITY**: "debug" # minimum priority of event for using + this output, order is + `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` #### Slack/Rocketchat/Mattermost/Googlechat Message Formatting -The `SLACK_MESSAGEFORMAT` environment variable and `slack.messageformat` YAML value accept a [Go template](https://golang.org/pkg/text/template/) which can be used to format the text of a slack alert. These templates are evaluated on the JSON data from each Falco event - the following fields are available: +The `SLACK_MESSAGEFORMAT` environment variable and `slack.messageformat` YAML +value accept a [Go template](https://golang.org/pkg/text/template/) which can be +used to format the text of a slack alert. These templates are evaluated on the +JSON data from each Falco event - the following fields are available: | Template Syntax | Description | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -423,18 +560,25 @@ The `SLACK_MESSAGEFORMAT` environment variable and `slack.messageformat` YAML va | `{{ .Time }}` | The timestamp when the event occurred. | | `{{ index .OutputFields \"\" }}` | A map of additional optional fields emitted depending on the event. These may not be present for every event, in which case they expand to the string `` | -Go templates also support some basic methods for text manipulation which can be used to improve the clarity of alerts - see the documentation for details. +Go templates also support some basic methods for text manipulation which can be +used to improve the clarity of alerts - see the documentation for details. ## Handlers Different URI (handlers) are available : -* `/` : main and default handler, your falco config must be configured to use it -* `/ping` : you will get a `pong` as answer, useful to test if falcosidekick is running and its port is opened (for healthcheck purpose for example). This endpoint is deprecated and it will be removed in `3.0.0`. -* `/healthz`: you will get a HTTP status code `200` response as answer, useful to test if falcosidekick is running and its port is opened (for healthcheck or purpose for example) -* `/test` : (for debug only) send a test event to all enabled outputs. -* `/debug/vars` : get statistics from daemon (in JSON format), it uses classic `expvar` package and some custom values are added -* `/metrics` : prometheus endpoint, for scraping metrics about events and `falcosidekick` +- `/` : main and default handler, your falco config must be configured to use it +- `/ping` : you will get a `pong` as answer, useful to test if falcosidekick is + running and its port is opened (for healthcheck purpose for example). This + endpoint is deprecated and it will be removed in `3.0.0`. +- `/healthz`: you will get a HTTP status code `200` response as answer, useful + to test if falcosidekick is running and its port is opened (for healthcheck or + purpose for example) +- `/test` : (for debug only) send a test event to all enabled outputs. +- `/debug/vars` : get statistics from daemon (in JSON format), it uses classic + `expvar` package and some custom values are added +- `/metrics` : prometheus endpoint, for scraping metrics about events and + `falcosidekick` ## Logs @@ -448,7 +592,8 @@ All logs are sent to `stdout`. ### Golang ExpVar -The daemon exposes the common *Golang* metrics and some custom values in JSON format. It's useful for monitoring purpose. +The daemon exposes the common _Golang_ metrics and some custom values in JSON +format. It's useful for monitoring purpose. ![expvar json](https://github.com/falcosecurity/falcosidekick/raw/master/imgs/expvar_json.png) ![expvarmon](https://github.com/falcosecurity/falcosidekick/raw/master/imgs/expvarmon.png) @@ -459,32 +604,34 @@ The daemon exposes a `prometheus` endpoint on URI `/metrics`. ### StatsD / DogStatsD -The daemon is able to push its metrics to a StatsD/DogstatsD server. See [Configuration](https://github.com/falcosecurity/falcosidekick#configuration) section for how-to. - +The daemon is able to push its metrics to a StatsD/DogstatsD server. See +[Configuration](https://github.com/falcosecurity/falcosidekick#configuration) +section for how-to. ### AWS Policy example -When using the AWS output you will need to set the AWS keys with some permissions to access the resources you selected to use, like -`SQS`, `Lambda`, `SNS` and `CloudWatchLogs` +When using the AWS output you will need to set the AWS keys with some +permissions to access the resources you selected to use, like `SQS`, `Lambda`, +`SNS` and `CloudWatchLogs` #### CloudWatch Logs Sample Policy ```json { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "cloudwacthlogs", - "Effect": "Allow", - "Action": [ - "logs:CreateLogStream", - "logs:DescribeLogStreams", - "logs:PutRetentionPolicy", - "logs:PutLogEvents" - ], - "Resource": "*" - } - ] + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "cloudwacthlogs", + "Effect": "Allow", + "Action": [ + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:PutRetentionPolicy", + "logs:PutLogEvents" + ], + "Resource": "*" + } + ] } ``` @@ -492,15 +639,17 @@ When using the AWS output you will need to set the AWS keys with some permission ```json { - "Version": "2012-10-17", - "Id": "sqs", - "Statement": [{ - "Sid":"sendMessage", + "Version": "2012-10-17", + "Id": "sqs", + "Statement": [ + { + "Sid": "sendMessage", "Effect": "Allow", "Principal": "*", "Action": "sqs:SendMessage", "Resource": "arn:aws:sqs:*:111122223333:queue1" - }] + } + ] } ``` @@ -508,15 +657,17 @@ When using the AWS output you will need to set the AWS keys with some permission ```json { - "Version": "2012-10-17", - "Id": "sns", - "Statement": [{ - "Sid":"publish", + "Version": "2012-10-17", + "Id": "sns", + "Statement": [ + { + "Sid": "publish", "Effect": "Allow", "Principal": "*", "Action": "sns:Publish", "Resource": "arn:aws:sqs:*:111122223333:queue1" - }] + } + ] } ``` @@ -524,15 +675,17 @@ When using the AWS output you will need to set the AWS keys with some permission ```json { - "Version": "2012-10-17", - "Id": "lambda", - "Statement": [{ - "Sid":"invoke", + "Version": "2012-10-17", + "Id": "lambda", + "Statement": [ + { + "Sid": "invoke", "Effect": "Allow", "Principal": "*", "Action": "lambda:InvokeFunction", "Resource": "*" - }] + } + ] } ``` @@ -556,7 +709,9 @@ You should get : ![slack no fields example](https://github.com/falcosecurity/falcosidekick/raw/master/imgs/slack_no_fields.png) -(SLACK_OUTPUTFORMAT="**fields**" and SLACK_MESSAGEFORMAT="**Alert : rule \*{{ .Rule }}\* triggered by user \*{{ index .OutputFields \"user.name\" }}\***") +(SLACK_OUTPUTFORMAT="**fields**" and SLACK_MESSAGEFORMAT="**Alert : +rule \*{{ .Rule }}\* triggered by +user \*{{ index .OutputFields \"user.name\" }}\***") ![slack message format example](https://github.com/falcosecurity/falcosidekick/raw/master/imgs/slack_fields_messageformat.png) @@ -576,7 +731,7 @@ You should get : ### Datadog -*(Tip: filter on `sources: falco`)* +_(Tip: filter on `sources: falco`)_ ![datadog example](https://github.com/falcosecurity/falcosidekick/raw/master/imgs/datadog.png) @@ -642,7 +797,6 @@ time akey bkey ckey priority rule value ![google chat text example](https://github.com/falcosecurity/falcosidekick/raw/master/imgs/google_chat_example.png) - ## Development ### Build diff --git a/config.go b/config.go index 932e36a04..f234b5649 100644 --- a/config.go +++ b/config.go @@ -19,6 +19,7 @@ func getConfig() *types.Configuration { c := &types.Configuration{ Customfields: make(map[string]string), Webhook: types.WebhookOutputConfig{CustomHeaders: make(map[string]string)}, + CloudEvents: types.CloudEventsOutputConfig{Extensions: make(map[string]string)}, } configFile := kingpin.Flag("config-file", "config file").Short('c').ExistingFile() @@ -112,6 +113,8 @@ func getConfig() *types.Configuration { v.SetDefault("Customfields", map[string]string{}) v.SetDefault("Webhook.Address", "") v.SetDefault("Webhook.MinimumPriority", "") + v.SetDefault("CloudEvents.Address", "") + v.SetDefault("CloudEvents.MinimumPriority", "") v.SetDefault("Azure.eventHub.Namespace", "") v.SetDefault("Azure.eventHub.Name", "") v.SetDefault("Azure.eventHub.MinimumPriority", "") @@ -155,6 +158,7 @@ func getConfig() *types.Configuration { v.GetStringMapString("customfields") v.GetStringMapString("Webhook.CustomHeaders") + v.GetStringMapString("CloudEvents.Extensions") v.Unmarshal(c) if value, present := os.LookupEnv("CUSTOMFIELDS"); present { @@ -177,6 +181,16 @@ func getConfig() *types.Configuration { } } + if value, present := os.LookupEnv("CLOUDEVENTS_EXTENSIONS"); present { + customfields := strings.Split(value, ",") + for _, label := range customfields { + tagkeys := strings.Split(label, ":") + if len(tagkeys) == 2 { + c.CloudEvents.Extensions[tagkeys[0]] = tagkeys[1] + } + } + } + if c.ListenPort == 0 || c.ListenPort > 65536 { log.Fatalf("[ERROR] : Bad port number\n") } @@ -198,6 +212,7 @@ func getConfig() *types.Configuration { c.AWS.CloudWatchLogs.MinimumPriority = checkPriority(c.AWS.CloudWatchLogs.MinimumPriority) c.Opsgenie.MinimumPriority = checkPriority(c.Opsgenie.MinimumPriority) c.Webhook.MinimumPriority = checkPriority(c.Webhook.MinimumPriority) + c.CloudEvents.MinimumPriority = checkPriority(c.CloudEvents.MinimumPriority) c.Azure.EventHub.MinimumPriority = checkPriority(c.Azure.EventHub.MinimumPriority) c.GCP.PubSub.MinimumPriority = checkPriority(c.GCP.PubSub.MinimumPriority) c.Googlechat.MinimumPriority = checkPriority(c.Googlechat.MinimumPriority) diff --git a/config_example.yaml b/config_example.yaml index 1d2972133..7bdccd3f0 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -118,6 +118,12 @@ webhook: # key: value # minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) +cloudevents: +# address: "" # CloudEvents consumer http address, if not empty, CloudEvents output is enabled +# extensions: # Extensions to add in the outbound Event, useful for routing +# key: value +# minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + azure: eventHub: name: "" # Name of the Hub, if not empty, EventHub is enabled diff --git a/go.mod b/go.mod index b22dac766..ccc0e1f56 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 github.com/emersion/go-smtp v0.14.0 github.com/gkarthiks/k8s-discovery v0.19.0 + github.com/cloudevents/sdk-go/v2 v2.3.1 github.com/google/uuid v1.1.2 github.com/imdario/mergo v0.3.7 // indirect github.com/nats-io/nats-streaming-server v0.19.0 // indirect diff --git a/handlers.go b/handlers.go index d87e9af2f..36e8345f7 100644 --- a/handlers.go +++ b/handlers.go @@ -187,6 +187,10 @@ func forwardEvent(falcopayload types.FalcoPayload) { go webhookClient.WebhookPost(falcopayload) } + if config.CloudEvents.Address != "" && (falcopayload.Priority >= types.Priority(config.CloudEvents.MinimumPriority) || falcopayload.Rule == TestRule) { + go cloudeventsClient.CloudEventsSend(falcopayload) + } + if config.Azure.EventHub.Name != "" && (falcopayload.Priority >= types.Priority(config.Azure.EventHub.MinimumPriority) || falcopayload.Rule == TestRule) { go azureClient.EventHubPost(falcopayload) } diff --git a/main.go b/main.go index a2dad9eae..23a9a52dd 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ var ( smtpClient *outputs.Client opsgenieClient *outputs.Client webhookClient *outputs.Client + cloudeventsClient *outputs.Client azureClient *outputs.Client gcpClient *outputs.Client googleChatClient *outputs.Client @@ -271,6 +272,16 @@ func init() { } } + if config.CloudEvents.Address != "" { + var err error + cloudeventsClient, err = outputs.NewClient("CloudEvents", config.CloudEvents.Address, config, stats, promStats, statsdClient, dogstatsdClient) + if err != nil { + config.CloudEvents.Address = "" + } else { + enabledOutputsText += "CloudEvents " + } + } + if config.Azure.EventHub.Name != "" { var err error azureClient, err = outputs.NewEventHubClient(config, stats, promStats, statsdClient, dogstatsdClient) diff --git a/outputs/client.go b/outputs/client.go index dd740d8ea..e8bb55b61 100644 --- a/outputs/client.go +++ b/outputs/client.go @@ -17,6 +17,7 @@ import ( "github.com/DataDog/datadog-go/statsd" "github.com/PagerDuty/go-pagerduty" "github.com/aws/aws-sdk-go/aws/session" + cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/google/uuid" "github.com/segmentio/kafka-go" "k8s.io/client-go/kubernetes" @@ -47,18 +48,19 @@ var ErrClientCreation = errors.New("Client creation Error") // Client communicates with the different API. type Client struct { - OutputType string - EndpointURL *url.URL - Config *types.Configuration - Stats *types.Statistics - PromStats *types.PromStatistics - AWSSession *session.Session - StatsdClient *statsd.Client - DogstatsdClient *statsd.Client - GCPTopicClient *pubsub.Topic - KafkaProducer *kafka.Conn - PagerdutyClient *pagerduty.Client - KubernetesClient kubernetes.Interface + OutputType string + EndpointURL *url.URL + Config *types.Configuration + Stats *types.Statistics + PromStats *types.PromStatistics + AWSSession *session.Session + StatsdClient *statsd.Client + DogstatsdClient *statsd.Client + GCPTopicClient *pubsub.Topic + KafkaProducer *kafka.Conn + PagerdutyClient *pagerduty.Client + CloudEventsClient cloudevents.Client + KubernetesClient kubernetes.Interface } // NewClient returns a new output.Client for accessing the different API. diff --git a/outputs/cloudevents.go b/outputs/cloudevents.go new file mode 100644 index 000000000..1096b6086 --- /dev/null +++ b/outputs/cloudevents.go @@ -0,0 +1,56 @@ +package outputs + +import ( + "context" + cloudevents "github.com/cloudevents/sdk-go/v2" + "log" + + "github.com/falcosecurity/falcosidekick/types" +) + +// CloudEventsSend produces a CloudEvent and sends to the CloudEvents consumers. +func (c *Client) CloudEventsSend(falcopayload types.FalcoPayload) { + c.Stats.CloudEvents.Add(Total, 1) + + if c.CloudEventsClient == nil { + client, err := cloudevents.NewDefaultClient() + if err != nil { + go c.CountMetric(Outputs, 1, []string{"output:cloudevents", "status:error"}) + log.Printf("[ERROR] : CloudEvents - NewDefaultClient : %v\n", err) + return + } + c.CloudEventsClient = client + } + + ctx := cloudevents.ContextWithTarget(context.Background(), c.EndpointURL.String()) + + event := cloudevents.NewEvent() + event.SetTime(falcopayload.Time) + event.SetSource("falco.org") // TODO: this should have some info on the falco server that made the event. + event.SetType("falco.rule.output.v1") + event.SetExtension("priority", falcopayload.Priority.String()) + event.SetExtension("rule", falcopayload.Rule) + + // Set Extensions. + for k, v := range c.Config.CloudEvents.Extensions { + event.SetExtension(k, v) + } + + if err := event.SetData(cloudevents.ApplicationJSON, falcopayload); err != nil { + log.Printf("[ERROR] : CloudEvents, failed to set data : %v\n", err) + } + + if result := c.CloudEventsClient.Send(ctx, event); cloudevents.IsUndelivered(result) { + go c.CountMetric(Outputs, 1, []string{"output:cloudevents", "status:error"}) + c.Stats.CloudEvents.Add(Error, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "cloudevents", "status": Error}).Inc() + log.Printf("[ERROR] : CloudEvents - %v\n", result) + return + } + + // Setting the success status + go c.CountMetric(Outputs, 1, []string{"output:cloudevents", "status:ok"}) + c.Stats.CloudEvents.Add(OK, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "cloudevents", "status": OK}).Inc() + log.Printf("[INFO] : CloudEvents - Send OK\n") +} diff --git a/stats.go b/stats.go index d16d1f59c..787a03051 100644 --- a/stats.go +++ b/stats.go @@ -43,6 +43,7 @@ func getInitStats() *types.Statistics { Statsd: getOutputNewMap("statsd"), Dogstatsd: getOutputNewMap("dogstatsd"), Webhook: getOutputNewMap("webhook"), + CloudEvents: getOutputNewMap("cloudevents"), AzureEventHub: getOutputNewMap("azureeventhub"), GCPPubSub: getOutputNewMap("gcppubsub"), GoogleChat: getOutputNewMap("googlechat"), diff --git a/types/types.go b/types/types.go index a7da9a694..5f8a84c71 100644 --- a/types/types.go +++ b/types/types.go @@ -41,6 +41,7 @@ type Configuration struct { Statsd statsdOutputConfig Dogstatsd statsdOutputConfig Webhook WebhookOutputConfig + CloudEvents CloudEventsOutputConfig Azure azureConfig GCP gcpOutputConfig Googlechat GooglechatConfig @@ -200,6 +201,13 @@ type WebhookOutputConfig struct { MinimumPriority string } +// CloudEventsOutputConfig represents parameters for CloudEvents +type CloudEventsOutputConfig struct { + Address string + Extensions map[string]string + MinimumPriority string +} + type statsdOutputConfig struct { Forwarder string Namespace string @@ -291,6 +299,7 @@ type Statistics struct { GoogleChat *expvar.Map Kafka *expvar.Map Pagerduty *expvar.Map + CloudEvents *expvar.Map Kubeless *expvar.Map }