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

fix: Enhance Falco syscall events triggering and reliability #220

Merged
merged 5 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
14 changes: 6 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
FROM alpine:latest as builder
FROM golang:1.23.1-alpine3.20 AS builder

LABEL maintainer="[email protected]"

RUN apk add --no-cache make bash git build-base go
RUN apk add --no-cache make bash

WORKDIR /event-generator

COPY . .

RUN make

FROM alpine:latest

COPY --from=builder /event-generator/event-generator /bin/event-generator
FROM alpine:3.20

# Need to have this for helper.RunShell
RUN apk add bash
RUN apk add --no-cache sudo polkit libcap e2fsprogs-extra openssh nmap netcat-openbsd wget curl

# Need to have this for syscall.WriteBelowRpmDatabase
RUN mkdir -p /var/lib/rpm/
COPY --from=builder /event-generator/event-generator /bin/event-generator

ENTRYPOINT ["/bin/event-generator"]
12 changes: 5 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ prepare: clean events/k8saudit/yaml/bundle.go

.PHONY: ${output}
${output}:
$(GO) build -buildmode=pie -buildvcs=false -ldflags "$(LDFLAGS)" -o $@ ${main}
CGO_ENABLED=0 $(GO) build -buildmode=pie -buildvcs=false -ldflags "$(LDFLAGS)" -o $@ ${main}

.PHONY: clean
clean:
$(RM) -R ${output}
$(RM) -f events/k8saudit/yaml/bundle.go
$(RM) events/k8saudit/yaml/bundle.go
$(RM) -R ${output} ${docgen}

.PHONY: test
Expand All @@ -78,11 +78,9 @@ image:
$(DOCKER) build \
-t "$(IMAGE_NAME_BRANCH)" \
-f Dockerfile .
$(DOCKER) tag $(IMAGE_NAME_BRANCH) $(IMAGE_NAME_COMMIT)
$(DOCKER) tag "$(IMAGE_NAME_BRANCH)" $(IMAGE_NAME_COMMIT)

$(DOCKER) tag "$(IMAGE_NAME_BRANCH)" "$(IMAGE_NAME_COMMIT)"

.PHONY: push
push:
$(DOCKER) push $(IMAGE_NAME_BRANCH)
$(DOCKER) push $(IMAGE_NAME_COMMIT)
$(DOCKER) push "$(IMAGE_NAME_BRANCH)"
$(DOCKER) push "$(IMAGE_NAME_COMMIT)"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ INFO action executed action=syscall.ReadSensitiveF

Useful options:
- `--loop` to run actions in a loop
- `--sleep` to set the length of time to wait before running an action (default to `1s`)
- `--sleep` to set the length of time to wait before running an action (default to `100ms`)

Also, note that not all actions are enabled by default. To run all actions, use the `--all` option.

