From 9f3f30151dc6de5de91c067bd66979d95c9afada Mon Sep 17 00:00:00 2001 From: ShohamBit Date: Mon, 18 Nov 2024 09:04:06 +0000 Subject: [PATCH 01/45] Add traceectl to tracee --- cmd/traceectl/cmd/event.go | 192 ++++++++++++++++++ cmd/traceectl/cmd/event_test.go | 75 +++++++ cmd/traceectl/cmd/plugin.go | 59 ++++++ cmd/traceectl/cmd/plugin_test.go | 55 +++++ cmd/traceectl/cmd/policy.go | 100 +++++++++ cmd/traceectl/cmd/policy_test.go | 55 +++++ cmd/traceectl/cmd/root.go | 167 +++++++++++++++ cmd/traceectl/cmd/root_test.go | 66 ++++++ cmd/traceectl/cmd/stream.go | 151 ++++++++++++++ cmd/traceectl/cmd/stream_test.go | 72 +++++++ cmd/traceectl/go.mod | 27 +++ cmd/traceectl/go.sum | 45 ++++ cmd/traceectl/main.go | 10 + cmd/traceectl/pkg/client/client.go | 98 +++++++++ cmd/traceectl/pkg/client/diagnostic.go | 47 +++++ cmd/traceectl/pkg/client/service.go | 61 ++++++ .../pkg/cmd/formatter/JSONFormatter.go | 21 ++ .../pkg/cmd/formatter/TableFormatter.go | 144 +++++++++++++ cmd/traceectl/pkg/cmd/formatter/formatter.go | 84 ++++++++ cmd/traceectl/pkg/cmd/printer/printer.go | 103 ++++++++++ cmd/traceectl/pkg/cmd/printer/printer_test.go | 68 +++++++ cmd/traceectl/pkg/cmd/test/test.go | 59 ++++++ cmd/traceectl/pkg/mock/diagnostic_server.go | 17 ++ cmd/traceectl/pkg/mock/event_server.go | 28 +++ cmd/traceectl/pkg/mock/root_server.go | 12 ++ cmd/traceectl/pkg/mock/server.go | 79 +++++++ cmd/traceectl/pkg/mock/stream_server.go | 102 ++++++++++ cmd/traceectl/pkg/models/models.go | 8 + 28 files changed, 2005 insertions(+) create mode 100644 cmd/traceectl/cmd/event.go create mode 100644 cmd/traceectl/cmd/event_test.go create mode 100644 cmd/traceectl/cmd/plugin.go create mode 100644 cmd/traceectl/cmd/plugin_test.go create mode 100644 cmd/traceectl/cmd/policy.go create mode 100644 cmd/traceectl/cmd/policy_test.go create mode 100644 cmd/traceectl/cmd/root.go create mode 100644 cmd/traceectl/cmd/root_test.go create mode 100644 cmd/traceectl/cmd/stream.go create mode 100644 cmd/traceectl/cmd/stream_test.go create mode 100644 cmd/traceectl/go.mod create mode 100644 cmd/traceectl/go.sum create mode 100644 cmd/traceectl/main.go create mode 100644 cmd/traceectl/pkg/client/client.go create mode 100644 cmd/traceectl/pkg/client/diagnostic.go create mode 100644 cmd/traceectl/pkg/client/service.go create mode 100644 cmd/traceectl/pkg/cmd/formatter/JSONFormatter.go create mode 100644 cmd/traceectl/pkg/cmd/formatter/TableFormatter.go create mode 100644 cmd/traceectl/pkg/cmd/formatter/formatter.go create mode 100644 cmd/traceectl/pkg/cmd/printer/printer.go create mode 100644 cmd/traceectl/pkg/cmd/printer/printer_test.go create mode 100644 cmd/traceectl/pkg/cmd/test/test.go create mode 100644 cmd/traceectl/pkg/mock/diagnostic_server.go create mode 100644 cmd/traceectl/pkg/mock/event_server.go create mode 100644 cmd/traceectl/pkg/mock/root_server.go create mode 100644 cmd/traceectl/pkg/mock/server.go create mode 100644 cmd/traceectl/pkg/mock/stream_server.go create mode 100644 cmd/traceectl/pkg/models/models.go diff --git a/cmd/traceectl/cmd/event.go b/cmd/traceectl/cmd/event.go new file mode 100644 index 000000000000..8e0301a239ba --- /dev/null +++ b/cmd/traceectl/cmd/event.go @@ -0,0 +1,192 @@ +package cmd + +//TODO: +import ( + "context" + + 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/aquasecurity/tracee/cmd/traceectl/pkg/cmd/printer" + "github.com/spf13/cobra" +) + +var eventFormatFlag string +var eventOutputFlag string + +// main command +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 and run them. + `, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // Check if args are provided + if len(args) == 0 { + cmd.PrintErrln("Error: no event names provided. Please specify at least one event to enable.") + return // Exit if no arguments + } + }, +} + +func init() { + eventCmd.AddCommand(listEventCmd) + eventCmd.AddCommand(describeEventCmd) + eventCmd.AddCommand(enableEventCmd) + eventCmd.AddCommand(disableEventCmd) + eventCmd.AddCommand(runEventCmd) + + runEventCmd.PersistentFlags().StringVar(&runCmdArgs, "args", "", "Arguments for the event") + + listEventCmd.Flags().StringVarP(&eventFormatFlag, "format", "f", formatter.FormatTable, "Output format (json|table|template) ") + listEventCmd.Flags().StringVarP(&eventOutputFlag, "output", "o", "stdout", "Output destination ") + + describeEventCmd.Flags().StringVarP(&eventFormatFlag, "format", "f", formatter.FormatTable, "Output format (json|table|template) ") + describeEventCmd.Flags().StringVarP(&eventOutputFlag, "output", "o", "stdout", "Output destination ") +} + +// Sub commands +// list +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) + }, +} + +// describe +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) + }, +} + +// enable +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]) + }, +} + +// disable +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]) + }, +} + +// run +var runCmdArgs string +var runEventCmd = &cobra.Command{ + Use: "run [--args ]", + Short: "run event", + Long: `Manually triggers a user-space event.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + //runEvents(cmd, args) + }, +} + +func listEvents(cmd *cobra.Command, args []string) { + //create service client + 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 + + } + //display event definitions + //TODO: add support for different outputs and formats + format, err := formatter.New(eventFormatFlag, eventOutputFlag, cmd) + if err != nil { + cmd.PrintErrln("Error creating formatter: ", err) + return + } + //show events + printer.ListEvents(format, args, response) +} +func getEventDescriptions(cmd *cobra.Command, args []string) { + //create service client + 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 + + } + //display event definitions + //TODO: add support for different outputs and formats + format, err := formatter.New(eventFormatFlag, eventOutputFlag, cmd) + if err != nil { + cmd.PrintErrln("Error creating formatter: ", err) + return + } + //show events + printer.DescribeEvent(format, args, response) + +} +func enableEvents(cmd *cobra.Command, eventName string) { + // Create Tracee gRPC client + var traceeClient client.ServiceClient // tracee client + + if err := traceeClient.NewServiceClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return // Exit on error + } + + // Iterate over event names and enable each one + + _, 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) { + // Create Tracee gRPC client + var traceeClient client.ServiceClient + if err := traceeClient.NewServiceClient(serverInfo); err != nil { + cmd.PrintErrln("Error creating client: ", err) + return // Exit on error + } + _, 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..ab33decd3002 --- /dev/null +++ b/cmd/traceectl/cmd/event_test.go @@ -0,0 +1,75 @@ +package cmd + +import ( + "fmt" + "testing" + + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/cmd/test" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/models" +) + +func TestEvent(t *testing.T) { + eventTests := []models.TestCase{ + { + TestName: "event", + OutputSlice: []string{"event"}, + ExpectedPrinter: nil, + ExpectedError: fmt.Errorf("requires at least 1 arg(s), only received 0"), + }, + + //event list + { + TestName: "events list", + OutputSlice: []string{"event", "list", "--format", "json"}, + ExpectedPrinter: "", + ExpectedError: nil, + }, + + //event describe + { + 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, + }, + //event enable + { + TestName: "No events enable", + OutputSlice: []string{"event", "enable"}, + ExpectedPrinter: nil, + ExpectedError: fmt.Errorf("accepts 1 arg(s), received 0"), // Update expected output + + }, + { + TestName: "enable event", + OutputSlice: []string{"event", "enable", "event"}, + ExpectedPrinter: "Enabled event: event", + ExpectedError: nil, + }, + //event disable + { + TestName: "No disable events", + OutputSlice: []string{"event", "disable"}, + ExpectedPrinter: nil, + ExpectedError: fmt.Errorf("accepts 1 arg(s), received 0"), // Update expected output + }, + { + TestName: "disable event", + OutputSlice: []string{"event", "disable", "event"}, + ExpectedPrinter: "Disabled event: event", + ExpectedError: nil, + }, + //event run + //TODO: add test when support run is added + } + + for _, testCase := range eventTests { + t.Run(testCase.TestName, func(t *testing.T) { test.TestCommand(t, testCase, rootCmd) }) + } +} diff --git a/cmd/traceectl/cmd/plugin.go b/cmd/traceectl/cmd/plugin.go new file mode 100644 index 000000000000..24ac6941cfd7 --- /dev/null +++ b/cmd/traceectl/cmd/plugin.go @@ -0,0 +1,59 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// pluginCmd represents the plugin command +var pluginCmd = &cobra.Command{ + Use: "plugin", + Short: "plugin management for traceectl", + Long: `Plugin Management: + - traceectl plugin install --name --repo + - traceectl plugin list + - traceectl plugin uninstall +`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("plugin called") + }, +} + +func init() { + pluginCmd.AddCommand(pluginInstallCmd) + pluginCmd.AddCommand(pluginListCmd) + pluginCmd.AddCommand(pluginUninstallCmd) +} + +var pluginInstallCmd = &cobra.Command{ + Use: "install", + Short: "install a plugin from a remote repository", + Long: `Install a plugin from a remote repository: + - traceectl plugin install --name --repo +`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("install called") + }, +} + +var pluginListCmd = &cobra.Command{ + Use: "list", + Short: "list installed plugins", + Long: `List all installed plugins.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("list called") + }, +} + +var pluginUninstallCmd = &cobra.Command{ + Use: "uninstall ", + Short: "uninstall a plugin", + Long: `Uninstalls a plugin by its name.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("uninstall called") + }, +} diff --git a/cmd/traceectl/cmd/plugin_test.go b/cmd/traceectl/cmd/plugin_test.go new file mode 100644 index 000000000000..4a1da846845e --- /dev/null +++ b/cmd/traceectl/cmd/plugin_test.go @@ -0,0 +1,55 @@ +package cmd + +// import ( +// "bytes" +// "testing" +// "time" + +// "github.com/aquasecurity/tracee/cmd/traceectl/pkg/mock" +// "github.com/aquasecurity/tracee/cmd/traceectl/pkg/models" +// ) + +// var pluginTests = []models.TestCase{ +// { +// Name: "No plugin subcommand", +// Args: []string{"plugin"}, +// ExpectedOutput: "Error: requires at least 1 arg(s), only received 0\n", // Update expected output +// }, +// } + +// func TestPluginCmd(t *testing.T) { +// // Start the mock server +// mockServer, err := mock.StartMockServer() +// if err != nil { +// t.Fatalf("Failed to start mock server: %v", err) +// } +// defer mockServer.Stop() // Ensure the server is stopped after the test + +// // Wait for the server to start +// time.Sleep(100 * time.Millisecond) + +// for _, test := range pluginTests { +// t.Run(test.Name, func(t *testing.T) { +// // Capture output +// var buf bytes.Buffer +// rootCmd := GetRootCmd() +// rootCmd.SetOut(&buf) +// rootCmd.SetErr(&buf) + +// // Set arguments for the test +// rootCmd.SetArgs(test.Args) + +// // Execute the command +// if err := rootCmd.Execute(); err != nil { +// t.Error(t, err) +// } + +// // Validate output and error (if any) +// output := buf.String() + +// if output != test.ExpectedOutput { +// t.Errorf("Expected output: %s, got: %s", test.ExpectedOutput, output) +// } +// }) +// } +// } diff --git a/cmd/traceectl/cmd/policy.go b/cmd/traceectl/cmd/policy.go new file mode 100644 index 000000000000..0d07aad7fb1e --- /dev/null +++ b/cmd/traceectl/cmd/policy.go @@ -0,0 +1,100 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// policyCmd represents the policy command +var policyCmd = &cobra.Command{ + Use: "policy", + Short: "policy management for traceectl", + Long: `Policy Management: + - traceectl policy create + - traceectl policy describe + - traceectl policy list + - traceectl policy update + - traceectl policy delete + - traceectl policy enable + - traceectl policy disable +`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("policy called") + }, +} + +func init() { + policyCmd.AddCommand(createCmd) + policyCmd.AddCommand(describePolicyCmd) + policyCmd.AddCommand(listPolicyCmd) + policyCmd.AddCommand(updateCmd) + policyCmd.AddCommand(deleteCmd) + policyCmd.AddCommand(enableCmd) + policyCmd.AddCommand(disableCmd) +} + +var createCmd = &cobra.Command{ + Use: "create ", + Short: "create a policy", + Long: `Creates a new policy from the YAML file specified by < policy_file > .`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("create called") + }, +} +var describePolicyCmd = &cobra.Command{ + Use: "describe ", + Short: "describe a policy", + Long: `Retrieves the details of a specific policy by its name.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("describe called") + }, +} + +var listPolicyCmd = &cobra.Command{ + Use: "list", + Short: "list policies", + Long: `Lists all available policies, providing a brief summary of each.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("list called") + }, +} + +var updateCmd = &cobra.Command{ + Use: "update ", + Short: "update a policy", + Long: `Updates an existing policy from the YAML file specified by < updated_policy_file > .`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("update called") + }, +} + +var deleteCmd = &cobra.Command{ + Use: "delete ", + Short: "delete a policy", + Long: `Removes a policy by its name.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("delete called") + }, +} + +var enableCmd = &cobra.Command{ + Use: "enable ", + Short: "enable a policy", + Long: `Enables a policy by its name.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("enable called") + }, +} + +var disableCmd = &cobra.Command{ + Use: "disable ", + Short: "disable a policy", + Long: `Disables a policy by its name.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("disable called") + }, +} diff --git a/cmd/traceectl/cmd/policy_test.go b/cmd/traceectl/cmd/policy_test.go new file mode 100644 index 000000000000..ac25b3344b70 --- /dev/null +++ b/cmd/traceectl/cmd/policy_test.go @@ -0,0 +1,55 @@ +package cmd + +// import ( +// "bytes" +// "testing" +// "time" + +// "github.com/aquasecurity/tracee/cmd/traceectl/pkg/mock" +// "github.com/aquasecurity/tracee/cmd/traceectl/pkg/models" +// ) + +// var policyTests = []models.TestCase{ +// { +// Name: "No policy subcommand", +// Args: []string{"policy"}, +// ExpectedOutput: "Error: requires at least 1 arg(s), only received 0\n", // Update expected output +// }, +// } + +// func TestPolicyCmd(t *testing.T) { +// // Start the mock server +// mockServer, err := mock.StartMockServer() +// if err != nil { +// t.Fatalf("Failed to start mock server: %v", err) +// } +// defer mockServer.Stop() // Ensure the server is stopped after the test + +// // Wait for the server to start +// time.Sleep(100 * time.Millisecond) + +// for _, test := range policyTests { +// t.Run(test.Name, func(t *testing.T) { +// // Capture output +// var buf bytes.Buffer +// rootCmd := GetRootCmd() +// rootCmd.SetOut(&buf) +// rootCmd.SetErr(&buf) + +// // Set arguments for the test +// rootCmd.SetArgs(test.Args) + +// // Execute the command +// if err := rootCmd.Execute(); err != nil { +// t.Error(t, err) +// } + +// // Validate output and error (if any) +// output := buf.String() + +// if output != test.ExpectedOutput { +// t.Errorf("Expected output: %s, got: %s", test.ExpectedOutput, output) +// } +// }) +// } +// } diff --git a/cmd/traceectl/cmd/root.go b/cmd/traceectl/cmd/root.go new file mode 100644 index 000000000000..7993a047e2cd --- /dev/null +++ b/cmd/traceectl/cmd/root.go @@ -0,0 +1,167 @@ +package cmd + +import ( + "context" + "os" + + pb "github.com/aquasecurity/tracee/api/v1beta1" + "github.com/aquasecurity/tracee/cmd/traceectl/pkg/client" + + "github.com/spf13/cobra" +) + +var formatFlag string +var outputFlag string +var serverFlag 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.", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, + } +) + +func init() { + + // commands + rootCmd.AddCommand(streamCmd) + rootCmd.AddCommand(eventCmd) + rootCmd.AddCommand(pluginCmd) + rootCmd.AddCommand(policyCmd) + + //other commends + rootCmd.AddCommand(connectCmd) + rootCmd.AddCommand(metricsCmd) + rootCmd.AddCommand(diagnoseCmd) + rootCmd.AddCommand(logsCmd) + rootCmd.AddCommand(statusCmd) + rootCmd.AddCommand(configCmd) + rootCmd.AddCommand(versionCmd) + + //one global flag for server connection(connection Type: tcp or unix socket) + //no default for tcp, only for unix socket + //for tcp + //for unix socket + rootCmd.PersistentFlags().StringVar(&serverInfo.ADDR, "server", client.SOCKET, `Server connection path or address. + for unix socket (default: /tmp/tracee.sock) + for tcp `) + +} + +var connectCmd = &cobra.Command{ + Use: "connect []", + Short: "Connect to the server", + Long: "Connects to a stream and displays events in real time.", + Run: func(cmd *cobra.Command, args []string) { + }, +} +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 diagnoseCmd = &cobra.Command{ + Use: "diagnose [--component ]", + Short: "Collect diagnostic information to help troubleshoot issues", + Long: "Collects diagnostic information to help troubleshoot issues.", + Run: func(cmd *cobra.Command, args []string) { + }, +} + +var logsCmd = &cobra.Command{ + Use: "logs [--filter ]", + Short: "Display log messages from Tracee", + Long: "Displays log messages from Tracee, optionally filtered.", + Run: func(cmd *cobra.Command, args []string) { + }, +} +var statusCmd = &cobra.Command{ + Use: "status", + Short: "Shows the status of the Tracee Daemon and its components", + Long: "Shows the status of the Tracee Daemon and its components.", + Run: func(cmd *cobra.Command, args []string) { + }, +} + +var configCmd = &cobra.Command{ + Use: "config [set|get|update] [