From 61b04318b9fb5b043b568dd05bc9aefc5dbe289c Mon Sep 17 00:00:00 2001 From: Gaurav Gahlot Date: Tue, 20 Jul 2021 18:47:09 +0530 Subject: [PATCH 1/3] added fission output type Signed-off-by: Gaurav Gahlot --- config.go | 11 +++++ handlers.go | 4 ++ main.go | 11 +++++ outputs/client.go | 2 + outputs/constants.go | 1 + outputs/fission.go | 102 +++++++++++++++++++++++++++++++++++++++++++ stats.go | 1 + types/types.go | 13 ++++++ 8 files changed, 145 insertions(+) create mode 100644 outputs/fission.go diff --git a/config.go b/config.go index eb3210bc9..a2185879b 100644 --- a/config.go +++ b/config.go @@ -197,6 +197,16 @@ func getConfig() *types.Configuration { 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) @@ -302,6 +312,7 @@ func getConfig() *types.Configuration { c.Pagerduty.MinimumPriority = checkPriority(c.Pagerduty.MinimumPriority) c.Kubeless.MinimumPriority = checkPriority(c.Kubeless.MinimumPriority) c.Openfaas.MinimumPriority = checkPriority(c.Openfaas.MinimumPriority) + c.Fission.MinimumPriority = checkPriority(c.Fission.MinimumPriority) c.Rabbitmq.MinimumPriority = checkPriority(c.Rabbitmq.MinimumPriority) c.Wavefront.MinimumPriority = checkPriority(c.Wavefront.MinimumPriority) diff --git a/handlers.go b/handlers.go index 6e366221a..b90e29c3b 100644 --- a/handlers.go +++ b/handlers.go @@ -252,4 +252,8 @@ func forwardEvent(falcopayload types.FalcoPayload) { if config.WebUI.URL != "" { go webUIClient.WebUIPost(falcopayload) } + + if config.Fission.Function != "" && (falcopayload.Priority >= types.Priority(config.Fission.MinimumPriority) || falcopayload.Rule == testRule) { + go fissionClient.FissionCall(falcopayload) + } } diff --git a/main.go b/main.go index b35e57ea3..cbafafaf3 100644 --- a/main.go +++ b/main.go @@ -45,6 +45,7 @@ var ( webUIClient *outputs.Client rabbitmqClient *outputs.Client wavefrontClient *outputs.Client + fissionClient *outputs.Client statsdClient, dogstatsdClient *statsd.Client config *types.Configuration @@ -426,6 +427,16 @@ func init() { } } + if config.Fission.Function != "" { + var err error + fissionClient, err = outputs.NewFissionClient(config, stats, promStats, statsdClient, dogstatsdClient) + if err != nil { + log.Printf("[ERROR] : Fission - %v\n", err) + } else { + outputs.EnabledOutputs = append(outputs.EnabledOutputs, outputs.Fission) + } + } + log.Printf("[INFO] : Enabled Outputs : %s\n", outputs.EnabledOutputs) } diff --git a/outputs/client.go b/outputs/client.go index 1825a6708..ce56ba7f3 100644 --- a/outputs/client.go +++ b/outputs/client.go @@ -209,6 +209,8 @@ func (c *Client) Post(payload interface{}) error { } else if c.OutputType == Openfaas { log.Printf("[INFO] : %v - Function Response : %v\n", Openfaas, string(body)) + } else if c.OutputType == Fission { + log.Printf("[INFO] : %v - Function Response : %v\n", Fission, string(body)) } return nil case http.StatusBadRequest: //400 diff --git a/outputs/constants.go b/outputs/constants.go index 88f6fc31a..0a5616d2d 100644 --- a/outputs/constants.go +++ b/outputs/constants.go @@ -38,4 +38,5 @@ const ( Kubeless string = "Kubeless" Openfaas string = "OpenFaas" + Fission string = "Fission" ) diff --git a/outputs/fission.go b/outputs/fission.go new file mode 100644 index 000000000..51124dd3b --- /dev/null +++ b/outputs/fission.go @@ -0,0 +1,102 @@ +package outputs + +import ( + "context" + "encoding/json" + "log" + "strconv" + + "github.com/DataDog/datadog-go/statsd" + "github.com/google/uuid" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + + "github.com/falcosecurity/falcosidekick/types" +) + +// Some constant strings to use in request headers +const FissionEventIDKey = "event-id" +const FissionEventNamespaceKey = "event-namespace" +const FissionContentType = "application/json" + +// NewFissionClient returns a new output.Client for accessing Kubernetes. +func NewFissionClient(config *types.Configuration, stats *types.Statistics, promStats *types.PromStatistics, + statsdClient, dogstatsdClient *statsd.Client) (*Client, error) { + if config.Fission.KubeConfig != "" { + restConfig, err := clientcmd.BuildConfigFromFlags("", config.Fission.KubeConfig) + if err != nil { + return nil, err + } + clientset, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return nil, err + } + return &Client{ + OutputType: Fission, + Config: config, + Stats: stats, + PromStats: promStats, + StatsdClient: statsdClient, + DogstatsdClient: dogstatsdClient, + KubernetesClient: clientset, + }, nil + } + return NewClient( + Fission, + "http://"+config.Fission.RouterService+"."+config.Fission.RouterNamespace+ + ".svc.cluster.local:"+strconv.Itoa(config.Fission.RouterPort)+ + "/fission-function/"+config.Fission.Function, + config.Fission.MutualTLS, + config.Fission.CheckCert, + config, + stats, + promStats, + statsdClient, + dogstatsdClient, + ) +} + +// FissionCall . +func (c *Client) FissionCall(falcopayload types.FalcoPayload) { + c.Stats.Fission.Add(Total, 1) + + if c.Config.Fission.KubeConfig != "" { + str, _ := json.Marshal(falcopayload) + req := c.KubernetesClient.CoreV1().RESTClient().Post().AbsPath("/api/v1/namespaces/" + + c.Config.Fission.RouterNamespace + "/services/" + c.Config.Fission.RouterService + + ":" + strconv.Itoa(c.Config.Fission.RouterPort) + "/proxy/" + "/fission-function/" + + c.Config.Fission.Function).Body(str) + req.SetHeader(FissionEventIDKey, uuid.New().String()) + req.SetHeader(ContentTypeHeaderKey, FissionContentType) + req.SetHeader(UserAgentHeaderKey, UserAgentHeaderValue) + + res := req.Do(context.TODO()) + rawbody, err := res.Raw() + if err != nil { + go c.CountMetric(Outputs, 1, []string{"output:Fission", "status:error"}) + c.Stats.Fission.Add(Error, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "Fission", "status": Error}).Inc() + log.Println("[INFO] : Fission - KubeConfig not empty") + log.Printf("[ERROR] : Fission - %v\n", err.Error()) + return + } + log.Printf("[INFO] : Fission - Function Response : %v\n", string(rawbody)) + } else { + c.AddHeader(FissionEventIDKey, uuid.New().String()) + c.ContentType = FissionContentType + + err := c.Post(falcopayload) + if err != nil { + go c.CountMetric(Outputs, 1, []string{"output:Fission", "status:error"}) + c.Stats.Fission.Add(Error, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "Fission", "status": Error}).Inc() + log.Println("[INFO] : Fission - KubeConfig empty") + log.Printf("[ERROR] : %s - %v\n", Fission, err.Error()) + return + } + } + log.Printf("[INFO] : %s - Call Function \"%v\" OK\n", Fission, c.Config.Fission.Function) + go c.CountMetric(Outputs, 1, []string{"output:Fission", "status:ok"}) + c.Stats.Fission.Add(OK, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "Fission", "status": OK}).Inc() +} diff --git a/stats.go b/stats.go index 89a7bac37..ad5dfd2ad 100644 --- a/stats.go +++ b/stats.go @@ -58,6 +58,7 @@ func getInitStats() *types.Statistics { WebUI: getOutputNewMap("webui"), Rabbitmq: getOutputNewMap("rabbitmq"), Wavefront: getOutputNewMap("wavefront"), + Fission: getOutputNewMap("fission"), } stats.Falco.Add(outputs.Emergency, 0) stats.Falco.Add(outputs.Alert, 0) diff --git a/types/types.go b/types/types.go index 3b6ec441b..4c206d1d3 100644 --- a/types/types.go +++ b/types/types.go @@ -54,6 +54,7 @@ type Configuration struct { WebUI WebUIOutputConfig Rabbitmq RabbitmqConfig Wavefront WavefrontOutputConfig + Fission fissionConfig } // SlackOutputConfig represents parameters for Slack @@ -414,6 +415,7 @@ type Statistics struct { WebUI *expvar.Map Rabbitmq *expvar.Map Wavefront *expvar.Map + Fission *expvar.Map } // PromStatistics is a struct to store prometheus metrics @@ -422,3 +424,14 @@ type PromStatistics struct { Inputs *prometheus.CounterVec Outputs *prometheus.CounterVec } + +type fissionConfig struct { + RouterNamespace string + RouterService string + RouterPort int + Function string + KubeConfig string + MinimumPriority string + CheckCert bool + MutualTLS bool +} From 01245f489ea916de747ce6069cad9d7083cc1ee0 Mon Sep 17 00:00:00 2001 From: Gaurav Gahlot Date: Wed, 21 Jul 2021 15:43:01 +0530 Subject: [PATCH 2/3] updated readme and config_example for fission Signed-off-by: Gaurav Gahlot --- README.md | 20 +++++++++++++++++++- config_example.yaml | 9 +++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9554512a1..7bb40d4e6 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ It works as a single endpoint for as many as you want `Falco` instances : - [**OpenFaaS**](https://www.openfaas.com) - [**GCP Cloud Run**](https://cloud.google.com/run) - [**GCP Cloud Functions**](https://cloud.google.com/functions) +- [**Fission**](https://fission.io) ### Message queue / Streaming @@ -382,7 +383,15 @@ wavefront: batchsize: 10000 # max batch of data sent per flush interval. defaults to 10,000. Used only in direct mode flushintervalseconds: 1 # Time in seconds between flushing metrics to Wavefront. Defaults to 1s # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) - + +fission: + function: "" # Name of Fission function, if not empty, Fission is enabled + routernamespace: "fission" # Namespace of Fission Router, "fission" (default) + routerservice: "router" # Service of Fission Router, "router" (default) + routerport: 80 # Port of service of Fission Router + # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + # checkcert: true # check if ssl certificate of the output is valid (default: true) + # mutualtls: false # if true, checkcert flag will be ignored (server cert will always be checked) webui: url: "" # WebUI URL, if not empty, WebUI output is enabled @@ -718,6 +727,15 @@ care of lower/uppercases**) : `yaml: a.b --> envvar: A_B` : - **WAVEFRONT_METRICNAME**: "falco.alert" # Metric name to be created/used in Wavefront - **WAVEFRONT_MINIMUMPRIORITY**: "debug" # minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **FISSION_FUNCTION**: Name of Fission function, if not empty, Fission is enabled +- **FISSION_ROUTERNAMESPACE**: Namespace of Fission Router, "fission" (default) +- **FISSION_ROUTERSERVICE**: Service of Fission Router, "router" (default) +- **FISSION_ROUTERPORT**: Port of service of Fission Router +- **FISSION_MINIMUMPRIORITY**: "debug" # minimum priority of event for using + this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **FISSION_MUTUALTLS**: if true, checkcert flag will be ignored (server cert will always be checked) +- **FISSION_CHECKCERT**: check if ssl certificate of the output is valid (default: `true`) + #### Slack/Rocketchat/Mattermost/Googlechat Message Formatting The `SLACK_MESSAGEFORMAT` environment variable and `slack.messageformat` YAML diff --git a/config_example.yaml b/config_example.yaml index c76377eac..568529290 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -233,3 +233,12 @@ wavefront: webui: url: "" # WebUI URL, if not empty, WebUI output is enabled + +fission: + function: "" # Name of Fission function, if not empty, Fission is enabled + routernamespace: "fission" # Namespace of Fission Router, "fission" (default) + routerservice: "router" # Service of Fission Router, "router" (default) + routerport: 80 # Port of service of Fission Router + # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + # checkcert: true # check if ssl certificate of the output is valid (default: true) + # mutualtls: false # if true, checkcert flag will be ignored (server cert will always be checked) From bb43dacd53b9d54ef49dc5a959d8234678ea86ba Mon Sep 17 00:00:00 2001 From: Gaurav Gahlot Date: Wed, 21 Jul 2021 15:48:48 +0530 Subject: [PATCH 3/3] logs cleaned up Signed-off-by: Gaurav Gahlot --- outputs/fission.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/outputs/fission.go b/outputs/fission.go index 51124dd3b..cbe1742c3 100644 --- a/outputs/fission.go +++ b/outputs/fission.go @@ -76,11 +76,10 @@ func (c *Client) FissionCall(falcopayload types.FalcoPayload) { go c.CountMetric(Outputs, 1, []string{"output:Fission", "status:error"}) c.Stats.Fission.Add(Error, 1) c.PromStats.Outputs.With(map[string]string{"destination": "Fission", "status": Error}).Inc() - log.Println("[INFO] : Fission - KubeConfig not empty") - log.Printf("[ERROR] : Fission - %v\n", err.Error()) + log.Printf("[ERROR] : %s - %v\n", Fission, err.Error()) return } - log.Printf("[INFO] : Fission - Function Response : %v\n", string(rawbody)) + log.Printf("[INFO] : %s - Function Response : %v\n", Fission, string(rawbody)) } else { c.AddHeader(FissionEventIDKey, uuid.New().String()) c.ContentType = FissionContentType @@ -90,7 +89,6 @@ func (c *Client) FissionCall(falcopayload types.FalcoPayload) { go c.CountMetric(Outputs, 1, []string{"output:Fission", "status:error"}) c.Stats.Fission.Add(Error, 1) c.PromStats.Outputs.With(map[string]string{"destination": "Fission", "status": Error}).Inc() - log.Println("[INFO] : Fission - KubeConfig empty") log.Printf("[ERROR] : %s - %v\n", Fission, err.Error()) return }