From 9517e97da4bee7e126906f1f5506e78b56a22507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Batuhan=20Apayd=C4=B1n?= Date: Thu, 11 Mar 2021 11:38:55 +0300 Subject: [PATCH] openfaas output type added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Batuhan Apaydın --- config.go | 10 ++++++ config_example.yaml | 9 +++++ handlers.go | 4 +++ main.go | 11 ++++++ outputs/constants.go | 1 + outputs/openfaas.go | 84 ++++++++++++++++++++++++++++++++++++++++++++ stats.go | 1 + types/types.go | 12 +++++++ 8 files changed, 132 insertions(+) create mode 100644 outputs/openfaas.go diff --git a/config.go b/config.go index 8214416154..e675f0c4f6 100644 --- a/config.go +++ b/config.go @@ -141,6 +141,15 @@ func getConfig() *types.Configuration { v.SetDefault("Kubeless.Port", 8080) v.SetDefault("Kubeless.Kubeconfig", "") v.SetDefault("Kubeless.MinimumPriority", "") + + 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("Webui.URL", "") v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) @@ -227,6 +236,7 @@ func getConfig() *types.Configuration { c.Kafka.MinimumPriority = checkPriority(c.Kafka.MinimumPriority) c.Pagerduty.MinimumPriority = checkPriority(c.Pagerduty.MinimumPriority) c.Kubeless.MinimumPriority = checkPriority(c.Kubeless.MinimumPriority) + c.Openfaas.MinimumPriority = checkPriority(c.Openfaas.MinimumPriority) c.Slack.MessageFormatTemplate = getMessageFormatTemplate("Slack", c.Slack.MessageFormat) c.Rocketchat.MessageFormatTemplate = getMessageFormatTemplate("Rocketchat", c.Rocketchat.MessageFormat) diff --git a/config_example.yaml b/config_example.yaml index 23359bfbaf..01493adbb5 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -166,5 +166,14 @@ kubeless: kubeconfig: "~/.kube/config" # Kubeconfig file to use (only if falcoside is running outside the cluster) # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) +openfaas: + gatewaynamespace: "" # Namespace of OpenFaaS Gateway, "openfaas" (default) + gatewayservice: "" # Service of OpenFaaS Gateway, "gateway" (default) + functionname: "" # Name of OpenFaaS function, if not empty, OpenFaaS is enabled + functionnamespace: "" # Namespace of OpenFaaS function, "openfaas-fn" (default) + gatewayport: 8080 # Port of service of OpenFaaS Gateway + kubeconfig: "~/.kube/config" # Kubeconfig file to use (only if falcosidekick is running outside the cluster) + # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + webui: url: "" # WebUI URL, if not empty, WebUI output is enabled diff --git a/handlers.go b/handlers.go index 5cb8ce2b5f..cf28d6397f 100644 --- a/handlers.go +++ b/handlers.go @@ -225,6 +225,10 @@ func forwardEvent(falcopayload types.FalcoPayload) { go kubelessClient.KubelessCall(falcopayload) } + if config.Openfaas.FunctionName != "" && (falcopayload.Priority >= types.Priority(config.Openfaas.MinimumPriority) || falcopayload.Rule == testRule) { + go openfaasClient.OpenfaasCall(falcopayload) + } + if config.WebUI.URL != "" { go webUIClient.WebUIPost(falcopayload) } diff --git a/main.go b/main.go index 10351615c2..dbd547277f 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,7 @@ var ( kafkaClient *outputs.Client pagerdutyClient *outputs.Client kubelessClient *outputs.Client + openfaasClient *outputs.Client webUIClient *outputs.Client statsdClient, dogstatsdClient *statsd.Client @@ -369,6 +370,16 @@ func init() { } } + if config.Openfaas.FunctionName != "" { + var err error + openfaasClient, err = outputs.NewOpenfaasClient(config, stats, promStats, statsdClient, dogstatsdClient) + if err != nil { + log.Printf("[ERROR] : OpenFaaS - %v\n", err) + } else { + outputs.EnabledOutputs = append(outputs.EnabledOutputs, "OpenFaaS") + } + } + log.Printf("[INFO] : Enabled Outputs : %s\n", outputs.EnabledOutputs) } diff --git a/outputs/constants.go b/outputs/constants.go index 002d9ad28a..ded783596d 100644 --- a/outputs/constants.go +++ b/outputs/constants.go @@ -37,4 +37,5 @@ const ( Orange string = "#ff5400" Kubeless string = "Kubeless" + Openfaas string = "Openfaas" ) diff --git a/outputs/openfaas.go b/outputs/openfaas.go new file mode 100644 index 0000000000..de907f3e83 --- /dev/null +++ b/outputs/openfaas.go @@ -0,0 +1,84 @@ +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" +) + +// NewOpenfaasClient returns a new output.Client for accessing Kubernetes. +func NewOpenfaasClient(config *types.Configuration, stats *types.Statistics, promStats *types.PromStatistics, statsdClient, dogstatsdClient *statsd.Client) (*Client, error) { + if config.Openfaas.Kubeconfig != "" { + restConfig, err := clientcmd.BuildConfigFromFlags("", config.Openfaas.Kubeconfig) + if err != nil { + return nil, err + } + clientset, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return nil, err + } + return &Client{ + OutputType: "OpenFaaS", + Config: config, + Stats: stats, + PromStats: promStats, + StatsdClient: statsdClient, + DogstatsdClient: dogstatsdClient, + KubernetesClient: clientset, + }, nil + } + return NewClient( + "OpenFaaS", + "http://"+config.Openfaas.GatewayService+"."+config.Openfaas.GatewayNamespace+":"+strconv.Itoa(config.Openfaas.GatewayPort)+"/function/"+config.Openfaas.FunctionName+"."+config.Openfaas.FunctionNamespace, + config, + stats, + promStats, + statsdClient, + dogstatsdClient, + ) +} + +// OpenfaasCall . +func (c *Client) OpenfaasCall(falcopayload types.FalcoPayload) { + c.Stats.Openfaas.Add(Total, 1) + + if c.Config.Openfaas.Kubeconfig != "" { + str, _ := json.Marshal(falcopayload) + req := c.KubernetesClient.CoreV1().RESTClient().Post().AbsPath("/api/v1/namespaces/" + c.Config.Openfaas.GatewayNamespace + "/services/" + c.Config.Openfaas.GatewayService + ":" + strconv.Itoa(c.Config.Openfaas.GatewayPort) + "/proxy" + "/function/" + c.Config.Openfaas.FunctionName + "." + c.Config.Openfaas.FunctionNamespace).Body(str) + req.SetHeader("event-id", uuid.New().String()) + req.SetHeader("Content-Type", "application/json") + req.SetHeader("User-Agent", "Falcosidekick") + + res := req.Do(context.TODO()) + rawbody, err := res.Raw() + if err != nil { + go c.CountMetric(Outputs, 1, []string{"output:OpenFaaS", "status:error"}) + c.Stats.Openfaas.Add(Error, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "OpenFaaS", "status": Error}).Inc() + log.Printf("[ERROR] : Openfaas - %v\n", err) + return + } + log.Printf("[INFO] : OpenFaaS - Function Response : %v\n", string(rawbody)) + } else { + err := c.Post(falcopayload) + if err != nil { + go c.CountMetric(Outputs, 1, []string{"output:OpenFaaS", "status:error"}) + c.Stats.Openfaas.Add(Error, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "OpenFaaS", "status": Error}).Inc() + log.Printf("[ERROR] : OpenFaaS - %v\n", err) + return + } + } + log.Printf("[INFO] : OpenFaaS - Call Function \"%v\" OK\n", c.Config.Openfaas.FunctionName+"."+c.Config.Openfaas.FunctionNamespace) + go c.CountMetric(Outputs, 1, []string{"output:OpenFaaS", "status:ok"}) + c.Stats.Openfaas.Add(OK, 1) + c.PromStats.Outputs.With(map[string]string{"destination": "OpenFaaS", "status": OK}).Inc() +} diff --git a/stats.go b/stats.go index 7fe4f57cae..2791a9d42c 100644 --- a/stats.go +++ b/stats.go @@ -51,6 +51,7 @@ func getInitStats() *types.Statistics { Kafka: getOutputNewMap("kafka"), Pagerduty: getOutputNewMap("pagerduty"), Kubeless: getOutputNewMap("kubeless"), + Openfaas: getOutputNewMap("openfaas"), WebUI: getOutputNewMap("webui"), } stats.Falco.Add(outputs.Emergency, 0) diff --git a/types/types.go b/types/types.go index 8054fe48d7..b018ed0191 100644 --- a/types/types.go +++ b/types/types.go @@ -50,6 +50,7 @@ type Configuration struct { Kafka kafkaConfig Pagerduty PagerdutyConfig Kubeless kubelessConfig + Openfaas openfaasConfig WebUI WebUIOutputConfig } @@ -274,6 +275,16 @@ type kubelessConfig struct { MinimumPriority string } +type openfaasConfig struct { + GatewayNamespace string + GatewayService string + FunctionName string + FunctionNamespace string + GatewayPort int + Kubeconfig string + MinimumPriority string +} + // WebUIOutputConfig represents parameters for WebUI type WebUIOutputConfig struct { URL string @@ -314,6 +325,7 @@ type Statistics struct { Pagerduty *expvar.Map CloudEvents *expvar.Map Kubeless *expvar.Map + Openfaas *expvar.Map WebUI *expvar.Map }