From decbdbacdcec46323ebde7d2d7ee5e6ab6d65ae4 Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Mon, 30 Dec 2024 08:13:56 +0000 Subject: [PATCH 01/18] added simple implantation of server flag --- cmd/tracee-ebpf/main.go | 53 ++++++------- cmd/tracee-rules/main.go | 50 ++++++------ cmd/tracee/cmd/root.go | 60 ++------------- pkg/cmd/cobra/cobra.go | 15 +--- pkg/cmd/cobra/config.go | 46 +++++++++++ pkg/cmd/cobra/config_test.go | 134 +++++++++++++++++++++++++++++++++ pkg/cmd/flags/server/server.go | 122 ++++++++++++++++++++++-------- pkg/cmd/urfave/urfave.go | 19 ++--- 8 files changed, 342 insertions(+), 157 deletions(-) diff --git a/cmd/tracee-ebpf/main.go b/cmd/tracee-ebpf/main.go index 69919130be73..d8de1eb07a60 100644 --- a/cmd/tracee-ebpf/main.go +++ b/cmd/tracee-ebpf/main.go @@ -10,7 +10,8 @@ import ( "github.com/aquasecurity/tracee/pkg/cmd" "github.com/aquasecurity/tracee/pkg/cmd/flags" - "github.com/aquasecurity/tracee/pkg/cmd/flags/server" + + // "github.com/aquasecurity/tracee/pkg/cmd/flags/server" "github.com/aquasecurity/tracee/pkg/cmd/initialize" "github.com/aquasecurity/tracee/pkg/cmd/urfave" "github.com/aquasecurity/tracee/pkg/logger" @@ -128,31 +129,31 @@ func main() { Value: "/tmp/tracee", Usage: "path where tracee will install or lookup it's resources", }, - &cli.BoolFlag{ - Name: server.MetricsEndpointFlag, - Usage: "enable metrics endpoint", - Value: false, - }, - &cli.BoolFlag{ - Name: server.HealthzEndpointFlag, - Usage: "enable healthz endpoint", - Value: false, - }, - &cli.BoolFlag{ - Name: server.PProfEndpointFlag, - Usage: "enable pprof endpoints", - Value: false, - }, - &cli.BoolFlag{ - Name: server.PyroscopeAgentFlag, - Usage: "enable pyroscope agent", - Value: false, - }, - &cli.StringFlag{ - Name: server.HTTPListenEndpointFlag, - Usage: "listening address of the metrics endpoint server", - Value: ":3366", - }, + // &cli.BoolFlag{ + // Name: server.MetricsEndpointFlag, + // Usage: "enable metrics endpoint", + // Value: false, + // }, + // &cli.BoolFlag{ + // Name: server.HealthzEndpointFlag, + // Usage: "enable healthz endpoint", + // Value: false, + // }, + // &cli.BoolFlag{ + // Name: server.PProfEndpointFlag, + // Usage: "enable pprof endpoints", + // Value: false, + // }, + // &cli.BoolFlag{ + // Name: server.PyroscopeAgentFlag, + // Usage: "enable pyroscope agent", + // Value: false, + // }, + // &cli.StringFlag{ + // Name: server.HTTPListenEndpointFlag, + // Usage: "listening address of the metrics endpoint server", + // Value: ":3366", + // }, &cli.BoolFlag{ Name: "no-containers", Usage: "disable container info enrichment to events. safeguard option.", diff --git a/cmd/tracee-rules/main.go b/cmd/tracee-rules/main.go index 2ca9028c63a1..d3eb53ab6964 100644 --- a/cmd/tracee-rules/main.go +++ b/cmd/tracee-rules/main.go @@ -139,13 +139,13 @@ func main() { return fmt.Errorf("constructing engine: %w", err) } - httpServer, err := server.PrepareHTTPServer( - c.String(server.HTTPListenEndpointFlag), - c.Bool(server.MetricsEndpointFlag), - c.Bool(server.HealthzEndpointFlag), - c.Bool(server.PProfEndpointFlag), - c.Bool(server.PyroscopeAgentFlag), - ) + // httpServer, err := server.PrepareHTTPServer( + // c.String(server.HTTPListenEndpointFlag), + // c.Bool(server.MetricsEndpointFlag), + // c.Bool(server.HealthzEndpointFlag), + // c.Bool(server.PProfEndpointFlag), + // c.Bool(server.PyroscopeAgentFlag), + // ) if err != nil { return err } @@ -158,9 +158,9 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() - if httpServer != nil { - go httpServer.Start(ctx) - } + // if httpServer != nil { + // go httpServer.Start(ctx) + // } e.Start(ctx) @@ -218,21 +218,21 @@ func main() { Usage: "size of the event channel's buffer consumed by signatures", Value: 1000, }, - &cli.BoolFlag{ - Name: server.MetricsEndpointFlag, - Usage: "enable metrics endpoint", - Value: false, - }, - &cli.BoolFlag{ - Name: server.HealthzEndpointFlag, - Usage: "enable healthz endpoint", - Value: false, - }, - &cli.StringFlag{ - Name: server.HTTPListenEndpointFlag, - Usage: "listening address of the metrics endpoint server", - Value: ":4466", - }, + // &cli.BoolFlag{ + // Name: server.MetricsEndpointFlag, + // Usage: "enable metrics endpoint", + // Value: false, + // }, + // &cli.BoolFlag{ + // Name: server.HealthzEndpointFlag, + // Usage: "enable healthz endpoint", + // Value: false, + // }, + // &cli.StringFlag{ + // Name: server.HTTPListenEndpointFlag, + // Usage: "listening address of the metrics endpoint server", + // Value: ":4466", + // }, &cli.BoolFlag{ Name: "allcaps", Value: false, diff --git a/cmd/tracee/cmd/root.go b/cmd/tracee/cmd/root.go index 71c384524c02..822ae7b37e4b 100644 --- a/cmd/tracee/cmd/root.go +++ b/cmd/tracee/cmd/root.go @@ -12,7 +12,6 @@ import ( "github.com/spf13/viper" cmdcobra "github.com/aquasecurity/tracee/pkg/cmd/cobra" - "github.com/aquasecurity/tracee/pkg/cmd/flags/server" "github.com/aquasecurity/tracee/pkg/cmd/initialize" "github.com/aquasecurity/tracee/pkg/errfmt" "github.com/aquasecurity/tracee/pkg/logger" @@ -250,62 +249,13 @@ func initCmd() error { // Server flags - rootCmd.Flags().Bool( - server.MetricsEndpointFlag, - false, - "\t\t\t\t\tEnable metrics endpoint", - ) - err = viper.BindPFlag(server.MetricsEndpointFlag, rootCmd.Flags().Lookup(server.MetricsEndpointFlag)) - if err != nil { - return errfmt.WrapError(err) - } - - rootCmd.Flags().Bool( - server.HealthzEndpointFlag, - false, - "\t\t\t\t\tEnable healthz endpoint", - ) - err = viper.BindPFlag(server.HealthzEndpointFlag, rootCmd.Flags().Lookup(server.HealthzEndpointFlag)) - if err != nil { - return errfmt.WrapError(err) - } - - rootCmd.Flags().Bool( - server.PProfEndpointFlag, - false, - "\t\t\t\t\tEnable pprof endpoints", - ) - err = viper.BindPFlag(server.PProfEndpointFlag, rootCmd.Flags().Lookup(server.PProfEndpointFlag)) - if err != nil { - return errfmt.WrapError(err) - } - - rootCmd.Flags().Bool( - server.PyroscopeAgentFlag, - false, - "\t\t\t\t\tEnable pyroscope agent", - ) - err = viper.BindPFlag(server.PyroscopeAgentFlag, rootCmd.Flags().Lookup(server.PyroscopeAgentFlag)) - if err != nil { - return errfmt.WrapError(err) - } - - rootCmd.Flags().String( - server.HTTPListenEndpointFlag, - ":3366", - "\t\t\t\tListening address of the metrics endpoint server", + rootCmd.Flags().StringArray( + "server", + []string{""}, + "Server configuration options (e.g., --server http.metrics=true)", //add help comments ) - err = viper.BindPFlag(server.HTTPListenEndpointFlag, rootCmd.Flags().Lookup(server.HTTPListenEndpointFlag)) - if err != nil { - return errfmt.WrapError(err) - } - rootCmd.Flags().String( - server.GRPCListenEndpointFlag, - "", // disabled by default - "\t\t\tListening address of the grpc server eg: tcp:4466, unix:/tmp/tracee.sock (default: disabled)", - ) - err = viper.BindPFlag(server.GRPCListenEndpointFlag, rootCmd.Flags().Lookup(server.GRPCListenEndpointFlag)) + err = viper.BindPFlag("server", rootCmd.Flags().Lookup("server")) if err != nil { return errfmt.WrapError(err) } diff --git a/pkg/cmd/cobra/cobra.go b/pkg/cmd/cobra/cobra.go index 1c05c9907760..d75b2553ddbe 100644 --- a/pkg/cmd/cobra/cobra.go +++ b/pkg/cmd/cobra/cobra.go @@ -292,24 +292,17 @@ func GetTraceeRunner(c *cobra.Command, version string) (cmd.Runner, error) { // Prepare the server - httpServer, err := server.PrepareHTTPServer( - viper.GetString(server.HTTPListenEndpointFlag), - viper.GetBool(server.MetricsEndpointFlag), - viper.GetBool(server.HealthzEndpointFlag), - viper.GetBool(server.PProfEndpointFlag), - viper.GetBool(server.PyroscopeAgentFlag), - ) + serverFlag, err := GetFlagsFromViper("server") if err != nil { return runner, err } - - grpcServer, err := flags.PrepareGRPCServer(viper.GetString(server.GRPCListenEndpointFlag)) + server, err := server.PrepareServer(serverFlag) if err != nil { return runner, err } - runner.HTTPServer = httpServer - runner.GRPCServer = grpcServer + runner.HTTPServer = server.HTTPServer + runner.GRPCServer = server.GRPCServer runner.TraceeConfig = cfg runner.Printer = p runner.InstallPath = traceeInstallPath diff --git a/pkg/cmd/cobra/config.go b/pkg/cmd/cobra/config.go index c344290d5d78..e9a593888435 100644 --- a/pkg/cmd/cobra/config.go +++ b/pkg/cmd/cobra/config.go @@ -22,6 +22,8 @@ func GetFlagsFromViper(key string) ([]string, error) { rawValue := viper.Get(key) switch key { + case "server": + flagger = &ServerConfig{} case "cache": flagger = &CacheConfig{} case "proctree": @@ -122,6 +124,50 @@ func getCRIConfigFlags(rawValue interface{}) ([]string, error) { return flags, nil } +// +// server flag +// + +type ServerConfig struct { + Http HttpConfig `mapstructure:"http"` + Grpc GrpcConfig `mapstructure:"grpc"` +} +type HttpConfig struct { + Metrics bool `mapstructure:"metrics"` + Pprof bool `mapstructure:"pprof"` + Healthz bool `mapstructure:"healthz"` + Pyroscope bool `mapstructure:"pyroscope"` + Address string `mapstructure:"address"` +} + +type GrpcConfig struct { + Address string `mapstructure:"address"` +} + +func (s *ServerConfig) flags() []string { + flags := make([]string, 0) + + if s.Grpc.Address != "" { + flags = append(flags, fmt.Sprintf("grpc.address=%s", s.Grpc.Address)) + } + if s.Http.Address != "" { + flags = append(flags, fmt.Sprintf("http.address=%s", s.Http.Address)) + } + if s.Http.Metrics { + flags = append(flags, "http.metrics=true") + } + if s.Http.Pprof { + flags = append(flags, "http.pprof=true") + } + if s.Http.Healthz { + flags = append(flags, "http.healthz=true") + } + if s.Http.Pyroscope { + flags = append(flags, "http.pyroscope=true") + } + return flags +} + // // config flag // diff --git a/pkg/cmd/cobra/config_test.go b/pkg/cmd/cobra/config_test.go index 0307f11074a9..88e912e435b3 100644 --- a/pkg/cmd/cobra/config_test.go +++ b/pkg/cmd/cobra/config_test.go @@ -1,6 +1,7 @@ package cobra import ( + "fmt" "log" "sort" "strings" @@ -352,6 +353,26 @@ output: "webhook:http://localhost:9000?timeout=3s&gotemplate=/path/to/template2&contentType=application/ld+json", }, }, + { + name: "server flag check", + yamlContent: ` +server: + http: + address: localhost:8080 + metrics: false + pprof: false + healthz: true + pyroscope: true + grpc: + address: unix:/var/run/tracee.sock`, + key: "server", + expectedFlags: []string{ + "grpc.address=unix:/var/run/tracee.sock", + "http.address=localhost:8080", + "http.healthz=true", + "http.pyroscope=true", + }, + }, } for _, tt := range tests { @@ -1032,6 +1053,119 @@ func TestOutputConfigFlags(t *testing.T) { }) } } +func TestServerConfigFlags(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + config ServerConfig + expected []string + }{ + { + name: "empty config", + config: ServerConfig{ + Http: HttpConfig{}, + Grpc: GrpcConfig{}, + }, + expected: []string{}, + }, + { + name: "grpc only", + config: ServerConfig{ + Http: HttpConfig{}, + Grpc: GrpcConfig{ + Address: "unix:/var/run/tracee.sock", + }, + }, + expected: []string{ + "grpc.address=unix:/var/run/tracee.sock", + }, + }, + { + name: "http only", + config: ServerConfig{ + Http: HttpConfig{ + Address: "localhost:8080", + }, + Grpc: GrpcConfig{}, + }, + expected: []string{ + "http.address=localhost:8080", + }, + }, + { + name: "http with options", + config: ServerConfig{ + Http: HttpConfig{ + Address: "localhost:8080", + Metrics: true, + Pprof: true, + Healthz: true, + Pyroscope: true, + }, + Grpc: GrpcConfig{}, + }, + expected: []string{ + "http.address=localhost:8080", + "http.metrics=true", + "http.pprof=true", + "http.healthz=true", + "http.pyroscope=true", + }, + }, + { + name: "both http and grpc", + config: ServerConfig{ + Http: HttpConfig{ + Address: "localhost:8080", + }, + Grpc: GrpcConfig{ + Address: "unix:/var/run/tracee.sock", + }, + }, + expected: []string{ + "grpc.address=unix:/var/run/tracee.sock", + "http.address=localhost:8080", + }, + }, + { + name: "both http and grpc with options", + config: ServerConfig{ + Http: HttpConfig{ + Address: "localhost:8080", + Metrics: true, + Pprof: true, + Healthz: true, + Pyroscope: true, + }, + Grpc: GrpcConfig{ + Address: "unix:/var/run/tracee.sock", + }, + }, + expected: []string{ + "grpc.address=unix:/var/run/tracee.sock", + "http.address=localhost:8080", + "http.metrics=true", + "http.pprof=true", + "http.healthz=true", + "http.pyroscope=true", + }, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + got := tt.config.flags() + if !slicesEqualIgnoreOrder(got, tt.expected) { + t.Errorf("flags() = %v, want %v", got, tt.expected) + } else { + fmt.Println(got) + } + }) + } +} // slicesEqualIgnoreOrder compares two string slices, ignoring order func slicesEqualIgnoreOrder(a, b []string) bool { diff --git a/pkg/cmd/flags/server/server.go b/pkg/cmd/flags/server/server.go index 7b5400fe3898..411d9eea19f2 100644 --- a/pkg/cmd/flags/server/server.go +++ b/pkg/cmd/flags/server/server.go @@ -1,18 +1,22 @@ package server import ( + "os" + "strings" + "github.com/aquasecurity/tracee/pkg/errfmt" - "github.com/aquasecurity/tracee/pkg/logger" + "github.com/aquasecurity/tracee/pkg/server/grpc" "github.com/aquasecurity/tracee/pkg/server/http" ) const ( - MetricsEndpointFlag = "metrics" - HealthzEndpointFlag = "healthz" - PProfEndpointFlag = "pprof" - HTTPListenEndpointFlag = "http-listen-addr" - GRPCListenEndpointFlag = "grpc-listen-addr" - PyroscopeAgentFlag = "pyroscope" + HTTPServer = "http" + GRPCServer = "grpc" + MetricsEndpointFlag = "metrics" + HealthzEndpointFlag = "healthz" + PProfEndpointFlag = "pprof" + ListenEndpointFlag = "address" + PyroscopeAgentFlag = "pyroscope" ) // TODO: this should be extract to be under 'pkg/cmd/flags' once we remove the binary tracee-rules. @@ -21,38 +25,94 @@ const ( // 'pkf/cmd/flags' directly libbpfgo becomes a dependency and we need to compile it with // tracee-rules. -func PrepareHTTPServer(listenAddr string, metrics, healthz, pprof, pyro bool) (*http.Server, error) { - if len(listenAddr) == 0 { - return nil, errfmt.Errorf("http listen address cannot be empty") - } +type Server struct { + HTTPServer *http.Server + GRPCServer *grpc.Server +} - if metrics || healthz || pprof { - httpServer := http.New(listenAddr) +func PrepareServer(serverSlice []string) (*Server, error) { + var err error + var server *Server + var ( + enableMetrics = false + enableHealthz = false + enablePProf = false + enablePyroscope = false + ) + grpcAddr := "" + for _, endpoint := range serverSlice { + if strings.Contains(endpoint, HTTPServer) { + if strings.Contains(endpoint, MetricsEndpointFlag) { + if strings.Contains(endpoint, "true") { + enableMetrics = true + } + } else if strings.Contains(endpoint, HealthzEndpointFlag) { + if strings.Contains(endpoint, "true") { + enableHealthz = true + } + } else if strings.Contains(endpoint, PProfEndpointFlag) { + if strings.Contains(endpoint, "true") { + enablePProf = true + } + } else if strings.Contains(endpoint, PyroscopeAgentFlag) { + if strings.Contains(endpoint, "true") { + enablePyroscope = true + } + } else if strings.Contains(endpoint, ListenEndpointFlag) { + server.HTTPServer = http.New(endpoint[len(HTTPServer)+1+len(ListenEndpointFlag):]) + } else { + server.HTTPServer = http.New("") + } + } else if strings.Contains(endpoint, GRPCServer) { + if strings.Contains(endpoint, ListenEndpointFlag) { + grpcAddr = endpoint[len(GRPCServer)+1+len(ListenEndpointFlag):] - if metrics { - logger.Debugw("Enabling metrics endpoint") - httpServer.EnableMetricsEndpoint() - } + addrParts := strings.SplitN(grpcAddr, ":", 2) + protocol := addrParts[0] - if healthz { - logger.Debugw("Enabling healthz endpoint") - httpServer.EnableHealthzEndpoint() - } + if protocol != "tcp" && protocol != "unix" { + return nil, errfmt.Errorf("grpc supported protocols are tcp or unix. eg: tcp:4466, unix:/tmp/tracee.sock") + } + + if len(addrParts) == 1 { + if protocol == "tcp" { + grpcAddr += ":4466" + } else { // protocol == "unix" + grpcAddr += ":/var/run/tracee.sock" + } + } + // cleanup listen address if needed (unix socket), for example if a panic happened + if protocol == "unix" { + path := strings.SplitN(grpcAddr, ":", 2)[1] + if _, err = os.Stat(path); err == nil { + err = os.Remove(path) + if err != nil { + return nil, errfmt.Errorf("failed to cleanup gRPC listening address (%s): %v", path, err) + } + } + } + server.GRPCServer, err = grpc.New("dsa", "dsa") //protocol, grpcAddr) - if pprof { - logger.Debugw("Enabling pprof endpoint") - httpServer.EnablePProfEndpoint() + } + } + } + if server.HTTPServer != nil { + if enableMetrics { + server.HTTPServer.EnableMetricsEndpoint() } - if pyro { - logger.Debugw("Enabling pyroscope agent") - err := httpServer.EnablePyroAgent() + if enableHealthz { + server.HTTPServer.EnableHealthzEndpoint() + } + if enablePProf { + server.HTTPServer.EnablePProfEndpoint() + } + if enablePyroscope { + err = server.HTTPServer.EnablePyroAgent() if err != nil { - return httpServer, err + return nil, err } } - - return httpServer, nil } - return nil, nil + return server, nil } diff --git a/pkg/cmd/urfave/urfave.go b/pkg/cmd/urfave/urfave.go index 6dcf0b2a1920..4f55f7fa31b8 100644 --- a/pkg/cmd/urfave/urfave.go +++ b/pkg/cmd/urfave/urfave.go @@ -5,7 +5,8 @@ import ( "github.com/aquasecurity/tracee/pkg/cmd" "github.com/aquasecurity/tracee/pkg/cmd/flags" - "github.com/aquasecurity/tracee/pkg/cmd/flags/server" + + // "github.com/aquasecurity/tracee/pkg/cmd/flags/server" "github.com/aquasecurity/tracee/pkg/cmd/initialize" "github.com/aquasecurity/tracee/pkg/cmd/printer" "github.com/aquasecurity/tracee/pkg/config" @@ -187,19 +188,19 @@ func GetTraceeRunner(c *cli.Context, version string) (cmd.Runner, error) { return runner, errfmt.Errorf("failed preparing BPF object: %v", err) } - httpServer, err := server.PrepareHTTPServer( - c.String(server.HTTPListenEndpointFlag), - c.Bool(server.MetricsEndpointFlag), - c.Bool(server.HealthzEndpointFlag), - c.Bool(server.PProfEndpointFlag), - c.Bool(server.PyroscopeAgentFlag), - ) + // httpServer, err := server.PrepareHTTPServer( + // c.String(server.HTTPListenEndpointFlag), + // c.Bool(server.MetricsEndpointFlag), + // c.Bool(server.HealthzEndpointFlag), + // c.Bool(server.PProfEndpointFlag), + // c.Bool(server.PyroscopeAgentFlag), + // ) if err != nil { return runner, err } - runner.HTTPServer = httpServer + runner.HTTPServer = nil runner.TraceeConfig = cfg runner.Printer = broadcast runner.InstallPath = traceeInstallPath From 2398bed7beff27d529fc267f4a22dfe9617ff887 Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Thu, 2 Jan 2025 15:33:52 +0000 Subject: [PATCH 02/18] enhanced prepareServer for better readabilty --- pkg/cmd/flags/server/server.go | 103 +++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/pkg/cmd/flags/server/server.go b/pkg/cmd/flags/server/server.go index 411d9eea19f2..c771e1075b9f 100644 --- a/pkg/cmd/flags/server/server.go +++ b/pkg/cmd/flags/server/server.go @@ -10,13 +10,13 @@ import ( ) const ( - HTTPServer = "http" - GRPCServer = "grpc" - MetricsEndpointFlag = "metrics" - HealthzEndpointFlag = "healthz" - PProfEndpointFlag = "pprof" - ListenEndpointFlag = "address" - PyroscopeAgentFlag = "pyroscope" + HTTPServer = "http" + GRPCServer = "grpc" + MetricsEndpointFlag = "metrics" + HealthzEndpointFlag = "healthz" + PProfEndpointFlag = "pprof" + ListenEndpointFlag = "address" + PyroscopeAgentEndpointFlag = "pyroscope" ) // TODO: this should be extract to be under 'pkg/cmd/flags' once we remove the binary tracee-rules. @@ -39,60 +39,75 @@ func PrepareServer(serverSlice []string) (*Server, error) { enablePProf = false enablePyroscope = false ) - grpcAddr := "" for _, endpoint := range serverSlice { - if strings.Contains(endpoint, HTTPServer) { - if strings.Contains(endpoint, MetricsEndpointFlag) { - if strings.Contains(endpoint, "true") { + // split flag http.address or grpc.address for example + serverParts := strings.SplitN(endpoint, ".", 2) + switch serverParts[0] { + //flag http.Xxx + case HTTPServer: + httpParts := strings.SplitN(serverParts[1], "=", 1) + switch httpParts[0] { + case ListenEndpointFlag: + server.HTTPServer = http.New(httpParts[1]) + case MetricsEndpointFlag: + if strings.Compare(httpParts[1], "true") == 0 { enableMetrics = true } - } else if strings.Contains(endpoint, HealthzEndpointFlag) { - if strings.Contains(endpoint, "true") { + case HealthzEndpointFlag: + if strings.Compare(httpParts[1], "true") == 0 { enableHealthz = true } - } else if strings.Contains(endpoint, PProfEndpointFlag) { - if strings.Contains(endpoint, "true") { + case PProfEndpointFlag: + if strings.Compare(httpParts[1], "true") == 0 { enablePProf = true } - } else if strings.Contains(endpoint, PyroscopeAgentFlag) { - if strings.Contains(endpoint, "true") { + case PyroscopeAgentEndpointFlag: + if strings.Compare(endpoint, "true") == 0 { enablePyroscope = true } - } else if strings.Contains(endpoint, ListenEndpointFlag) { - server.HTTPServer = http.New(endpoint[len(HTTPServer)+1+len(ListenEndpointFlag):]) - } else { + default: server.HTTPServer = http.New("") } - } else if strings.Contains(endpoint, GRPCServer) { - if strings.Contains(endpoint, ListenEndpointFlag) { - grpcAddr = endpoint[len(GRPCServer)+1+len(ListenEndpointFlag):] - - addrParts := strings.SplitN(grpcAddr, ":", 2) - protocol := addrParts[0] - - if protocol != "tcp" && protocol != "unix" { - return nil, errfmt.Errorf("grpc supported protocols are tcp or unix. eg: tcp:4466, unix:/tmp/tracee.sock") - } - - if len(addrParts) == 1 { - if protocol == "tcp" { - grpcAddr += ":4466" - } else { // protocol == "unix" - grpcAddr += ":/var/run/tracee.sock" + //flag grpc.Xxx + case GRPCServer: + grpcParts := strings.SplitN(serverParts[1], "=", 1) + switch grpcParts[0] { + case ListenEndpointFlag: + addressParts := strings.SplitN(grpcParts[1], ":", 2) + switch addressParts[0] { + case "unix": + if len(addressParts) == 1 { + addressParts = append(addressParts, "/var/run/tracee.sock") } - } - // cleanup listen address if needed (unix socket), for example if a panic happened - if protocol == "unix" { - path := strings.SplitN(grpcAddr, ":", 2)[1] - if _, err = os.Stat(path); err == nil { - err = os.Remove(path) + if _, err = os.Stat(addressParts[1]); err == nil { + err = os.Remove(addressParts[1]) if err != nil { - return nil, errfmt.Errorf("failed to cleanup gRPC listening address (%s): %v", path, err) + return nil, errfmt.Errorf("failed to cleanup gRPC listening address (%s): %v", addressParts[1], err) } } + case "tcp": + if len(addressParts) == 1 { + addressParts = append(addressParts, "4466") + } + default: + return nil, errfmt.Errorf("grpc supported protocols are tcp or unix. eg: tcp:4466, unix:/tmp/tracee.sock") + } + server.GRPCServer, err = grpc.New(addressParts[0], addressParts[1]) + if err != nil { + return nil, err } - server.GRPCServer, err = grpc.New("dsa", "dsa") //protocol, grpcAddr) + default: + if _, err = os.Stat("/var/run/tracee.sock"); err == nil { + err = os.Remove("/var/run/tracee.sock") + if err != nil { + return nil, errfmt.Errorf("failed to cleanup gRPC listening address (%s): %v", "/var/run/tracee.sock", err) + } + } + server.GRPCServer, err = grpc.New("unix", "/var/run/tracee.sock") + if err != nil { + return nil, err + } } } } From 563f0d6eb36c73269f64f559cf1f1b7d73f320da Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Sun, 5 Jan 2025 15:08:46 +0000 Subject: [PATCH 03/18] fix server algorithem --- pkg/cmd/flags/server/server.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/flags/server/server.go b/pkg/cmd/flags/server/server.go index c771e1075b9f..f4373f671811 100644 --- a/pkg/cmd/flags/server/server.go +++ b/pkg/cmd/flags/server/server.go @@ -65,8 +65,6 @@ func PrepareServer(serverSlice []string) (*Server, error) { if strings.Compare(endpoint, "true") == 0 { enablePyroscope = true } - default: - server.HTTPServer = http.New("") } //flag grpc.Xxx case GRPCServer: @@ -111,7 +109,11 @@ func PrepareServer(serverSlice []string) (*Server, error) { } } } - if server.HTTPServer != nil { + + if server.HTTPServer != nil || enableMetrics || enableHealthz || enablePProf || enablePyroscope { + if server.HTTPServer == nil { + server.HTTPServer = http.New("") + } if enableMetrics { server.HTTPServer.EnableMetricsEndpoint() } From 156bc6f037d8854f468a88292b02f6456ce452c9 Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Sun, 5 Jan 2025 18:18:32 +0000 Subject: [PATCH 04/18] added simple tests for server flag --- pkg/cmd/flags/server/server.go | 14 ++++++---- pkg/cmd/flags/server/server_test.go | 41 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 pkg/cmd/flags/server/server_test.go diff --git a/pkg/cmd/flags/server/server.go b/pkg/cmd/flags/server/server.go index f4373f671811..0ea4fc6172ff 100644 --- a/pkg/cmd/flags/server/server.go +++ b/pkg/cmd/flags/server/server.go @@ -1,6 +1,7 @@ package server import ( + "fmt" "os" "strings" @@ -32,7 +33,7 @@ type Server struct { func PrepareServer(serverSlice []string) (*Server, error) { var err error - var server *Server + var server Server var ( enableMetrics = false enableHealthz = false @@ -42,10 +43,13 @@ func PrepareServer(serverSlice []string) (*Server, error) { for _, endpoint := range serverSlice { // split flag http.address or grpc.address for example serverParts := strings.SplitN(endpoint, ".", 2) + if len(serverParts) < 2 { + return nil, fmt.Errorf("cannot process http or grpc alone") + } switch serverParts[0] { //flag http.Xxx case HTTPServer: - httpParts := strings.SplitN(serverParts[1], "=", 1) + httpParts := strings.SplitN(serverParts[1], "=", 2) switch httpParts[0] { case ListenEndpointFlag: server.HTTPServer = http.New(httpParts[1]) @@ -62,7 +66,7 @@ func PrepareServer(serverSlice []string) (*Server, error) { enablePProf = true } case PyroscopeAgentEndpointFlag: - if strings.Compare(endpoint, "true") == 0 { + if strings.Compare(httpParts[1], "true") == 0 { enablePyroscope = true } } @@ -110,7 +114,7 @@ func PrepareServer(serverSlice []string) (*Server, error) { } } - if server.HTTPServer != nil || enableMetrics || enableHealthz || enablePProf || enablePyroscope { + if enableMetrics || enableHealthz || enablePProf || enablePyroscope { if server.HTTPServer == nil { server.HTTPServer = http.New("") } @@ -131,5 +135,5 @@ func PrepareServer(serverSlice []string) (*Server, error) { } } - return server, nil + return &server, nil } diff --git a/pkg/cmd/flags/server/server_test.go b/pkg/cmd/flags/server/server_test.go new file mode 100644 index 000000000000..556d112d51c2 --- /dev/null +++ b/pkg/cmd/flags/server/server_test.go @@ -0,0 +1,41 @@ +package server + +import ( + "testing" + + "github.com/aquasecurity/tracee/pkg/server/http" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPrepareServer(t *testing.T) { + t.Parallel() + + testCases := []struct { + testName string + serverFlags []string + expectedServer *Server + expectedError error + }{ + { + testName: "http server only", + serverFlags: []string{"http.address=127.0.0.1:8080"}, + expectedServer: &Server{ + HTTPServer: http.New("127.0.0.1:8080"), + }, + expectedError: nil, + }, + } + for _, testcase := range testCases { + t.Run(testcase.testName, func(t *testing.T) { + server, err := PrepareServer(testcase.serverFlags) + if err != nil { + require.NotNil(t, testcase.expectedError) + assert.Contains(t, err.Error(), testcase.expectedError.Error()) + } else { + assert.Equal(t, testcase.expectedServer, server) + } + + }) + } +} From d55e76fe0f2c2317820fc0ee5cfe60957d379420 Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Mon, 6 Jan 2025 14:34:45 +0000 Subject: [PATCH 05/18] changes init of grpc server --- pkg/cmd/flags/server/server.go | 10 ++-------- pkg/server/grpc/server.go | 14 +++++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/pkg/cmd/flags/server/server.go b/pkg/cmd/flags/server/server.go index 0ea4fc6172ff..84b8e87b131a 100644 --- a/pkg/cmd/flags/server/server.go +++ b/pkg/cmd/flags/server/server.go @@ -94,10 +94,7 @@ func PrepareServer(serverSlice []string) (*Server, error) { default: return nil, errfmt.Errorf("grpc supported protocols are tcp or unix. eg: tcp:4466, unix:/tmp/tracee.sock") } - server.GRPCServer, err = grpc.New(addressParts[0], addressParts[1]) - if err != nil { - return nil, err - } + server.GRPCServer = grpc.New(addressParts[0], addressParts[1]) default: if _, err = os.Stat("/var/run/tracee.sock"); err == nil { @@ -106,10 +103,7 @@ func PrepareServer(serverSlice []string) (*Server, error) { return nil, errfmt.Errorf("failed to cleanup gRPC listening address (%s): %v", "/var/run/tracee.sock", err) } } - server.GRPCServer, err = grpc.New("unix", "/var/run/tracee.sock") - if err != nil { - return nil, err - } + server.GRPCServer = grpc.New("unix", "/var/run/tracee.sock") } } } diff --git a/pkg/server/grpc/server.go b/pkg/server/grpc/server.go index a9cc582b377e..af4d0c5b0776 100644 --- a/pkg/server/grpc/server.go +++ b/pkg/server/grpc/server.go @@ -21,20 +21,20 @@ type Server struct { server *grpc.Server } -func New(protocol, listenAddr string) (*Server, error) { +func New(protocol, listenAddr string) *Server { if protocol == "tcp" { listenAddr = ":" + listenAddr } - lis, err := net.Listen(protocol, listenAddr) - if err != nil { - return nil, err - } - - return &Server{listener: lis, protocol: protocol, listenAddr: listenAddr}, nil + return &Server{listener: nil, protocol: protocol, listenAddr: listenAddr} } func (s *Server) Start(ctx context.Context, t *tracee.Tracee, e *engine.Engine) { + var err error + s.listener, err = net.Listen(s.protocol, s.listenAddr) + if err != nil { + logger.Errorw("GRPC server", "error", err) + } srvCtx, srvCancel := context.WithCancel(ctx) defer srvCancel() From c8e8f3514236f7505ccee0c5057437447ee9713f Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Mon, 6 Jan 2025 19:36:02 +0000 Subject: [PATCH 06/18] added return nol error for function --- pkg/cmd/flags/grpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/flags/grpc.go b/pkg/cmd/flags/grpc.go index 1ae4c9cde0e6..d6ecffae2b82 100644 --- a/pkg/cmd/flags/grpc.go +++ b/pkg/cmd/flags/grpc.go @@ -33,5 +33,5 @@ func PrepareGRPCServer(listenAddr string) (*grpc.Server, error) { } } - return grpc.New(addr[0], addr[1]) + return grpc.New(addr[0], addr[1]), nil } From d55024a10fa223a73215247c0f81fb698a790f29 Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Mon, 6 Jan 2025 19:36:39 +0000 Subject: [PATCH 07/18] added error handling to server --- pkg/cmd/flags/server/server.go | 51 ++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/flags/server/server.go b/pkg/cmd/flags/server/server.go index 84b8e87b131a..76dcfa667f37 100644 --- a/pkg/cmd/flags/server/server.go +++ b/pkg/cmd/flags/server/server.go @@ -1,7 +1,10 @@ package server import ( + "errors" "fmt" + "net" + "net/url" "os" "strings" @@ -44,7 +47,7 @@ func PrepareServer(serverSlice []string) (*Server, error) { // split flag http.address or grpc.address for example serverParts := strings.SplitN(endpoint, ".", 2) if len(serverParts) < 2 { - return nil, fmt.Errorf("cannot process http or grpc alone") + return nil, fmt.Errorf("cannot process the flag: try grpc.Xxx or http.Xxx instead") } switch serverParts[0] { //flag http.Xxx @@ -52,7 +55,11 @@ func PrepareServer(serverSlice []string) (*Server, error) { httpParts := strings.SplitN(serverParts[1], "=", 2) switch httpParts[0] { case ListenEndpointFlag: - server.HTTPServer = http.New(httpParts[1]) + if isValidAddr(httpParts[1]) { + server.HTTPServer = http.New(httpParts[1]) + } else { + return nil, errors.New("invalid http address") + } case MetricsEndpointFlag: if strings.Compare(httpParts[1], "true") == 0 { enableMetrics = true @@ -69,10 +76,12 @@ func PrepareServer(serverSlice []string) (*Server, error) { if strings.Compare(httpParts[1], "true") == 0 { enablePyroscope = true } + default: + return nil, errors.New("invalid http flag, consider using one of the following commands: address, metrics, healthz, pprof, pyroscope") } //flag grpc.Xxx case GRPCServer: - grpcParts := strings.SplitN(serverParts[1], "=", 1) + grpcParts := strings.SplitN(serverParts[1], "=", 2) switch grpcParts[0] { case ListenEndpointFlag: addressParts := strings.SplitN(grpcParts[1], ":", 2) @@ -92,19 +101,15 @@ func PrepareServer(serverSlice []string) (*Server, error) { addressParts = append(addressParts, "4466") } default: - return nil, errfmt.Errorf("grpc supported protocols are tcp or unix. eg: tcp:4466, unix:/tmp/tracee.sock") + return nil, errfmt.Errorf("grpc supported protocols are tcp or unix. eg: tcp:4466, unix:/var/run/tracee.sock") } server.GRPCServer = grpc.New(addressParts[0], addressParts[1]) default: - if _, err = os.Stat("/var/run/tracee.sock"); err == nil { - err = os.Remove("/var/run/tracee.sock") - if err != nil { - return nil, errfmt.Errorf("failed to cleanup gRPC listening address (%s): %v", "/var/run/tracee.sock", err) - } - } - server.GRPCServer = grpc.New("unix", "/var/run/tracee.sock") + return nil, errors.New("invalid grpc flag, consider using address") } + default: + return nil, fmt.Errorf("cannot process the flag: try grpc.Xxx or http.Xxx instead") } } @@ -131,3 +136,27 @@ func PrepareServer(serverSlice []string) (*Server, error) { return &server, nil } + +func isValidAddr(addr string) bool { + _, err := url.ParseRequestURI("http://" + addr) + if err != nil { + return false + } + + host, port, err := net.SplitHostPort(addr) + if err != nil { + return false + } + + ip := net.ParseIP(host) + if ip == nil && host != "localhost" && host != "0.0.0.0" { + return false + } + + _, err = net.LookupPort("tcp", port) + if err != nil { + return false + } + + return true +} From 6de4e616439bbaf72ab8dc1703ce30406f974f27 Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Mon, 6 Jan 2025 19:37:06 +0000 Subject: [PATCH 08/18] added more test to server flag --- pkg/cmd/flags/server/server_test.go | 109 +++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/flags/server/server_test.go b/pkg/cmd/flags/server/server_test.go index 556d112d51c2..b68f432ab43f 100644 --- a/pkg/cmd/flags/server/server_test.go +++ b/pkg/cmd/flags/server/server_test.go @@ -1,8 +1,10 @@ package server import ( + "fmt" "testing" + "github.com/aquasecurity/tracee/pkg/server/grpc" "github.com/aquasecurity/tracee/pkg/server/http" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -10,7 +12,6 @@ import ( func TestPrepareServer(t *testing.T) { t.Parallel() - testCases := []struct { testName string serverFlags []string @@ -25,6 +26,92 @@ func TestPrepareServer(t *testing.T) { }, expectedError: nil, }, + { + testName: "grpc server only", + serverFlags: []string{"grpc.address=unix:/tmp/tracee.sock"}, + expectedServer: &Server{ + GRPCServer: grpc.New("unix", "/tmp/tracee.sock"), + }, + expectedError: nil, + }, + { + testName: "http and grpc server", + serverFlags: []string{ + "http.address=127.0.0.1:8080", + "grpc.address=unix:/tmp/tracee.sock", + }, + expectedServer: &Server{ + HTTPServer: http.New("127.0.0.1:8080"), + GRPCServer: grpc.New("unix", "/tmp/tracee.sock"), + }, + expectedError: nil, + }, + { + testName: "grpc server with unix socket", + serverFlags: []string{ + "grpc.address=unix", + }, + expectedServer: &Server{ + GRPCServer: grpc.New("unix", "/var/run/tracee.sock"), + }, + expectedError: nil, + }, + { + testName: "grpc server with tcp address", + serverFlags: []string{ + "grpc.address=tcp:4466", + }, + expectedServer: &Server{ + GRPCServer: grpc.New("tcp", "4466"), + }, + expectedError: nil, + }, + { + testName: "grpc server with tcp only", + serverFlags: []string{ + "grpc.address=tcp", + }, + expectedServer: &Server{ + GRPCServer: grpc.New("tcp", "4466"), + }, + expectedError: nil, + }, + { + testName: "invalid server flag", + serverFlags: []string{"invalid"}, + expectedServer: nil, + expectedError: fmt.Errorf("cannot process the flag: try grpc.Xxx or http.Xxx instead"), + }, + { + testName: "invalid server flag", + serverFlags: []string{"invalid.invalid"}, + expectedServer: nil, + expectedError: fmt.Errorf("cannot process the flag: try grpc.Xxx or http.Xxx instead"), + }, + { + testName: "invalid http flag", + serverFlags: []string{"http.invalid=true"}, + expectedServer: nil, + expectedError: fmt.Errorf("invalid http flag, consider using one of the following commands: address, metrics, healthz, pprof, pyroscope"), + }, + { + testName: "invalid grpc flag", + serverFlags: []string{"grpc.invalid=true"}, + expectedServer: nil, + expectedError: fmt.Errorf("invalid grpc flag, consider using address"), + }, + { + testName: "invalid grpc protocol", + serverFlags: []string{"grpc.address=invalid:4466"}, + expectedServer: nil, + expectedError: fmt.Errorf("grpc supported protocols are tcp or unix. eg: tcp:4466, unix:/var/run/tracee.sock"), + }, + { + testName: "invalid http protocol", + serverFlags: []string{"http.address=invalid:8080"}, + expectedServer: nil, + expectedError: fmt.Errorf("invalid http address"), + }, } for _, testcase := range testCases { t.Run(testcase.testName, func(t *testing.T) { @@ -39,3 +126,23 @@ func TestPrepareServer(t *testing.T) { }) } } + +func GetHttpTestServer(listenAddr string, metrics bool, pprof bool, healthz bool, pyroscope bool) *http.Server { + server := http.New(listenAddr) + if metrics { + server.EnableMetricsEndpoint() + } + if healthz { + server.EnableHealthzEndpoint() + } + if pprof { + server.EnablePProfEndpoint() + } + if pyroscope { + err := server.EnablePyroAgent() + if err != nil { + panic(err) + } + } + return server +} From a2515d605bcbbd08102f6a30142e5e8572d5d45d Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Mon, 6 Jan 2025 19:56:28 +0000 Subject: [PATCH 09/18] add help for server flag --- cmd/tracee/cmd/root.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/tracee/cmd/root.go b/cmd/tracee/cmd/root.go index 822ae7b37e4b..656b010bf891 100644 --- a/cmd/tracee/cmd/root.go +++ b/cmd/tracee/cmd/root.go @@ -252,8 +252,9 @@ func initCmd() error { rootCmd.Flags().StringArray( "server", []string{""}, - "Server configuration options (e.g., --server http.metrics=true)", //add help comments - ) + `--server .