Expand Down Expand Up @@ -145,7 +145,7 @@ The `syscall` collection performs a variety of suspect actions detected by the [
$ docker run -it --rm falcosecurity/event-generator run syscall --loop
```

The above command loops forever, incessantly generating a sample event each second.
The above command loops forever, incessantly generating a sample event every 100 miliseconds.


### Generate activity for the k8s audit rules
Expand Down
6 changes: 3 additions & 3 deletions cmd/bench.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ func NewBench() *cobra.Command {
This command generates a high number of Event Per Second (EPS), to test the events throughput allowed by Falco.
The number of EPS is controlled by the "--sleep" option: reduce the sleeping duration to increase the EPS.
If the "--loop" option is set, the sleeping duration is halved on each round.
The "--pid" option can be used to monitor the Falco process.
The "--pid" option can be used to monitor the Falco process.

N.B.:
- the Falco gRPC Output must be enabled to use this command
- "outputs.rate" and "outputs.max_burst" values within the Falco configuration must be increased,
otherwise EPS will be rate-limited by the throttling mechanism
- since not all actions can be used for benchmarking,
- since not all actions can be used for benchmarking,
only those actions matching the given regular expression are used

One commmon way to use this command is as following:

event-generator bench "ChangeThreadNamespace|ReadSensitiveFileUntrusted" --all --loop --sleep 10ms --pid $(pidof -s falco)
event-generator bench "ChangeThreadNamespace|ReadSensitiveFileUntrusted" --all --loop --sleep 10ms --pid $(pidof -s falco)


` + runWarningMessage
Expand Down
8 changes: 4 additions & 4 deletions cmd/config_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ limitations under the License.
package cmd

import (
"fmt"
"errors"

"github.com/creasty/defaults"
"github.com/falcosecurity/event-generator/cmd/internal/validate"
Expand All @@ -42,11 +42,11 @@ func NewConfigOptions() *ConfigOptions {
// Validate validates the ConfigOptions fields.
func (co *ConfigOptions) Validate() []error {
if err := validate.V.Struct(co); err != nil {
errors := err.(validator.ValidationErrors)
errs := err.(validator.ValidationErrors)
errArr := []error{}
for _, e := range errors {
for _, e := range errs {
// Translate each error one at a time
errArr = append(errArr, fmt.Errorf(e.Translate(validate.T)))
errArr = append(errArr, errors.New(e.Translate(validate.T)))
}
return errArr
}
Expand Down
5 changes: 1 addition & 4 deletions cmd/internal/validate/isfilepath.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ func isFilePath(fl validator.FieldLevel) bool {
case reflect.String:
fileInfo, err := os.Stat(field.String())
if err != nil {
if !os.IsNotExist(err) {
return false
}
return true
return os.IsNotExist(err)
}

return !fileInfo.IsDir()
Expand Down
5 changes: 1 addition & 4 deletions cmd/internal/validate/islogruslevel.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,5 @@ import (
func isLogrusLevel(fl validator.FieldLevel) bool {
level := fl.Field().String()
_, err := logger.ParseLevel(level)
if err != nil {
return false
}
return true
return err == nil
}
32 changes: 23 additions & 9 deletions cmd/internal/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,24 @@ func init() {
return name
})

V.RegisterValidation("filepath", isFilePath)
V.RegisterValidation("logrus", isLogrusLevel)
if err := V.RegisterValidation("filepath", isFilePath); err != nil {
panic(err)
}

if err := V.RegisterValidation("logrus", isLogrusLevel); err != nil {
panic(err)
}

V.RegisterAlias("format", "eq=text|eq=json")

eng := en.New()
uni := ut.New(eng, eng)
T, _ = uni.GetTranslator("en")
en_translations.RegisterDefaultTranslations(V, T)
if err := en_translations.RegisterDefaultTranslations(V, T); err != nil {
panic(err)
}

V.RegisterTranslation(
if err := V.RegisterTranslation(
"filepath",
T,
func(ut ut.Translator) error {
Expand All @@ -63,9 +71,11 @@ func init() {
t, _ := ut.T("filepath", fe.Field())
return t
},
)
); err != nil {
panic(err)
}

V.RegisterTranslation(
if err := V.RegisterTranslation(
"logrus",
T,
func(ut ut.Translator) error {
Expand All @@ -75,9 +85,11 @@ func init() {
t, _ := ut.T("logrus", fe.Value().(string))
return t
},
)
); err != nil {
panic(err)
}

V.RegisterTranslation(
if err := V.RegisterTranslation(
"format",
T,
func(ut ut.Translator) error {
Expand All @@ -87,5 +99,7 @@ func init() {
t, _ := ut.T("format", fe.Value().(string))
return t
},
)
); err != nil {
panic(err)
}
}
13 changes: 10 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ func New(configOptions *ConfigOptions) *cobra.Command {
debugFlags(flags)
},
Run: func(c *cobra.Command, args []string) {
c.Help()
if err := c.Help(); err != nil {
logger.WithError(err).Fatal("error running help")
}
},
}

Expand Down Expand Up @@ -202,14 +204,19 @@ func initConfig(configFile string) {
// - config file (e.g. ~/.falco-event-generator.yaml)
// - its default
func initFlags(flags *pflag.FlagSet, exclude map[string]bool) {
viper.BindPFlags(flags)
if err := viper.BindPFlags(flags); err != nil {
logger.WithError(err).Fatal("error binding flags to configuration")
}

flags.VisitAll(func(f *pflag.Flag) {
if exclude[f.Name] {
return
}
viper.SetDefault(f.Name, f.DefValue)
if v := viper.GetString(f.Name); v != f.DefValue {
flags.Set(f.Name, v)
if err := flags.Set(f.Name, v); err != nil {
logger.WithError(err).WithField("flag", f.Name).Fatal("error setting flag")
}
}
})
}
Expand Down
8 changes: 6 additions & 2 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,27 @@ Without arguments it runs all actions, otherwise only those actions matching the

ns := flags.Lookup("namespace")
ns.DefValue = DefaultNamespace
ns.Value.Set(DefaultNamespace)
if err := ns.Value.Set(DefaultNamespace); err != nil {
panic(err)
}

return c, func(c *cobra.Command, args []string, options ...runner.Option) error {

flags := c.Flags()
ns, err := flags.GetString("namespace")
if err != nil {
return err
}

sleep, err := flags.GetDuration("sleep")
if err != nil {
return err
}

loop, err := flags.GetBool("loop")
if err != nil {
return err
}

all, err := flags.GetBool("all")
if err != nil {
return err
Expand Down
2 changes: 0 additions & 2 deletions cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ limitations under the License.
package cmd

import (

// register event collections
"time"

"github.com/falcosecurity/event-generator/pkg/runner"
Expand Down
2 changes: 1 addition & 1 deletion events/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ For this reason, *actions* should revert any operation that changed the state of

```golang
func WriteBelowEtc(h events.Helper) error {
const filename = "/etc/created-by-event-generator"
const filename = "/etc/falco-event-generator"
h.Log().Infof("writing to %s", filename)
defer os.Remove(filename) // clean up here!!!
return os.WriteFile(filename, nil, os.FileMode(0755))
Expand Down
63 changes: 39 additions & 24 deletions events/helper/combined_server_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ limitations under the License.
package helper

import (
"bytes"
"errors"
"net"
"time"

Expand All @@ -24,48 +26,61 @@ import (
var _ = events.Register(CombinedServerClient)

func CombinedServerClient(h events.Helper) error {
errCh := make(chan error)
go func() {
errCh <- runServer()
}()

time.Sleep(1 * time.Second)
return runClient()
}

func runServer() error {
serverAddr, err := net.ResolveUDPAddr("udp", ":80")
serverAddr, err := net.ResolveUDPAddr("udp", "localhost:1234")
if err != nil {
return err
}

// start server
serverConn, err := net.ListenUDP("udp", serverAddr)
if err != nil {
return err
}
defer func() {
if err := serverConn.Close(); err != nil {
h.Log().WithError(err).Error("failed to close server connection")
}
}()

h.Log().Debug("server is listening on localhost:1234")

defer serverConn.Close()
buf := make([]byte, 1024)
_, _, err = serverConn.ReadFromUDP(buf)
return err
}

func runClient() error {
serverAddr, err := net.ResolveUDPAddr("udp", "localhost:80")
if err != nil {
return err
}
// wait for client to send data
srvErr := make(chan error)
go func() {
defer close(srvErr)
_, _, err = serverConn.ReadFromUDP(buf)
srvErr <- err
}()

// connect to server and send data
clientConn, err := net.DialUDP("udp", nil, serverAddr)
if err != nil {
return err
}
defer clientConn.Close()
defer func() {
if err := clientConn.Close(); err != nil {
h.Log().WithError(err).Error("failed to close client connection")
}
}()

data := []byte{0xCA, 0xFE, 0xBA, 0xBE}
_, err = clientConn.Write(data)
if err != nil {
if _, err = clientConn.Write(data); err != nil {
return err
}
return nil

h.Log().Debugf("client sent: %X", data)

// wait for server to respond or timeout
select {
case err := <-srvErr:
if err != nil {
return err
}
h.Log().Debugf("server received: %X", bytes.Trim(buf, "\x00"))
return nil
case <-time.After(5 * time.Second):
return errors.New("timeout")
}
}
Loading
Loading