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

vote: Add k8saudit-ovh plugin #554

Merged
merged 3 commits into from
Jan 16, 2025
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
3 changes: 3 additions & 0 deletions plugins/k8saudit-ovh/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
libk8saudit-ovh.so
.vscode
falco.yaml
5 changes: 5 additions & 0 deletions plugins/k8saudit-ovh/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## v0.1.0

* First version of the `k8saudit-ovh` plugin 🎉
22 changes: 22 additions & 0 deletions plugins/k8saudit-ovh/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
SHELL=/bin/bash -o pipefail
GO ?= go

NAME := k8saudit-ovh
OUTPUT := lib$(NAME).so

ifeq ($(DEBUG), 1)
GODEBUGFLAGS= GODEBUG=cgocheck=1
else
GODEBUGFLAGS= GODEBUG=cgocheck=0
endif

all: build

clean:
@rm -f lib$(NAME).so

build: clean
@$(GODEBUGFLAGS) $(GO) build -buildmode=c-shared -buildvcs=false -o $(OUTPUT) ./plugin

install:
sudo cp $(OUTPUT) /usr/share/falco/plugins/
3 changes: 3 additions & 0 deletions plugins/k8saudit-ovh/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
approvers:
- scraly
- Issif
218 changes: 218 additions & 0 deletions plugins/k8saudit-ovh/README.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions plugins/k8saudit-ovh/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module github.com/falcosecurity/plugins/plugins/k8saudit-ovh

go 1.23.3

require (
github.com/falcosecurity/plugin-sdk-go v0.7.4
github.com/falcosecurity/plugins/plugins/k8saudit v0.11.0
github.com/gorilla/websocket v1.5.3
golang.org/x/net v0.32.0
)

require (
github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b // indirect
github.com/iancoleman/orderedmap v0.3.0 // indirect
github.com/valyala/fastjson v1.6.4 // indirect
)
37 changes: 37 additions & 0 deletions plugins/k8saudit-ovh/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b h1:doCpXjVwui6HUN+xgNsNS3SZ0/jUZ68Eb+mJRNOZfog=
github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/falcosecurity/plugin-sdk-go v0.7.4 h1:iNV0pgWgJwOHqSCjTw4Hsvtu5WuwoqckAWzpIEy9giQ=
github.com/falcosecurity/plugin-sdk-go v0.7.4/go.mod h1:NP+y22DYOS+G3GDXIXNmzf0CBL3nfPPMoQuHvAzfitQ=
github.com/falcosecurity/plugins/plugins/k8saudit v0.11.0 h1:ywwQ8kQmMS0HL3PuwBSKUmERqePrCSnajxnSCNC0HQY=
github.com/falcosecurity/plugins/plugins/k8saudit v0.11.0/go.mod h1:RmSc1za6asI52w3uVhZGb/p6RoQr2OWmp/Zc8+kiMWw=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
190 changes: 190 additions & 0 deletions plugins/k8saudit-ovh/pkg/k8sauditovh/k8sauditovh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package k8sauditovh

import (
"bytes"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"net/url"
"os"
"text/template"
"time"

"github.com/falcosecurity/plugin-sdk-go/pkg/sdk"
"github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins"
"github.com/falcosecurity/plugin-sdk-go/pkg/sdk/plugins/source"
"github.com/falcosecurity/plugins/plugins/k8saudit/pkg/k8saudit"

"github.com/gorilla/websocket"
)

var (
ID uint32
Name string
Description string
Contact string
Version string
EventSource string
)

const (
pluginName = "k8saudit-ovh"

// Time allowed to read the next pong message from the client.
pongWait = 60 * time.Second
)

type PluginConfig struct {
MaxEventSize uint64 `json:"maxEventSize" jsonschema:"title=Maximum event size,description=Maximum size of single audit event (Default: 262144),default=262144"`
}

// Plugin represents our plugin
type Plugin struct {
k8saudit.Plugin
Logger *log.Logger
Config PluginConfig
}

