Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added fission output type #255

Merged
merged 3 commits into from
Jul 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down
9 changes: 9 additions & 0 deletions config_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
4 changes: 4 additions & 0 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
11 changes: 11 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var (
webUIClient *outputs.Client
rabbitmqClient *outputs.Client
wavefrontClient *outputs.Client
fissionClient *outputs.Client

statsdClient, dogstatsdClient *statsd.Client
config *types.Configuration
Expand Down Expand Up @@ -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)
}

Expand Down
2 changes: 2 additions & 0 deletions outputs/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions outputs/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ const (

Kubeless string = "Kubeless"
Openfaas string = "OpenFaas"
Fission string = "Fission"
)
100 changes: 100 additions & 0 deletions outputs/fission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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.Printf("[ERROR] : %s - %v\n", Fission, err.Error())
return
}
log.Printf("[INFO] : %s - Function Response : %v\n", Fission, 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.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()
}
1 change: 1 addition & 0 deletions stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Configuration struct {
WebUI WebUIOutputConfig
Rabbitmq RabbitmqConfig
Wavefront WavefrontOutputConfig
Fission fissionConfig
}

// SlackOutputConfig represents parameters for Slack
Expand Down Expand Up @@ -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
Expand All @@ -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
}