diff --git a/cmd/traceectl/cmd/event.go b/cmd/traceectl/cmd/event.go new file mode 100644 index 000000000000..be5c3e173420 --- /dev/null +++ b/cmd/traceectl/cmd/event.go @@ -0,0 +1,179 @@ +package cmd + +//TODO: +import ( + "context" + "fmt" + "strings" + + pb "github.com/aquasecurity/tracee/api/v1beta1" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/client" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/cmd/formatter" + "github.com/spf13/cobra" +) + +var eventCmd = &cobra.Command{ + Use: "event [command]", + Short: "Event management for tracee", + Long: `Event Management for tracee + Let you enable and disable events in tracee. + Get descriptions of events. + `, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + cmd.PrintErrln("Error: no event names provided. Please specify at least one event to enable.") + return + } + }, +} + +func init() { + eventCmd.AddCommand(listEventCmd) + eventCmd.AddCommand(describeEventCmd) + eventCmd.AddCommand(enableEventCmd) + eventCmd.AddCommand(disableEventCmd) + + listEventCmd.Flags().StringVarP(&formatFlag, "format", "f", formatter.FormatTable, "Output format (json|table)") + describeEventCmd.Flags().StringVarP(&formatFlag, "format", "f", formatter.FormatTable, "Output format (json|table)") +} + +var listEventCmd = &cobra.Command{ + Use: "list", + Short: "list events", + Long: `Lists all available event definitions (built-in and plugin-defined), providing a brief summary of each.`, + Args: cobra.MaximumNArgs(0), + Run: func(cmd *cobra.Command, args []string) { + listEvents(cmd, args) + }, +} +var describeEventCmd = &cobra.Command{ + Use: "describe ", + Short: "describe event", + Long: `Retrieves the detailed definition of a specific event, including its fields, types, and other metadata.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + getEventDescriptions(cmd, args) + }, +} +var enableEventCmd = &cobra.Command{ + Use: "enable ", + Short: "enable event", + Long: `Enables capturing of a specific event type.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + enableEvents(cmd, args[0]) + }, +} +var disableEventCmd = &cobra.Command{ + Use: "disable ", + Short: "disable event", + Long: `Disables capturing of a specific event type.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + disableEvents(cmd, args[0]) + }, +} + +func listEvents(cmd *cobra.Command, args []string) { + var traceeClient client.ServiceClient + if err := traceeClient.NewServiceClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return + } + defer traceeClient.CloseConnection() + + response, err := traceeClient.GetEventDefinitions(context.Background(), &pb.GetEventDefinitionsRequest{EventNames: args}) + if err != nil { + cmd.PrintErrln("Error getting event definitions: ", err) + return + } + format, err := formatter.NewFormatter(formatFlag, cmd) + if err != nil { + cmd.PrintErrln("Error creating formatter: ", err) + return + } + switch format.GetFormat() { + case formatter.FormatJson: + format.PrintJson(response.String()) + case formatter.FormatTable: + format.PrintTableHeaders([]string{"ID", "Name", "Version", "Tags"}) + for _, event := range response.Definitions { + //remove descriptions + format.PrintTableRow(prepareDescription(event)[:4]) + } + default: + //Error: output format not supported + return + } +} + +func getEventDescriptions(cmd *cobra.Command, args []string) { + var traceeClient client.ServiceClient + if err := traceeClient.NewServiceClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return + } + defer traceeClient.CloseConnection() + + response, err := traceeClient.GetEventDefinitions(context.Background(), &pb.GetEventDefinitionsRequest{EventNames: args}) + if err != nil { + cmd.PrintErrln("Error getting event definitions: ", err) + return + + } + format, err := formatter.NewFormatter(formatFlag, cmd) + if err != nil { + cmd.PrintErrln("Error creating formatter: ", err) + return + } + switch format.GetFormat() { + case formatter.FormatJson: + format.PrintJson(response.String()) + case formatter.FormatTable: + format.PrintTableHeaders([]string{"ID", "Name", "Version", "Tags", "Description"}) + for _, event := range response.Definitions { + format.PrintTableRow(prepareDescription(event)) + } + default: + //Error: output format not supported + return + + } +} +func prepareDescription(event *pb.EventDefinition) []string { + return []string{ + fmt.Sprintf("%d", event.Id), + event.Name, + fmt.Sprintf("%d.%d.%d", event.Version.Major, event.Version.Minor, event.Version.Patch), + strings.Join(event.Tags, ", "), + event.Description, + } + +} +func enableEvents(cmd *cobra.Command, eventName string) { + var traceeClient client.ServiceClient + if err := traceeClient.NewServiceClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return + } + _, err := traceeClient.EnableEvent(context.Background(), &pb.EnableEventRequest{Name: eventName}) + if err != nil { + cmd.PrintErrln("Error enabling event:", err) + return + } + cmd.Printf("Enabled event: %s\n", eventName) +} +func disableEvents(cmd *cobra.Command, eventName string) { + var traceeClient client.ServiceClient + if err := traceeClient.NewServiceClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return + } + _, err := traceeClient.DisableEvent(context.Background(), &pb.DisableEventRequest{Name: eventName}) + if err != nil { + cmd.PrintErrln("Error disabling event:", err) + return + } + cmd.Printf("Disabled event: %s\n", eventName) +} diff --git a/cmd/traceectl/cmd/event_test.go b/cmd/traceectl/cmd/event_test.go new file mode 100644 index 000000000000..ffc8dfde2d6b --- /dev/null +++ b/cmd/traceectl/cmd/event_test.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "fmt" + "testing" + + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/cmd/test" +) + +func TestEvent(t *testing.T) { + eventTests := []test.TestCase{ + { + TestName: "event", + OutputSlice: []string{"event"}, + ExpectedPrinter: nil, + ExpectedError: fmt.Errorf("requires at least 1 arg(s), only received 0"), + }, + { + TestName: "events list", + OutputSlice: []string{"event", "list", "--format", "json"}, + ExpectedPrinter: "", + ExpectedError: nil, + }, + { + TestName: "No events describe", + OutputSlice: []string{"event", "describe", "--format", "json"}, + ExpectedPrinter: nil, + ExpectedError: fmt.Errorf("accepts 1 arg(s), received 0"), + }, + { + TestName: "describe ", + OutputSlice: []string{"event", "describe", "event_test1", "--format", "json"}, + ExpectedPrinter: "event_test1", + ExpectedError: nil, + }, + { + TestName: "No events enable", + OutputSlice: []string{"event", "enable"}, + ExpectedPrinter: nil, + ExpectedError: fmt.Errorf("accepts 1 arg(s), received 0"), + }, + { + TestName: "enable event", + OutputSlice: []string{"event", "enable", "event"}, + ExpectedPrinter: "Enabled event: event", + ExpectedError: nil, + }, + { + TestName: "No disable events", + OutputSlice: []string{"event", "disable"}, + ExpectedPrinter: nil, + ExpectedError: fmt.Errorf("accepts 1 arg(s), received 0"), + }, + { + TestName: "disable event", + OutputSlice: []string{"event", "disable", "event"}, + ExpectedPrinter: "Disabled event: event", + ExpectedError: nil, + }, + } + for _, testCase := range eventTests { + t.Run(testCase.TestName, func(t *testing.T) { test.TestCommand(t, testCase, rootCmd) }) + } +} diff --git a/cmd/traceectl/cmd/root.go b/cmd/traceectl/cmd/root.go new file mode 100644 index 000000000000..f58e5dc874d4 --- /dev/null +++ b/cmd/traceectl/cmd/root.go @@ -0,0 +1,114 @@ +package cmd + +import ( + "context" + "os" + + pb "github.com/aquasecurity/tracee/api/v1beta1" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/client" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/cmd/flags" + + "github.com/spf13/cobra" +) + +var formatFlag string +var outputFlag string +var ( + serverInfo client.ServerInfo = client.ServerInfo{ + ConnectionType: client.Protocol_UNIX, + Addr: client.Socket, + } + + rootCmd = &cobra.Command{ + Use: "traceectl [flags] [command]", + Short: "TraceeCtl is a CLI tool for tracee", + Long: "TraceeCtl is the client for the tracee API server.", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if err := flags.PrepareOutput(cmd, outputFlag); err != nil { + return err + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, + } +) + +func init() { + rootCmd.AddCommand(streamCmd) + rootCmd.AddCommand(eventCmd) + rootCmd.AddCommand(metricsCmd) + rootCmd.AddCommand(versionCmd) + + rootCmd.PersistentFlags().StringVar(&serverInfo.Addr, "server", client.Socket, `Server connection path or address. + for unix socket (default: /tmp/tracee.sock) + for tcp `) + rootCmd.PersistentFlags().StringVarP(&outputFlag, "output", "o", "", "Specify the output format") +} + +var metricsCmd = &cobra.Command{ + Use: "metrics [--output ]", + Short: "Display Tracee metrics", + Long: "Retrieves metrics about Tracee's performance and resource usage.", + Run: func(cmd *cobra.Command, args []string) { + displayMetrics(cmd, args) + }, +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Display the version of tracee", + Long: "This is the version of tracee application you connected to", + Run: func(cmd *cobra.Command, args []string) { + displayVersion(cmd, args) + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +func displayMetrics(cmd *cobra.Command, _ []string) { + var traceeClient client.DiagnosticClient + if err := traceeClient.NewDiagnosticClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return + } + defer traceeClient.CloseConnection() + + response, err := traceeClient.GetMetrics(context.Background(), &pb.GetMetricsRequest{}) + if err != nil { + cmd.PrintErrln("Error getting metrics: ", err) + return + } + cmd.Println("EventCount:", response.EventCount) + cmd.Println("EventsFiltered:", response.EventsFiltered) + cmd.Println("NetCapCount:", response.NetCapCount) + cmd.Println("BPFLogsCount:", response.BPFLogsCount) + cmd.Println("ErrorCount:", response.ErrorCount) + cmd.Println("LostEvCount:", response.LostEvCount) + cmd.Println("LostWrCount:", response.LostWrCount) + cmd.Println("LostNtCapCount:", response.LostNtCapCount) + cmd.Println("LostBPFLogsCount:", response.LostBPFLogsCount) +} + +func displayVersion(cmd *cobra.Command, _ []string) { + var traceeClient client.ServiceClient + if err := traceeClient.NewServiceClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return + } + defer traceeClient.CloseConnection() + + response, err := traceeClient.GetVersion(context.Background(), &pb.GetVersionRequest{}) + + if err != nil { + cmd.PrintErrln("Error getting version: ", err) + return + } else { + cmd.Println("Version: ", response.Version) + } +} diff --git a/cmd/traceectl/cmd/stream.go b/cmd/traceectl/cmd/stream.go new file mode 100644 index 000000000000..0dbd8416ba1a --- /dev/null +++ b/cmd/traceectl/cmd/stream.go @@ -0,0 +1,128 @@ +package cmd + +import ( + "fmt" + "strings" + + pb "github.com/aquasecurity/tracee/api/v1beta1" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/client" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/cmd/formatter" + "github.com/spf13/cobra" +) + +var streamCmd = &cobra.Command{ + Use: "stream [policies...]", + Short: "Stream events from tracee", + Long: `Stream Management: + + `, + Run: func(cmd *cobra.Command, args []string) { + streamEvents(cmd, args) + }, +} + +func init() { + streamCmd.Flags().StringVarP(&formatFlag, "format", "f", formatter.FormatTable, "Output format (json|table)") +} + +func streamEvents(cmd *cobra.Command, args []string) { + //connect to tracee + var traceeClient client.ServiceClient + err := traceeClient.NewServiceClient(serverInfo) + if err != nil { + cmd.PrintErrln("Error creating client: ", err) + return + } + defer traceeClient.CloseConnection() + + //request to stream events + req := &pb.StreamEventsRequest{Policies: args} + stream, err := traceeClient.StreamEvents(cmd.Context(), req) + if err != nil { + cmd.PrintErrln("Error calling Stream: ", err) + return + } + //create formatter + format, err := formatter.NewFormatter(formatFlag, cmd) + if err != nil { + cmd.PrintErrln("Error creating formatter: ", err) + return + } + switch format.GetFormat() { + case formatter.FormatJson: + for { + res, err := stream.Recv() + if err != nil { + //End of stream\close connection + if err.Error() == "EOF" { + break + } + //Error receiving streamed event + } + format.PrintJson(res.Event.String()) + } + case formatter.FormatTable: + format.PrintTableHeaders([]string{"TIME", "EVENT NAME", "POLICIES", "PID", "DATA"}) + for { + res, err := stream.Recv() + if err != nil { + //End of stream\close connection + if err.Error() == "EOF" { + break + } + //Error receiving streamed event + } + + format.PrintTableRow(prepareEvent(res.Event)) + } + default: + //Error: output format not supported + return + } +} +func prepareEvent(event *pb.Event) []string { + return []string{ + event.Timestamp.AsTime().Format("15:04:05.000"), + event.Name, + strings.Join(event.Policies.Matched, ","), + fmt.Sprintf("%d", event.Context.Process.Pid.Value), + getEventData(event.Data), + } + +} +func getEventData(data []*pb.EventValue) string { + var result []string + for _, ev := range data { + result = append(result, getEventName(ev)+getEventValue(ev)) + } + return strings.Join(result, ", ") +} +func getEventName(ev *pb.EventValue) string { + return strings.ToUpper(ev.Name[0:1]) + ev.Name[1:] + ": " +} +func getEventValue(ev *pb.EventValue) string { + switch v := ev.Value.(type) { + case *pb.EventValue_Int32: + return fmt.Sprintf("%d", v.Int32) + case *pb.EventValue_Int64: + return fmt.Sprintf("%d", v.Int64) + case *pb.EventValue_UInt32: + return fmt.Sprintf("%d", v.UInt32) + case *pb.EventValue_UInt64: + return fmt.Sprintf("%d", v.UInt64) + case *pb.EventValue_Str: + return v.Str + case *pb.EventValue_Bytes: + return fmt.Sprintf("%x", v.Bytes) + case *pb.EventValue_Bool: + return fmt.Sprintf("%t", v.Bool) + case *pb.EventValue_StrArray: + return strings.Join(v.StrArray.Value, ", ") + case *pb.EventValue_Int32Array: + return fmt.Sprintf("%v", v.Int32Array.Value) + case *pb.EventValue_UInt64Array: + return fmt.Sprintf("%v", v.UInt64Array.Value) + default: + return "unknown" + } +} diff --git a/cmd/traceectl/go.mod b/cmd/traceectl/go.mod new file mode 100644 index 000000000000..94d1d8ba2f95 --- /dev/null +++ b/cmd/traceectl/go.mod @@ -0,0 +1,23 @@ +module github.com/aquasecurity/tracee/cmd/traceectl + +go 1.23.1 + +require ( + github.com/aquasecurity/tracee/api v0.0.0-20240918153521-1b3f9e8657e0 + github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.7.1 + google.golang.org/grpc v1.67.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/cmd/traceectl/go.sum b/cmd/traceectl/go.sum new file mode 100644 index 000000000000..44de7a6fd20b --- /dev/null +++ b/cmd/traceectl/go.sum @@ -0,0 +1,37 @@ +github.com/aquasecurity/tracee/api v0.0.0-20240918153521-1b3f9e8657e0 h1:3pt2Ceg6urRTUdOaX++LheYObFE4DbS63chktnQu24I= +github.com/aquasecurity/tracee/api v0.0.0-20240918153521-1b3f9e8657e0/go.mod h1:Gn6xVkaBkVe1pOQ0++uuHl+lMMClv0TPY8mCQ6j88aA= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +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= diff --git a/cmd/traceectl/main.go b/cmd/traceectl/main.go new file mode 100644 index 000000000000..9e890e896810 --- /dev/null +++ b/cmd/traceectl/main.go @@ -0,0 +1,10 @@ +/* +Copyright © 2024 Shoham Yosef Bitton +*/ +package main + +import "github.com/aquasecurity/tracee/cmd/traceectl/cmd" + +func main() { + cmd.Execute() +} diff --git a/cmd/traceectl/pkg/client/client.go b/cmd/traceectl/pkg/client/client.go new file mode 100644 index 000000000000..ca30aef9596e --- /dev/null +++ b/cmd/traceectl/pkg/client/client.go @@ -0,0 +1,73 @@ +package client + +import ( + "fmt" + "log" + "net" + "strings" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const ( + Protocol_UNIX = "unix" + Protocol_TCP = "tcp" + Socket = "/tmp/tracee.sock" +) + +type ServerInfo struct { + ConnectionType string + Addr string +} + +func connectToServer(serverInfo ServerInfo) (*grpc.ClientConn, error) { + var opts []grpc.DialOption + var conn *grpc.ClientConn + var err error + var address string + opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + err = determineConnectionType(serverInfo) + if err != nil { + return nil, err + } + switch serverInfo.ConnectionType { + case Protocol_UNIX: + address = fmt.Sprintf("unix://%s", serverInfo.Addr) + case Protocol_TCP: + address = fmt.Sprintf(serverInfo.Addr) + default: + return nil, fmt.Errorf("unsupported connection type: %s", serverInfo.ConnectionType) + } + conn, err = grpc.NewClient(address, opts...) + if err != nil { + log.Fatalf("failed to connect to server: %v", err) + return nil, err + } + return conn, nil +} + +func determineConnectionType(serverInfo ServerInfo) error { + if strings.Contains(serverInfo.Addr, ":") && isValidTCPAddress(serverInfo.Addr) { + serverInfo.ConnectionType = Protocol_TCP + return nil + } + if strings.HasPrefix(serverInfo.Addr, "/") { + serverInfo.ConnectionType = Protocol_UNIX + return nil + } + + return fmt.Errorf("unsupported connection type: %s", serverInfo.Addr) + +} +func isValidTCPAddress(addr string) bool { + host, port, err := net.SplitHostPort(addr) + if err != nil || host == "" || port == "" { + return false + } + if _, err := net.LookupPort("tcp", port); err != nil { + return false + } + + return true +} diff --git a/cmd/traceectl/pkg/client/diagnostic.go b/cmd/traceectl/pkg/client/diagnostic.go new file mode 100644 index 000000000000..915523e61663 --- /dev/null +++ b/cmd/traceectl/pkg/client/diagnostic.go @@ -0,0 +1,34 @@ +package client + +import ( + "context" + "log" + + pb "github.com/aquasecurity/tracee/api/v1beta1" + "google.golang.org/grpc" +) + +type DiagnosticClient struct { + conn *grpc.ClientConn + client pb.DiagnosticServiceClient +} + +func (tc *DiagnosticClient) NewDiagnosticClient(serverInfo ServerInfo) error { + conn, err := connectToServer(serverInfo) + if err != nil { + return err + } + tc.conn = conn + tc.client = pb.NewDiagnosticServiceClient(conn) + + return nil +} +func (tc *DiagnosticClient) CloseConnection() { + if err := tc.conn.Close(); err != nil { + log.Printf("Failed to close connection: %v", err) + return + } +} +func (tc *DiagnosticClient) GetMetrics(ctx context.Context, req *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { + return tc.client.GetMetrics(ctx, req) +} diff --git a/cmd/traceectl/pkg/client/service.go b/cmd/traceectl/pkg/client/service.go new file mode 100644 index 000000000000..7800b795206e --- /dev/null +++ b/cmd/traceectl/pkg/client/service.go @@ -0,0 +1,49 @@ +package client + +import ( + "context" + "log" + + pb "github.com/aquasecurity/tracee/api/v1beta1" + "google.golang.org/grpc" +) + +type ServiceClient struct { + conn *grpc.ClientConn + client pb.TraceeServiceClient +} + +func (tc *ServiceClient) NewServiceClient(serverInfo ServerInfo) error { + conn, err := connectToServer(serverInfo) + if err != nil { + return err + } + tc.conn = conn + tc.client = pb.NewTraceeServiceClient(conn) + + return nil +} +func (tc *ServiceClient) CloseConnection() { + if err := tc.conn.Close(); err != nil { + log.Printf("Failed to close connection: %v", err) + return + } +} +func (tc *ServiceClient) GetVersion(ctx context.Context, req *pb.GetVersionRequest) (*pb.GetVersionResponse, error) { + return tc.client.GetVersion(ctx, req) +} + +func (tc *ServiceClient) EnableEvent(ctx context.Context, req *pb.EnableEventRequest) (*pb.EnableEventResponse, error) { + return tc.client.EnableEvent(ctx, req) +} + +func (tc *ServiceClient) DisableEvent(ctx context.Context, req *pb.DisableEventRequest) (*pb.DisableEventResponse, error) { + return tc.client.DisableEvent(ctx, req) +} + +func (tc *ServiceClient) StreamEvents(ctx context.Context, req *pb.StreamEventsRequest) (pb.TraceeService_StreamEventsClient, error) { + return tc.client.StreamEvents(ctx, req) +} +func (tc *ServiceClient) GetEventDefinitions(ctx context.Context, req *pb.GetEventDefinitionsRequest) (*pb.GetEventDefinitionsResponse, error) { + return tc.client.GetEventDefinitions(ctx, req) +} diff --git a/cmd/traceectl/pkg/cmd/flags/output.go b/cmd/traceectl/pkg/cmd/flags/output.go new file mode 100644 index 000000000000..47abc4e451a7 --- /dev/null +++ b/cmd/traceectl/pkg/cmd/flags/output.go @@ -0,0 +1,39 @@ +package flags + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" +) + +func PrepareOutput(cmd *cobra.Command, output string) error { + if (output != "") && (output != "stdout") { + /// Validate the file path + if output == "" || strings.TrimSpace(output) == "" { + return fmt.Errorf("output file path is empty or invalid") + } + + // Ensure parent directories exist + dir := filepath.Dir(output) + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create directories for output file: %v", err) + } + + // Create or open the file + file, err := os.Create(output) + if err != nil { + return fmt.Errorf("failed to open output file: %v", err) + } + + cmd.SetOut(file) + cmd.SetErr(file) + // Make sure to close the file after execution + cmd.PersistentPostRun = func(cmd *cobra.Command, args []string) { + file.Close() + } + } + return nil +} diff --git a/cmd/traceectl/pkg/cmd/formatter/formatter.go b/cmd/traceectl/pkg/cmd/formatter/formatter.go new file mode 100644 index 000000000000..55222f13e3a8 --- /dev/null +++ b/cmd/traceectl/pkg/cmd/formatter/formatter.go @@ -0,0 +1,45 @@ +package formatter + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +const ( + FormatJson = "json" + FormatTable = "table" +) + +var SupportedFormats = []string{FormatJson, FormatTable} + +type Formatter struct { + format string + cmd *cobra.Command + paddings map[int][]int +} + +func NewFormatter(format string, cmd *cobra.Command) (*Formatter, error) { + switch format { + case FormatJson: + return &Formatter{ + format: format, + cmd: cmd, + }, nil + case FormatTable: + //add padding for table + return &Formatter{ + format: format, + cmd: cmd, + paddings: map[int][]int{ + 4: {20, 15, 15, 20}, // Padding for 4 columns + 5: {15, 10, 20, 15, 10}, // Padding for 5 columns + }, + }, nil + default: + return nil, fmt.Errorf("format %s is not supported", format) + } +} +func (f *Formatter) GetFormat() string { + return f.format +} diff --git a/cmd/traceectl/pkg/cmd/formatter/jsonFormat.go b/cmd/traceectl/pkg/cmd/formatter/jsonFormat.go new file mode 100644 index 000000000000..9ed64845ed9e --- /dev/null +++ b/cmd/traceectl/pkg/cmd/formatter/jsonFormat.go @@ -0,0 +1,5 @@ +package formatter + +func (f *Formatter) PrintJson(response string) { + f.cmd.Printf(response) +} diff --git a/cmd/traceectl/pkg/cmd/formatter/tableFormat.go b/cmd/traceectl/pkg/cmd/formatter/tableFormat.go new file mode 100644 index 000000000000..ef04c88efc1f --- /dev/null +++ b/cmd/traceectl/pkg/cmd/formatter/tableFormat.go @@ -0,0 +1,47 @@ +package formatter + +// PrintTableHeaders prints table headers with padding based on their length. +func (f *Formatter) PrintTableHeaders(headers []string) { + switch len(headers) { + case 4: + f.cmd.Printf("%-20s %-15s %-15s %-20s\n", + headers[0], + headers[1], + headers[2], + headers[3], + ) + case 5: + f.cmd.Printf("%-15s %-10s %-20s %-15s %-10s\n", + headers[0], + headers[1], + headers[2], + headers[3], + headers[4], + ) + default: + f.cmd.Println("Error: Unsupported number of headers.") + } +} + +// PrintTableRow prints a single row with padding matching the header format. +func (f *Formatter) PrintTableRow(row []string) { + switch len(row) { + case 4: + f.cmd.Printf("%-20s %-15s %-15s %-20s\n", + row[0], + row[1], + row[2], + row[3], + ) + case 5: + f.cmd.Printf("%-15s %-10s %-20s %-15s %-10s\n", + row[0], + row[1], + row[2], + row[3], + row[4], + ) + default: + f.cmd.Println("Error: Unsupported number of columns in row.") + } +} diff --git a/cmd/traceectl/pkg/cmd/test/test.go b/cmd/traceectl/pkg/cmd/test/test.go new file mode 100644 index 000000000000..2de066b69ce5 --- /dev/null +++ b/cmd/traceectl/pkg/cmd/test/test.go @@ -0,0 +1,52 @@ +package test + +import ( + "bytes" + "testing" + "time" + + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/mock" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" +) + +type TestCase struct { + TestName string + OutputSlice []string + ExpectedPrinter interface{} + ExpectedError error +} + +func runMockServer(t *testing.T) *grpc.Server { + mockServer, err := mock.StartMockServer() + if err != nil { + t.Fatalf("Failed to start mock server: %v", err) + } + time.Sleep(100 * time.Millisecond) + return mockServer +} +func TestCommand(t *testing.T, testCase TestCase, rootCmd *cobra.Command) { + server := runMockServer(t) + defer server.Stop() + var buf bytes.Buffer + rootCmd.SetOut(&buf) + rootCmd.SetErr(&buf) + rootCmd.SetArgs(testCase.OutputSlice) + err := rootCmd.Execute() + output := buf.String() + if err != nil && testCase.ExpectedError == nil { + t.Errorf("Unexpected error for test %s: %v", testCase.TestName, err) + return + } + if err == nil && testCase.ExpectedError != nil { + t.Errorf("Expected error for test %s but got none", testCase.TestName) + return + } + if testCase.ExpectedError != nil { + assert.ErrorContains(t, err, testCase.ExpectedError.Error()) + } else { + assert.Contains(t, output, testCase.ExpectedPrinter) + + } +} diff --git a/cmd/traceectl/pkg/mock/diagnostic_server.go b/cmd/traceectl/pkg/mock/diagnostic_server.go new file mode 100644 index 000000000000..1d4fcff36e58 --- /dev/null +++ b/cmd/traceectl/pkg/mock/diagnostic_server.go @@ -0,0 +1,17 @@ +package mock + +import ( + "context" + + pb "github.com/aquasecurity/tracee/api/v1beta1" +) + +var ( + ExpectedMetrics pb.GetMetricsResponse = pb.GetMetricsResponse{EventCount: 1, EventsFiltered: 2, NetCapCount: 3, + BPFLogsCount: 4, ErrorCount: 5, LostEvCount: 6, + LostWrCount: 7, LostNtCapCount: 8, LostBPFLogsCount: 9} +) + +func (s *MockDiagnosticServer) GetMetrics(ctx context.Context, req *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { + return &ExpectedMetrics, nil +} diff --git a/cmd/traceectl/pkg/mock/event_server.go b/cmd/traceectl/pkg/mock/event_server.go new file mode 100644 index 000000000000..689c98d8554c --- /dev/null +++ b/cmd/traceectl/pkg/mock/event_server.go @@ -0,0 +1,28 @@ +package mock + +import ( + "context" + + pb "github.com/aquasecurity/tracee/api/v1beta1" +) + +func (s *MockServiceServer) GetEventDefinitions(ctx context.Context, req *pb.GetEventDefinitionsRequest) (*pb.GetEventDefinitionsResponse, error) { + var eventDefinition []*pb.EventDefinition + for i := 0; i < len(req.EventNames); i++ { + eventDefinition = append(eventDefinition, &pb.EventDefinition{Name: req.EventNames[i], Id: (int32(i))}) + } + return &pb.GetEventDefinitionsResponse{Definitions: eventDefinition}, nil +} +func (s *MockServiceServer) DescribeEvent(ctx context.Context, req *pb.GetEventDefinitionsRequest) (*pb.GetEventDefinitionsResponse, error) { + return s.GetEventDefinitions(ctx, req) +} +func (s *MockServiceServer) ListEvent(ctx context.Context, req *pb.GetEventDefinitionsRequest) (*pb.GetEventDefinitionsResponse, error) { + return s.GetEventDefinitions(ctx, req) +} + +func (s *MockServiceServer) EnableEvent(ctx context.Context, req *pb.EnableEventRequest) (*pb.EnableEventResponse, error) { + return &pb.EnableEventResponse{}, nil +} +func (s *MockServiceServer) DisableEvent(ctx context.Context, req *pb.DisableEventRequest) (*pb.DisableEventResponse, error) { + return &pb.DisableEventResponse{}, nil +} diff --git a/cmd/traceectl/pkg/mock/root_server.go b/cmd/traceectl/pkg/mock/root_server.go new file mode 100644 index 000000000000..2d9ba32021aa --- /dev/null +++ b/cmd/traceectl/pkg/mock/root_server.go @@ -0,0 +1,11 @@ +package mock + +import ( + "context" + + pb "github.com/aquasecurity/tracee/api/v1beta1" +) + +func (s *MockServiceServer) GetVersion(ctx context.Context, req *pb.GetVersionRequest) (*pb.GetVersionResponse, error) { + return &pb.GetVersionResponse{Version: ExpectedVersion}, nil +} diff --git a/cmd/traceectl/pkg/mock/server.go b/cmd/traceectl/pkg/mock/server.go new file mode 100644 index 000000000000..1353f22e6010 --- /dev/null +++ b/cmd/traceectl/pkg/mock/server.go @@ -0,0 +1,63 @@ +package mock + +import ( + "fmt" + "net" + "os" + + pb "github.com/aquasecurity/tracee/api/v1beta1" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/client" + + "google.golang.org/grpc" +) + +var ( + ExpectedVersion string = "v0.22.0-15-gd09d7fca0d" + serverInfo *client.ServerInfo = &client.ServerInfo{ + Addr: client.Socket, + ConnectionType: client.Protocol_UNIX, + } +) + +type MockServiceServer struct { + pb.UnimplementedTraceeServiceServer +} +type MockDiagnosticServer struct { + pb.UnimplementedDiagnosticServiceServer +} + +func CreateMockServer() (*grpc.Server, net.Listener, error) { + if _, err := os.Stat(serverInfo.Addr); err == nil { + if err := os.Remove(serverInfo.Addr); err != nil { + return nil, nil, fmt.Errorf("failed to cleanup gRPC listening address (%s): %v", serverInfo.Addr, err) + } + } + listener, err := net.Listen("unix", serverInfo.Addr) + if err != nil { + return nil, nil, fmt.Errorf("failed to create Unix socket listener: %v", err) + } + server := grpc.NewServer() + pb.RegisterTraceeServiceServer(server, &MockServiceServer{}) + pb.RegisterDiagnosticServiceServer(server, &MockDiagnosticServer{}) + + return server, listener, nil +} +func StartMockServer() (*grpc.Server, error) { + mockServer, listener, err := CreateMockServer() + if err != nil { + return nil, fmt.Errorf("failed to create mock server: %v", err) + } + go func() { + if err := mockServer.Serve(listener); err != nil { + fmt.Printf("gRPC server failed: %v\n", err) + } + }() + + return mockServer, nil +} +func StopMockServer(server *grpc.Server) { + server.GracefulStop() + if err := os.Remove(serverInfo.Addr); err != nil { + fmt.Printf("failed to remove Unix socket: %v\n", err) + } +} diff --git a/cmd/traceectl/pkg/mock/stream_server.go b/cmd/traceectl/pkg/mock/stream_server.go new file mode 100644 index 000000000000..e69db107da21 --- /dev/null +++ b/cmd/traceectl/pkg/mock/stream_server.go @@ -0,0 +1,64 @@ +package mock + +import ( + "sort" + "strings" + "time" + + pb "github.com/aquasecurity/tracee/api/v1beta1" +) + +func (s *MockServiceServer) StreamEvents(req *pb.StreamEventsRequest, stream pb.TraceeService_StreamEventsServer) error { + mockEvents := CreateEventsFromPolicies(req.Policies) + for _, event := range mockEvents { + if err := stream.Send(event); err != nil { + return err + } + } + time.Sleep(100 * time.Millisecond) + return nil +} +func generateEvent(policy []string) *pb.Event { + return &pb.Event{ + Policies: &pb.Policies{Matched: policy}, + } +} +func CreateEventsFromPolicies(policies []string) []*pb.StreamEventsResponse { + if len(policies) == 0 { + return []*pb.StreamEventsResponse{ + {Event: generateEvent([]string{""})}, + } + } + sort.Strings(policies) + var results []*pb.StreamEventsResponse + combinations := generateCombinations(policies) + sort.SliceStable(combinations, func(i, j int) bool { + if len(combinations[i]) != len(combinations[j]) { + return len(combinations[i]) < len(combinations[j]) + } + return strings.Join(combinations[i], ",") < strings.Join(combinations[j], ",") + }) + for _, combo := range combinations { + results = append(results, &pb.StreamEventsResponse{ + Event: generateEvent(combo), + }) + } + + return results +} +func generateCombinations(policies []string) [][]string { + var result [][]string + n := len(policies) + var helper func(start int, combo []string) + helper = func(start int, combo []string) { + if len(combo) > 0 { + combinationCopy := append([]string{}, combo...) + result = append(result, combinationCopy) + } + for i := start; i < n; i++ { + helper(i+1, append(combo, policies[i])) + } + } + helper(0, []string{}) + return result +} diff --git a/docs/docs/traceectl/commands/event.md b/docs/docs/traceectl/commands/event.md new file mode 100644 index 000000000000..247b3643edfa --- /dev/null +++ b/docs/docs/traceectl/commands/event.md @@ -0,0 +1,83 @@ +# Event Command Usage + +The `event` command in **TraceeCtl** is used for managing events within Tracee. It allows you to list, describe, enable, and disable various event types that Tracee can capture. Below is the usage guide for the `event` command and its subcommands. + +## Usage + +The `event` command is structured as follows: + +```sh +traceectl event [subcommand] [flags] +``` + +## Subcommands + +- **list**: Lists all available event definitions (built-in and plugin-defined), providing a brief summary of each. + + ```sh + traceectl event list --format [json|table|template] + ``` + + - **`--format`** (`-f`): Specifies the output format (default is `table`). Supported formats are `json`, `table`, and `template`. + +- **describe**: Retrieves detailed information about a specific event, including its fields, types, and other metadata. + + ```sh + traceectl event describe --format [json|table|template] + ``` + + - **``**: The name of the event to describe. + - **`--format`** (`-f`): Specifies the output format (default is `table`). + +- **enable**: Enables capturing of a specific event type in Tracee. + + ```sh + traceectl event enable + ``` + + - **``**: The name of the event to enable. + +- **disable**: Disables capturing of a specific event type in Tracee. + + ```sh + traceectl event disable + ``` + + - **``**: The name of the event to disable. + +## Flags + +- **`--format`** (`-f`): Available with the `list` and `describe` subcommands. It specifies the format for the output. Supported values are: + - `json`: Outputs event details in JSON format. + - `table`: Outputs event details in a tabular view. + - `template`: Uses a custom template for formatting the output. + +## Examples + +- **List All Events in JSON Format** + + ```sh + traceectl event list --format json + ``` + +- **Describe an Event** + + ```sh + traceectl event describe execve --format table + ``` + +- **Enable an Event** + + ```sh + traceectl event enable execve + ``` + +- **Disable an Event** + + ```sh + traceectl event disable execve + ``` + +## Summary + +The `event` command in TraceeCtl is a powerful tool for managing Tracee's event capabilities. Use the `list`, `describe`, `enable`, and `disable` subcommands to gain detailed insight and control over the events Tracee monitors. diff --git a/docs/docs/traceectl/commands/metrics.md b/docs/docs/traceectl/commands/metrics.md new file mode 100644 index 000000000000..e95e5d974bc9 --- /dev/null +++ b/docs/docs/traceectl/commands/metrics.md @@ -0,0 +1,23 @@ +# Metrics Command Usage + +The `metrics` command in **TraceeCtl** provides information about Tracee's performance and resource usage metrics. This command is helpful for monitoring how Tracee is functioning in real-time. + +## Usage + +The `metrics` command is structured as follows: + +```sh +traceectl metrics +``` + +## Examples + +- **Display Metrics in Table Format** + + ```sh + traceectl metrics + ``` + +## Summary + +The `metrics` command in TraceeCtl allows users to obtain insights into Tracee's operational metrics, including event counts and resource usage. The command supports a table output format to suit different reporting needs. diff --git a/docs/docs/traceectl/commands/stream.md b/docs/docs/traceectl/commands/stream.md new file mode 100644 index 000000000000..6ac7c1c946cf --- /dev/null +++ b/docs/docs/traceectl/commands/stream.md @@ -0,0 +1,35 @@ +# Stream Command Usage + +The `stream` command in **TraceeCtl** allows users to stream events directly from Tracee in real time. This command provides flexible output formats for better integration and readability. + +## Usage + +The `stream` command is structured as follows: + +```sh +traceectl stream [policies...] [flags] +``` + +## Flags + +- **`--format`** (`-f`): Specifies the format for the output. Supported values are: + - `json`: Outputs event details in JSON format. + - `table`: Outputs event details in a tabular view. + +## Examples + +- **Stream Events in JSON Format** + + ```sh + traceectl stream --format json + ``` + +- **Stream Events in Table Format** + + ```sh + traceectl stream --format table + ``` + +## Summary + +The `stream` command in TraceeCtl is a useful tool for monitoring Tracee events in real time, with options for JSON or table output. Use this command to keep track of activities and ensure comprehensive observability for your system. diff --git a/docs/docs/traceectl/commands/version.md b/docs/docs/traceectl/commands/version.md new file mode 100644 index 000000000000..bd869af84279 --- /dev/null +++ b/docs/docs/traceectl/commands/version.md @@ -0,0 +1,28 @@ +# Version Command + +The `version` command in **TraceeCtl** provides detailed information about the current version of the tool. This includes the version number, build date, and other relevant metadata. + +## Usage + +To display the version information, use the following command: + +``` bash +traceectl version +``` + +This command will output details such as: + +- **Version Number**: The current version of TraceeCtl. +- **Commit Hash**: The Git commit hash associated with the current build (if applicable). + +### Example Output + +``` bash +v0.22.0-96-gaab269e885 +``` + +### Summary + +- **`traceectl version`**: Displays detailed version information. + +Use this command to verify your version or to gather information for troubleshooting purposes. diff --git a/docs/docs/traceectl/flags/format.md b/docs/docs/traceectl/flags/format.md new file mode 100644 index 000000000000..c8ff35b13b7e --- /dev/null +++ b/docs/docs/traceectl/flags/format.md @@ -0,0 +1,29 @@ +# `format` Flag + +The `--format` flag in **TraceeCtl** is used to specify the output format for certain commands. Currently, this flag supports the following values for the `stream`, `event list`, and `event describe` commands: + +- **`json`**: Outputs the data in JSON format, which is useful for automated processing or integration with other tools that consume JSON. + + Example: + + ```sh + traceectl event list --format json + ``` + + In this example, the command lists all available events and outputs them in JSON format. + +- **`table`**: Outputs the data in a tabular format, which is easier to read for users viewing the output directly in the terminal. + + Example: + + ```sh + traceectl stream --format table + ``` + + In this example, the command streams events from Tracee and displays them in a table format, making it more human-readable. + +The `--format` flag is helpful for customizing the output to meet different requirements, whether for readability or integration with other tools. + +## Default Format + +The default format for the `--format` flag is **table**. If no format is specified, the output will be displayed in a tabular format, which is more human-readable for most users. diff --git a/docs/docs/traceectl/flags/output.md b/docs/docs/traceectl/flags/output.md new file mode 100644 index 000000000000..59125ffab997 --- /dev/null +++ b/docs/docs/traceectl/flags/output.md @@ -0,0 +1,23 @@ +# `output` Flag + +The `--output` flag is used to specify the destination for the command's output. This flag can be set to **stdout** or a file location. + +- **stdout**: This is the default output destination, which means that the command's output will be displayed on the terminal. This is convenient for users who want to see immediate results directly in their console. + + Example: + + ```sh + traceectl stream --output stdout + ``` + + In this example, the command outputs the streamed events to the terminal. + +- **File Output**: You can use the `--output` flag to direct the output to a specific file. This is useful if you want to save the output for later analysis or for documentation purposes. + + Example: + + ```sh + traceectl stream --output file:/path/to/output.txt + ``` + + In this example, the command saves the streamed events to the file located at `/path/to/output.txt`. This is especially helpful for logging purposes or when working with large amounts of data that need to be stored for further processing. diff --git a/docs/docs/traceectl/flags/server.md b/docs/docs/traceectl/flags/server.md new file mode 100644 index 000000000000..416dfd36a997 --- /dev/null +++ b/docs/docs/traceectl/flags/server.md @@ -0,0 +1,23 @@ +# `server` Flag + +The `--server` flag in **TraceeCtl** is used to specify the connection type that TraceeCtl should use to communicate with the Tracee server. This connection type can be either **Unix socket** or **TCP**. + +- **Unix Socket**: This type of connection is generally used for local inter-process communication. It provides a secure and efficient means to connect to Tracee when both client and server are on the same machine. + + Example: + + ```sh + traceectl --server unix:/unix/socket/path.sock + ``` + + In this example, `unix:/unix/socket/path.sock` is the Unix socket path where the Tracee server is listening. Using Unix sockets is beneficial for security and performance since it avoids the overhead associated with network communication. + +- **TCP**: This type of connection allows TraceeCtl to communicate with the Tracee server over a network. It is useful when TraceeCtl and Tracee are running on different machines or when you need to connect to a remote Tracee instance. + + Example: + + ```sh + traceectl --server tcp:4466 + ``` + + In this example, `tcp:4466` is the address and port of the Tracee server. This is a typical setup for remote monitoring or when the server and client need to be distributed across different hosts. diff --git a/docs/docs/traceectl/index.md b/docs/docs/traceectl/index.md new file mode 100644 index 000000000000..bbbf44febde1 --- /dev/null +++ b/docs/docs/traceectl/index.md @@ -0,0 +1,15 @@ +# TraceeCtl - Client Service for Tracee + +**TraceeCtl** is a command-line interface (CLI) tool designed as a client service for [Tracee](https://github.com/aquasecurity/tracee), Aqua Security's open-source runtime security solution. Tracee provides real-time, powerful observability for Linux environments by monitoring system calls, events, and more. TraceeCtl is built to simplify interactions with Tracee, making it easier for users to manage, monitor, and gather security insights. + +## Overview + +TraceeCtl acts as a controller for Tracee, allowing users to: + +- **Stream Events**: Continuously stream security events from Tracee, with options to format the output as JSON, tables, or custom templates. +- **List Available Events**: Display the available events that Tracee can capture, providing essential insights into runtime activities. +- **Query Metrics**: Access various metrics related to Tracee, including event counts, errors, and more. + +## Installation and Usage + +To get started with TraceeCtl, go over the [Installation and Usage page](./usage.md) diff --git a/docs/docs/traceectl/usage.md b/docs/docs/traceectl/usage.md new file mode 100644 index 000000000000..03b78e1b1f12 --- /dev/null +++ b/docs/docs/traceectl/usage.md @@ -0,0 +1,73 @@ +# TraceeCtl Installation and Usage Guide + +## Installation + +To use **TraceeCtl**, you first need to compile and install the tool. Follow these steps to get started: + +1. **Clone the Repository** + + Begin by cloning the Tracee repository to your local machine and navigating to traceectl: + + ``` bash + git clone [URL-to-your-repo](https://github.com/aquasecurity/tracee.git) + cd cmd/traceectl + ``` + +2. **Build and Install** + + Compile and install TraceeCtl using the following commands: + + ``` bash + make build + make install + ``` + +## Configuring Tracee for TraceeCtl + +To use TraceeCtl effectively, you need to configure Tracee so that it can communicate with TraceeCtl over a Unix socket. This can be done by running Tracee with the correct gRPC settings: + +1. **Run Tracee with gRPC Unix Socket** + + Use the following command to start Tracee with gRPC support over a Unix socket: + + ``` bash + tracee --grpc-listen-addr unix:/var/run/tracee.sock + ``` + + This command sets up Tracee to listen for incoming connections from TraceeCtl at the specified Unix socket path (`/var/run/tracee.sock`). Ensure that this socket path is accessible and not blocked by permissions or other constraints. + +## Usage + +Once TraceeCtl is installed and Tracee is running, you can use various commands to interact with Tracee. Below are the main commands provided by TraceeCtl: + +- Stream Events: traceectl stream + +- Events management: traceectl event + +- Retrieve Metrics: traceectl metrics + +- Check Version: traceectl version + +For more info about the TraceeCtl command please refer to the appoint command documentation + +## Flags + +- server: Specifies the connection type, either unix or tcp. + + ``` bash + traceectl --server unix:/unix/socket/path.sock + ``` + +- output: Defines the output destination, such as stdout or a file. + + ``` bash + traceectl stream --output file:/path/to/output.txt + ``` + +For more info about the TraceeCtl flags please refer to the appoint flag documentation + +## Summary + +- **Install TraceeCtl** by cloning the repository, building, and installing it with `make`. +- **Configure Tracee** by running it with the appropriate gRPC Unix socket settings. +- **Use TraceeCtl** to interact with Tracee via commands like `stream`, `event`, `metrics`, and `version`.