From fbd149c3d87bde6cfee749b1382052c80031660c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geyslan=20Greg=C3=B3rio?= Date: Thu, 5 Dec 2024 11:42:22 -0300 Subject: [PATCH] wip --- Makefile | 31 ++++- cmd/evt/cmd/helpers/helpers.go | 23 ++++ cmd/evt/cmd/root.go | 37 ++++++ cmd/evt/cmd/trigger/trigger.go | 232 +++++++++++++++++++++++++++++++++ cmd/evt/main.go | 24 ++++ 5 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 cmd/evt/cmd/helpers/helpers.go create mode 100644 cmd/evt/cmd/root.go create mode 100644 cmd/evt/cmd/trigger/trigger.go create mode 100644 cmd/evt/main.go diff --git a/Makefile b/Makefile index 715af62e6697..db7ce1992bde 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ .PHONY: all | env -all: tracee-ebpf tracee-rules signatures tracee +all: tracee-ebpf tracee-rules signatures tracee evt # # make @@ -627,6 +627,35 @@ clean-signatures: # other commands # +# evt + +EVT_SRC_DIRS = ./cmd/evt/ +EVT_SRC = $(shell find $(EVT_SRC_DIRS) \ + -type f \ + -name '*.go' \ + ! -name '*_test.go' \ + ) + +.PHONY: evt +evt: $(OUTPUT_DIR)/evt + +$(OUTPUT_DIR)/evt: \ + $(EVT_SRC) \ + $(OUTPUT_DIR)/tracee \ + | .eval_goenv \ + .checkver_$(CMD_GO) \ +# + $(GO_ENV_EBPF) $(CMD_GO) build \ + -ldflags="$(GO_DEBUG_FLAG) \ + " \ + -v -o $@ \ + ./cmd/evt + +.PHONY: clean-evt +clean-evt: +# + $(CMD_RM) -rf $(OUTPUT_DIR)/evt + # tracee-bench TRACEE_BENCH_SRC_DIRS = ./cmd/tracee-bench/ diff --git a/cmd/evt/cmd/helpers/helpers.go b/cmd/evt/cmd/helpers/helpers.go new file mode 100644 index 000000000000..91ceb6bd597a --- /dev/null +++ b/cmd/evt/cmd/helpers/helpers.go @@ -0,0 +1,23 @@ +package helpers + +import ( + "fmt" + "io" + "path/filepath" +) + +func GetFilterOutCommScope(cmd string) string { + comm := filepath.Base(cmd) + comm = comm[:min(len(comm), 15)] + return fmt.Sprintf("comm!=%s", comm) +} + +type PrefixWriter struct { + Prefix []byte + Writer io.Writer +} + +// Write writes the given bytes with the prefix +func (pw *PrefixWriter) Write(p []byte) (int, error) { + return pw.Writer.Write(append(pw.Prefix, p...)) +} diff --git a/cmd/evt/cmd/root.go b/cmd/evt/cmd/root.go new file mode 100644 index 000000000000..3cab601702ca --- /dev/null +++ b/cmd/evt/cmd/root.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "context" + "os" + + "github.com/spf13/cobra" + + "github.com/aquasecurity/tracee/cmd/evt/cmd/trigger" +) + +func init() { + rootCmd.AddCommand(trigger.Cmd()) +} + +var ( + rootCmd = &cobra.Command{ + Use: "evt", + Short: "An event testing tool", + Long: "evt is a simple testing tool that generates events to stress the system", + } +) + +func initRootCmd() error { + rootCmd.SetOutput(os.Stdout) + rootCmd.SetErr(os.Stderr) + + return nil +} + +func Execute(ctx context.Context) error { + if err := initRootCmd(); err != nil { + return err + } + + return rootCmd.ExecuteContext(ctx) +} diff --git a/cmd/evt/cmd/trigger/trigger.go b/cmd/evt/cmd/trigger/trigger.go new file mode 100644 index 000000000000..9ccaffb8b370 --- /dev/null +++ b/cmd/evt/cmd/trigger/trigger.go @@ -0,0 +1,232 @@ +package trigger + +import ( + "context" + "fmt" + "os" + "os/exec" + "os/signal" + "syscall" + "time" + + "github.com/spf13/cobra" + + "github.com/aquasecurity/tracee/cmd/evt/cmd/helpers" +) + +func init() { + triggerCmd.Flags().StringP( + "event", + "e", + "", + "...\t\tSelect event to trigger", + ) + if err := triggerCmd.MarkFlagRequired("event"); err != nil { + triggerCmd.PrintErrf("marking required flag: %v\n", err) + os.Exit(1) + } + + triggerCmd.Flags().Int32P( + "ops", + "o", + defaultTriggerOps, + "...\t\tNumber of operations to perform", + ) + + triggerCmd.Flags().DurationP( + "sleep", + "s", + defaultTriggerSleep, + "...\t\tSleep time between operations", + ) + + triggerCmd.Flags().BoolP( + "bypass-flags", + "f", + false, + "\t\t\tPrint tracee bypass flags", + ) + + triggerCmd.Flags().BoolP( + "wait-signal", + "w", + false, + "\t\t\tWait for start signal (SIGUSR1)", + ) +} + +const ( + defaultTriggerOps = int32(1) + defaultTriggerSleep = 10 * time.Nanosecond + triggerTimeout = 30 * time.Minute +) + +var ( + triggerCmd = &cobra.Command{ + Use: "trigger", + Aliases: []string{"t"}, + Short: "Trigger events to trigger", + RunE: triggerCmdRun, + SilenceErrors: true, + SilenceUsage: true, + } +) + +type trigger struct { + event string + ops int32 + sleep time.Duration + waitSignal bool + printBypassFlags bool + + ctx context.Context + cmd *cobra.Command +} + +func (t *trigger) Run() error { + t.setCmdOutErr() + + if t.printBypassFlags { + t.printTraceeBypassFlags() + os.Exit(0) + } + + err := t.waitForSignal() + if err != nil { + return err + } + + const layout = "15:04:05.999999999" + now := time.Now() + t.Printf("Starting triggering %d ops with %v sleep time at %v\n", t.ops, t.sleep, now.Format(layout)) + + for i := int32(0); i < t.ops; i++ { + select { + case <-t.ctx.Done(): + t.Printf("Stopping triggering: %v\n", t.ctx.Err()) + return t.ctx.Err() + default: + time.Sleep(t.sleep) + } + + exeCmd := exec.CommandContext(t.ctx, getTriggerPath(t.event)) + err := exeCmd.Run() + if err != nil { + return fmt.Errorf("failed to run command: %w", err) + } + } + + end := time.Now() + t.Printf("Finished triggering %d ops at %v after %v\n", t.ops, end.Format(layout), end.Sub(now).String()) + + return nil +} + +func (t *trigger) Printf(format string, args ...interface{}) { + t.cmd.Printf(format, args...) +} + +func getTrigger(cmd *cobra.Command) (*trigger, error) { + event, err := cmd.Flags().GetString("event") + if err != nil { + return nil, err + } + + ops, err := cmd.Flags().GetInt32("ops") + if err != nil { + return nil, err + } + if ops <= 0 { + return nil, fmt.Errorf("ops must be greater than 0") + } + + sleep, err := cmd.Flags().GetDuration("sleep") + if err != nil { + return nil, err + } + + bypassFlags, err := cmd.Flags().GetBool("bypass-flags") + if err != nil { + return nil, err + } + + waitSignal, err := cmd.Flags().GetBool("wait-signal") + if err != nil { + return nil, err + } + + return &trigger{ + event: event, + ops: ops, + sleep: sleep, + printBypassFlags: bypassFlags, + waitSignal: waitSignal, + cmd: cmd, + }, nil +} + +func (t *trigger) setCmdOutErr() { + if !t.waitSignal { + return + } + + prefix := []byte(fmt.Sprintf("[trigger:%d:%s] ", os.Getpid(), t.event)) + cmd := t.cmd + cmd.SetOut(&helpers.PrefixWriter{ + Prefix: prefix, + Writer: os.Stdout, + }) + cmd.SetErr(&helpers.PrefixWriter{ + Prefix: prefix, + Writer: os.Stderr, + }) +} + +func (t *trigger) waitForSignal() error { + if !t.waitSignal { + return nil + } + + startChan := make(chan os.Signal, 1) + signal.Notify(startChan, syscall.SIGUSR1) + t.Printf("Waiting for start signal\n") + + ctx := t.ctx + select { + case <-ctx.Done(): + return ctx.Err() + case <-startChan: + return nil + } +} + +func getTriggerPath(event string) string { + return fmt.Sprintf("./cmd/evt/cmd/trigger/triggers/%s.sh", event) +} + +func (t *trigger) printTraceeBypassFlags() { + self := helpers.GetFilterOutCommScope(os.Args[0]) + comm := helpers.GetFilterOutCommScope(fmt.Sprintf("%s.sh", t.event)) + t.cmd.Printf("Tracee bypass flags: -s %s -s %s\n", self, comm) +} + +func triggerCmdRun(cmd *cobra.Command, args []string) error { + t, err := getTrigger(cmd) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeoutCause( + t.cmd.Context(), + triggerTimeout, + fmt.Errorf("timeout after %v", triggerTimeout), + ) + defer cancel() + t.ctx = ctx + + return t.Run() +} + +func Cmd() *cobra.Command { + return triggerCmd +} diff --git a/cmd/evt/main.go b/cmd/evt/main.go new file mode 100644 index 000000000000..31607c1d2380 --- /dev/null +++ b/cmd/evt/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/aquasecurity/tracee/cmd/evt/cmd" +) + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + err := cmd.Execute(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + os.Exit(0) +}