// Resets sets the configuration to its default values
func (k *PluginConfig) Reset() {
k.MaxEventSize = uint64(sdk.DefaultEvtSize)
}

// SetInfo is used to set the Info of the plugin
func (p *Plugin) SetInfo(id uint32, name, description, contact, version, eventSource string) {
ID = id
Name = name
Contact = contact
Version = version
EventSource = eventSource
}

// Info displays information of the plugin to Falco plugin framework
func (p *Plugin) Info() *plugins.Info {
return &plugins.Info{
ID: ID,
Name: Name,
Description: Description,
Contact: Contact,
Version: Version,
EventSource: EventSource,
}
}

// Init is called by the Falco plugin framework as first entry,
// we use it for setting default configuration values and mapping
// values from `init_config` (json format for this plugin)
func (p *Plugin) Init(config string) error {
p.Plugin.Config.Reset()
p.Config.Reset()
p.Logger = log.New(os.Stderr, "["+pluginName+"] ", log.LstdFlags|log.LUTC|log.Lmsgprefix)
return nil
}

func (p *Plugin) OpenParams() ([]sdk.OpenParam, error) {
return []sdk.OpenParam{
{Value: "", Desc: "The LDP Websocket URL to use to get the OVHcloud MKS Audit Logs sent to a LDP data stream"},
}, nil
}

// Open is called by Falco plugin framework for opening a stream of events, we call that an instance
func (p *Plugin) Open(ovhLDPURL string) (source.Instance, error) {
t, err := template.New("template").Funcs(template.FuncMap{
"color": color,
"bColor": bColor,
"noColor": func() string { return color("reset") },
"date": date,
"join": join,
"concat": concat,
"duration": duration,
"int": toInt,
"float": toFloat,
"string": toString,
"get": get,
"column": column,
"begin": begin,
"contain": contain,
"level": level,
}).Parse("{{._appID}}> {{.short_message}}")
if err != nil {
p.Logger.Fatalf("Failed to parse pattern: %s", err.Error())
}

if ovhLDPURL == "" {
return nil, fmt.Errorf("OVHcloud LDP URL can't be empty")
}

eventC := make(chan source.PushEvent)

go func() {
defer close(eventC)

u := url.URL{Scheme: "wss", Host: ovhLDPURL, Path: ""}
v, _ := url.QueryUnescape(u.String())

headers := make(http.Header)
// headers.Set("Origin", "http://mySelf")
wsChan, _, err := websocket.DefaultDialer.Dial(v, headers)
if err != nil {
eventC <- source.PushEvent{Err: err}
return
}
defer wsChan.Close()

for {
//wsChan.SetReadDeadline(time.Now().Add(5 * time.Second))
wsChan.SetReadDeadline(time.Now().Add(pongWait))
_, msg, err := wsChan.ReadMessage()

// Keep the WebSocket connection alive
if t, ok := err.(net.Error); ok && t.Timeout() {
// Timeout, send a Ping && continue
if err := wsChan.WriteMessage(websocket.PingMessage, nil); err != nil {
p.Logger.Println("The end host probably closed the connection", err.Error())
}
continue
}

if err != nil {
p.Logger.Printf("Error while reading from %q: %q. Will try to reconnect after 1s...\n", u.Host, err.Error())
time.Sleep(1 * time.Second)
break
}

// Extract Message
var logMessage struct {
Message string `json:"message"`
}
json.Unmarshal(msg, &logMessage)

// Extract infos
var message map[string]interface{}
json.Unmarshal([]byte(logMessage.Message), &message)

var m bytes.Buffer
err = t.Execute(&m, message)
if err != nil {
p.Logger.Println(err)
continue
}

// Parse audit events payload thanks to k8saudit extract parse and extract methods
values, err := p.Plugin.ParseAuditEventsPayload([]byte(m.String())[12:])
if err != nil {
p.Logger.Println(err)
continue
}
for _, j := range values {
if j.Err != nil {
p.Logger.Println(j.Err)
continue
}

eventC <- *j
}
}
}()
return source.NewPushInstance(eventC)
}
Loading
Loading