Skip to content

Commit

Permalink
fix: Enhance Falco syscall events triggering and reliability
Browse files Browse the repository at this point in the history
- Clean up several events to prevent inode starvation and other issues during repeated runs
- Use random temp file/folder names to avoid overlaps and errors
- Add missing binaries and fix their calls to enable all 60+ events to trigger correctly
- Fix CombinedServerClient() and event naming issues to improve test reliability
- Add prerequisite checks for some events and improve error handling
- Add timeouts to long-running commands and remove unnecessary bash dependency
- Remove deprecated and redundant Falco rules
- Fix build issues on macOS and ensure tests continue even if one fails

Bonus:
- Clean up Makefile, disable CGO_ENABLED, and fix all linter errors
- Use specific base image tags in Dockerfile to ensure a clean vulnerability scan
- Update Go and dependencies to address known security issues
- Refactor for consistency and code clarity, convert CRLF to LF line endings

Signed-off-by: Predrag Rogic <prezha@users.noreply.github.com>
  • Loading branch information
prezha authored and poiana committed Sep 20, 2024
1 parent 7b0dab5 commit 93df6eb
Show file tree
Hide file tree
Showing 90 changed files with 1,594 additions and 1,031 deletions.
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="cncf-falco-dev@lists.cncf.io"

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

0 comments on commit 93df6eb

Please sign in to comment.