From dda7f4c825aaa9e6b1f5e1282e1ba21a69d4c2a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Batuhan=20Apayd=C4=B1n?= Date: Thu, 20 May 2021 22:49:44 +0300 Subject: [PATCH] add Google Cloud Functions output type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Batuhan Apaydın --- README.md | 14 +++++++--- config.go | 3 +++ go.mod | 5 +++- go.sum | 10 +++++++ handlers.go | 4 +++ main.go | 6 ++++- outputs/client.go | 24 +++++++++-------- outputs/gcp.go | 69 +++++++++++++++++++++++++++++++++++++++++------ stats.go | 1 + types/types.go | 7 +++++ 10 files changed, 119 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 0272c97bd..07ade8152 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Currently available outputs are : `falcosidekick`) - [**GCP PubSub**](https://cloud.google.com/pubsub) - [**GCP Storage**](https://cloud.google.com/storage) +- [**GCP Cloud Functions**](https://cloud.google.com/functions) - [**Google Chat**](https://workspace.google.com/products/chat/) - [**Apache Kafka**](https://kafka.apache.org/) - [**PagerDuty**](https://pagerduty.com/) @@ -296,11 +297,14 @@ gcp: pubsub: projectid: "" # The GCP Project ID containing the Pub/Sub Topic topic: "" # The name of the Pub/Sub topic - # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) storage: # prefix : "" # name of prefix, keys will have format: gs:////YYYY-MM-DD/YYYY-MM-DDTHH:mm:ss.s+01:00.json bucket: "" # The name of the bucket - # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) + cloudfunctions: + name: "" # The name of the Cloud Function + # minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default) googlechat: webhookurl: "" # Google Chat WebhookURL (ex: https://chat.googleapis.com/v1/spaces/XXXXXX/YYYYYY), if not empty, Google Chat output is enabled @@ -599,11 +603,15 @@ care of lower/uppercases**) : `yaml: a.b --> envvar: A_B` : - **GCP_PUBSUB_TOPIC**: The name of the Pub/Sub topic - **GCP_PUBSUB_MINIMUMPRIORITY**: minimum priority of event for using this output, order is -- **GCP_STORAGE_BUCKET**: # The name of the bucket +- **GCP_STORAGE_BUCKET**: The name of the bucket - **GCP_STORAGE_PREFIX**: name of prefix, keys will have format: gs:////YYYY-MM-DD/YYYY-MM-DDTHH:mm:ss.s+01:00.json - **GCP_STORAGE_MINIMUMPRIORITY**: minimum priority of event for using this output, order is `emergency|alert|critical|error|warning|notice|informational|debug or "" (default)` +- **GCP_CLOUDFUNCTIONS_NAME**: The name of the Cloud Function +- **GCP_CLOUDFUNCTIONS_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_ diff --git a/config.go b/config.go index 200f4712e..fa6b21e77 100644 --- a/config.go +++ b/config.go @@ -159,6 +159,8 @@ func getConfig() *types.Configuration { v.SetDefault("GCP.Storage.Prefix", "") v.SetDefault("GCP.Storage.Bucket", "") v.SetDefault("GCP.Storage.MinimumPriority", "") + v.SetDefault("GCP.CloudFunctions.Name", "") + v.SetDefault("GCP.CloudFunctions.MinimumPriority", "") v.SetDefault("Googlechat.WebhookURL", "") v.SetDefault("Googlechat.OutputFormat", "all") v.SetDefault("Googlechat.MessageFormat", "") @@ -288,6 +290,7 @@ func getConfig() *types.Configuration { c.Azure.EventHub.MinimumPriority = checkPriority(c.Azure.EventHub.MinimumPriority) c.GCP.PubSub.MinimumPriority = checkPriority(c.GCP.PubSub.MinimumPriority) c.GCP.Storage.MinimumPriority = checkPriority(c.GCP.Storage.MinimumPriority) + c.GCP.CloudFunctions.MinimumPriority = checkPriority(c.GCP.CloudFunctions.MinimumPriority) c.Googlechat.MinimumPriority = checkPriority(c.Googlechat.MinimumPriority) c.Kafka.MinimumPriority = checkPriority(c.Kafka.MinimumPriority) c.Pagerduty.MinimumPriority = checkPriority(c.Pagerduty.MinimumPriority) diff --git a/go.mod b/go.mod index 86d783b4b..a36665e3c 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/falcosecurity/falcosidekick go 1.16 require ( + cloud.google.com/go v0.75.0 cloud.google.com/go/pubsub v1.9.1 cloud.google.com/go/storage v1.14.0 github.com/Azure/azure-event-hubs-go/v3 v3.3.4 @@ -13,6 +14,7 @@ require ( github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 github.com/emersion/go-smtp v0.14.0 github.com/google/uuid v1.2.0 + github.com/googleapis/gax-go v1.0.3 github.com/imdario/mergo v0.3.7 // indirect github.com/nats-io/nats-streaming-server v0.19.0 // indirect github.com/nats-io/nats.go v1.10.0 @@ -22,9 +24,10 @@ require ( github.com/spf13/viper v1.7.1 github.com/streadway/amqp v1.0.0 github.com/stretchr/testify v1.7.0 - github.com/wavefronthq/wavefront-sdk-go v0.9.8 // indirect + github.com/wavefronthq/wavefront-sdk-go v0.9.8 golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93 google.golang.org/api v0.40.0 + google.golang.org/genproto v0.0.0-20210226172003-ab064af71705 gopkg.in/alecthomas/kingpin.v2 v2.2.6 k8s.io/client-go v0.20.4 ) diff --git a/go.sum b/go.sum index ebf7dfc9b..266dff1d9 100644 --- a/go.sum +++ b/go.sum @@ -252,6 +252,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -316,6 +317,9 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v1.0.3 h1:9dMLqhaibYONnDRcnHdUs9P8Mw64jLlZTYlDe3leBtQ= +github.com/googleapis/gax-go v1.0.3/go.mod h1:QyXYajJFdARxGzjwUfbDFIse7Spkw81SJ4LrBJXtlQ8= +github.com/googleapis/gax-go/v2 v2.0.2/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -423,6 +427,7 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -684,6 +689,7 @@ golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190221220918-438050ddec5e/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -692,10 +698,12 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -920,6 +928,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw= gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= @@ -993,6 +1002,7 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705 h1:PYBmACG+YEv8uQPW0r1kJj8tR+gkF0UWq7iFdUezwEw= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= diff --git a/handlers.go b/handlers.go index 7c45c5373..8f5fd0a96 100644 --- a/handlers.go +++ b/handlers.go @@ -209,6 +209,10 @@ func forwardEvent(falcopayload types.FalcoPayload) { go gcpClient.GCPPublishTopic(falcopayload) } + if config.GCP.CloudFunctions.Name != "" && (falcopayload.Priority >= types.Priority(config.GCP.CloudFunctions.MinimumPriority) || falcopayload.Rule == testRule) { + go gcpClient.GCPCallCloudFunction(falcopayload) + } + if config.GCP.Storage.Bucket != "" && (falcopayload.Priority >= types.Priority(config.GCP.Storage.MinimumPriority) || falcopayload.Rule == testRule) { go gcpClient.UploadGCS(falcopayload) } diff --git a/main.go b/main.go index 833ccf41e..60c47dcf2 100644 --- a/main.go +++ b/main.go @@ -304,13 +304,14 @@ func init() { } } - if (config.GCP.PubSub.ProjectID != "" && config.GCP.PubSub.Topic != "") || config.GCP.Storage.Bucket != "" { + if (config.GCP.PubSub.ProjectID != "" && config.GCP.PubSub.Topic != "") || config.GCP.Storage.Bucket != "" || config.GCP.CloudFunctions.Name != "" { var err error gcpClient, err = outputs.NewGCPClient(config, stats, promStats, statsdClient, dogstatsdClient) if err != nil { config.GCP.PubSub.ProjectID = "" config.GCP.PubSub.Topic = "" config.GCP.Storage.Bucket = "" + config.GCP.CloudFunctions.Name = "" } else { if config.GCP.PubSub.Topic != "" && config.GCP.PubSub.ProjectID != "" { outputs.EnabledOutputs = append(outputs.EnabledOutputs, "GCPPubSub") @@ -318,6 +319,9 @@ func init() { if config.GCP.Storage.Bucket != "" { outputs.EnabledOutputs = append(outputs.EnabledOutputs, "GCPStorage") } + if config.GCP.CloudFunctions.Name != "" { + outputs.EnabledOutputs = append(outputs.EnabledOutputs, "GCPCloudFunctions") + } } } diff --git a/outputs/client.go b/outputs/client.go index 74d98e80c..a502520eb 100644 --- a/outputs/client.go +++ b/outputs/client.go @@ -14,6 +14,7 @@ import ( "regexp" "strings" + gcpfunctions "cloud.google.com/go/functions/apiv1" "github.com/streadway/amqp" wavefront "github.com/wavefronthq/wavefront-sdk-go/senders" @@ -60,17 +61,18 @@ const MutualTLSCacertFilename = "/ca.crt" // Client communicates with the different API. type Client struct { - OutputType string - EndpointURL *url.URL - MutualTLSEnabled bool - CheckCert bool - Config *types.Configuration - Stats *types.Statistics - PromStats *types.PromStatistics - AWSSession *session.Session - StatsdClient *statsd.Client - DogstatsdClient *statsd.Client - GCPTopicClient *pubsub.Topic + OutputType string + EndpointURL *url.URL + MutualTLSEnabled bool + CheckCert bool + Config *types.Configuration + Stats *types.Statistics + PromStats *types.PromStatistics + AWSSession *session.Session + StatsdClient *statsd.Client + DogstatsdClient *statsd.Client + GCPTopicClient *pubsub.Topic + GCPCloudFunctionsClient *gcpfunctions.CloudFunctionsClient GCSStorageClient *storage.Client KafkaProducer *kafka.Writer diff --git a/outputs/gcp.go b/outputs/gcp.go index 2da26eed9..4d7cd6b29 100644 --- a/outputs/gcp.go +++ b/outputs/gcp.go @@ -9,10 +9,13 @@ import ( "log" "time" + gcpfunctions "cloud.google.com/go/functions/apiv1" "cloud.google.com/go/storage" + gcpfunctionspb "google.golang.org/genproto/googleapis/cloud/functions/v1" "cloud.google.com/go/pubsub" "github.com/DataDog/datadog-go/statsd" + "github.com/googleapis/gax-go" "golang.org/x/oauth2/google" "google.golang.org/api/option" @@ -30,6 +33,7 @@ func NewGCPClient(config *types.Configuration, stats *types.Statistics, promStat googleCredentialsData := string(base64decodedCredentialsData) var topicClient *pubsub.Topic var storageClient *storage.Client + var cloudFunctionsClient *gcpfunctions.CloudFunctionsClient if config.GCP.PubSub.ProjectID != "" && config.GCP.PubSub.Topic != "" { if googleCredentialsData != "" { @@ -67,18 +71,67 @@ func NewGCPClient(config *types.Configuration, stats *types.Statistics, promStat } } + if config.GCP.CloudFunctions.Name != "" { + if googleCredentialsData != "" { + credentials, err := google.CredentialsFromJSON(context.Background(), []byte(googleCredentialsData), gcpfunctions.DefaultAuthScopes()...) + if err != nil { + log.Printf("[ERROR] : GCP CloudFunctions - %v\n", "Error while loading GCS Credentials") + return nil, errors.New("Error while loading GCP Credentials") + } + cloudFunctionsClient, err = gcpfunctions.NewCloudFunctionsClient(context.Background(), option.WithCredentials(credentials)) + if err != nil { + log.Printf("[ERROR]: GCP CloudFunctions - %v\n", "Error while creating GCP CloudFunctions Client") + return nil, errors.New("Error while creating GCP CloudFunctions Client") + } + } else { + cloudFunctionsClient, err = gcpfunctions.NewCloudFunctionsClient(context.Background()) + if err != nil { + log.Printf("[ERROR]: GCP CloudFunctions - %v\n", "Error while creating GCP CloudFunctions Client") + return nil, errors.New("Error while creating GCP CloudFunctions Client") + } + } + } + return &Client{ - OutputType: "GCP", - Config: config, - GCPTopicClient: topicClient, - GCSStorageClient: storageClient, - Stats: stats, - PromStats: promStats, - StatsdClient: statsdClient, - DogstatsdClient: dogstatsdClient, + OutputType: "GCP", + Config: config, + GCPTopicClient: topicClient, + GCSStorageClient: storageClient, + GCPCloudFunctionsClient: cloudFunctionsClient, + Stats: stats, + PromStats: promStats, + StatsdClient: statsdClient, + DogstatsdClient: dogstatsdClient, }, nil } +// GCPCallCloudFunction calls the given Cloud Function +func (c *Client) GCPCallCloudFunction(falcopayload types.FalcoPayload) { + c.Stats.GCPCloudFunctions.Add(Total, 1) + + payload, _ := json.Marshal(falcopayload) + data := string(payload) + + result, err := c.GCPCloudFunctionsClient.CallFunction(context.Background(), &gcpfunctionspb.CallFunctionRequest{ + Name: c.Config.GCP.CloudFunctions.Name, + Data: data, + }, gax.WithGRPCOptions()) + + if err != nil { + log.Printf("[ERROR] : GCPCloudFunctions - %v - %v\n", "Error while calling CloudFunction", err.Error()) + c.Stats.GCPPubSub.Add(Error, 1) + go c.CountMetric("outputs", 1, []string{"output:gcpcloudfunctions", "status:error"}) + c.PromStats.Outputs.With(map[string]string{"destination": "gcpcloudfunctions", "status": Error}).Inc() + + return + } + + log.Printf("[INFO] : GCPCloudFunctions - Call CloudFunction OK (%v)\n", result.ExecutionId) + c.Stats.GCPCloudFunctions.Add(OK, 1) + go c.CountMetric("outputs", 1, []string{"output:gcpcloudfunctions", "status:ok"}) + +} + // GCPPublishTopic sends a message to a GCP PubSub Topic func (c *Client) GCPPublishTopic(falcopayload types.FalcoPayload) { c.Stats.GCPPubSub.Add(Total, 1) diff --git a/stats.go b/stats.go index e8033dae4..cb6f3c839 100644 --- a/stats.go +++ b/stats.go @@ -48,6 +48,7 @@ func getInitStats() *types.Statistics { AzureEventHub: getOutputNewMap("azureeventhub"), GCPPubSub: getOutputNewMap("gcppubsub"), GCPStorage: getOutputNewMap("gcpstorage"), + GCPCloudFunctions: getOutputNewMap("gcpcloudfunctions"), GoogleChat: getOutputNewMap("googlechat"), Kafka: getOutputNewMap("kafka"), Pagerduty: getOutputNewMap("pagerduty"), diff --git a/types/types.go b/types/types.go index cb6041e57..fb213f8d7 100644 --- a/types/types.go +++ b/types/types.go @@ -283,6 +283,12 @@ type gcpOutputConfig struct { WorkloadIdentity bool PubSub gcpPubSub Storage gcpStorage + CloudFunctions gcpCloudFunctions +} + +type gcpCloudFunctions struct { + Name string + MinimumPriority string } type gcpPubSub struct { @@ -388,6 +394,7 @@ type Statistics struct { AzureEventHub *expvar.Map GCPPubSub *expvar.Map GCPStorage *expvar.Map + GCPCloudFunctions *expvar.Map GoogleChat *expvar.Map Kafka *expvar.Map Pagerduty *expvar.Map