diff --git a/api/proto/internal/rpc/audit.proto b/api/proto/internal/rpc/audit.proto index 7494a5c..3a1aab6 100644 --- a/api/proto/internal/rpc/audit.proto +++ b/api/proto/internal/rpc/audit.proto @@ -36,8 +36,17 @@ message RemoveFileSinkResponse { bool SinkGotRemoved = 1; } +message ListSinksRequest { + +} + +message ListSinksResponse { + repeated string sinks = 1; +} + service Audit { rpc WatchEvents (WatchEventsRequest) returns (stream inetmock.audit.EventEntity); rpc RegisterFileSink (RegisterFileSinkRequest) returns (RegisterFileSinkResponse); rpc RemoveFileSink (RemoveFileSinkRequest) returns (RemoveFileSinkResponse); + rpc ListSinks(ListSinksRequest) returns (ListSinksResponse); } \ No newline at end of file diff --git a/cmd/imctl/audit_file.go b/cmd/imctl/audit_file.go deleted file mode 100644 index 132067f..0000000 --- a/cmd/imctl/audit_file.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - - "github.com/spf13/cobra" - "gitlab.com/inetmock/inetmock/internal/rpc" - "google.golang.org/grpc" -) - -var ( - addFileCmd = &cobra.Command{ - Use: "addFile", - Short: "subscribe events to a file", - Args: cobra.ExactArgs(1), - RunE: runAddFile, - } - - removeFileCmd = &cobra.Command{ - Use: "removeFile", - Short: "remove file subscription", - Args: cobra.ExactArgs(1), - RunE: runRemoveFile, - } - - readFileCmd = &cobra.Command{ - Use: "readFile", - Short: "reads an audit file and prints the events", - Args: cobra.ExactArgs(1), - RunE: runReadFile, - } -) - -func runAddFile(_ *cobra.Command, args []string) (err error) { - var conn *grpc.ClientConn - - if conn, err = grpc.Dial(inetMockSocketPath, grpc.WithInsecure()); err != nil { - fmt.Printf("Failed to connecto INetMock socket: %v\n", err) - os.Exit(10) - } - - auditClient := rpc.NewAuditClient(conn) - ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout) - defer cancel() - - _, err = auditClient.RegisterFileSink(ctx, &rpc.RegisterFileSinkRequest{TargetPath: args[0]}) - return -} - -func runRemoveFile(_ *cobra.Command, args []string) (err error) { - auditClient := rpc.NewAuditClient(conn) - ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout) - defer cancel() - - _, err = auditClient.RemoveFileSink(ctx, &rpc.RemoveFileSinkRequest{TargetPath: args[0]}) - return -} - -func runReadFile(_ *cobra.Command, args []string) (err error) { - return -} diff --git a/cmd/imctl/audit_sinks.go b/cmd/imctl/audit_sinks.go new file mode 100644 index 0000000..75aefb5 --- /dev/null +++ b/cmd/imctl/audit_sinks.go @@ -0,0 +1,118 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + + "github.com/spf13/cobra" + "gitlab.com/inetmock/inetmock/internal/format" + "gitlab.com/inetmock/inetmock/internal/rpc" + "gitlab.com/inetmock/inetmock/pkg/audit" +) + +var ( + listSinksCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls", "dir"}, + Short: "List all subscribed sinks", + RunE: runListSinks, + } + addFileCmd = &cobra.Command{ + Use: "add-file", + Aliases: []string{"add"}, + Short: "subscribe events to a file", + Args: cobra.ExactArgs(1), + RunE: runAddFile, + } + + removeFileCmd = &cobra.Command{ + Use: "remove-file", + Aliases: []string{"rm", "del"}, + Short: "remove file subscription", + Args: cobra.ExactArgs(1), + RunE: runRemoveFile, + } + + readFileCmd = &cobra.Command{ + Use: "read-file", + Aliases: []string{"cat"}, + Short: "reads an audit file and prints the events", + Args: cobra.ExactArgs(1), + RunE: runReadFile, + } +) + +type printableSink struct { + Name string +} + +func runListSinks(*cobra.Command, []string) (err error) { + auditClient := rpc.NewAuditClient(conn) + ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout) + defer cancel() + + var resp *rpc.ListSinksResponse + if resp, err = auditClient.ListSinks(ctx, &rpc.ListSinksRequest{}); err != nil { + return + } + + var sinks []printableSink + for _, s := range resp.Sinks { + sinks = append(sinks, printableSink{Name: s}) + } + + writer := format.Writer(outputFormat, os.Stdout) + err = writer.Write(sinks) + return +} + +func runAddFile(_ *cobra.Command, args []string) (err error) { + auditClient := rpc.NewAuditClient(conn) + ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout) + defer cancel() + + _, err = auditClient.RegisterFileSink(ctx, &rpc.RegisterFileSinkRequest{TargetPath: args[0]}) + return +} + +func runRemoveFile(_ *cobra.Command, args []string) (err error) { + auditClient := rpc.NewAuditClient(conn) + ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout) + defer cancel() + + _, err = auditClient.RemoveFileSink(ctx, &rpc.RemoveFileSinkRequest{TargetPath: args[0]}) + return +} + +func runReadFile(_ *cobra.Command, args []string) (err error) { + if len(args) != 1 { + return errors.New("expected only 1 argument") + } + + var reader io.ReadCloser + if reader, err = os.Open(args[0]); err != nil { + return + } + + eventReader := audit.NewEventReader(reader) + var ev audit.Event + + for err == nil { + if ev, err = eventReader.Read(); err == nil { + var jsonBytes []byte + if jsonBytes, err = json.Marshal(ev); err == nil { + fmt.Println(string(jsonBytes)) + } + } + } + + if errors.Is(err, io.EOF) { + err = nil + } + + return +} diff --git a/cmd/imctl/endpoints.go b/cmd/imctl/endpoints.go index 592ef9b..622dd4d 100644 --- a/cmd/imctl/endpoints.go +++ b/cmd/imctl/endpoints.go @@ -25,7 +25,7 @@ var ( ) type printableEndpoint struct { - Id string + ID string Name string Handler string ListenAddress string @@ -34,7 +34,7 @@ type printableEndpoint struct { func fromEndpoint(ep *rpc.Endpoint) *printableEndpoint { return &printableEndpoint{ - Id: ep.Id, + ID: ep.Id, Name: ep.Name, Handler: ep.Handler, ListenAddress: ep.ListenAddress, diff --git a/cmd/imctl/main.go b/cmd/imctl/main.go index 1ca965f..93a0803 100644 --- a/cmd/imctl/main.go +++ b/cmd/imctl/main.go @@ -2,6 +2,8 @@ package main import ( "context" + "fmt" + "os" "os/user" "time" @@ -20,7 +22,6 @@ var ( ) func main() { - endpointsCmd.AddCommand(getEndpoints) handlerCmd.AddCommand(getHandlersCmd) healthCmd.AddCommand(generalHealthCmd, containerHealthCmd) @@ -29,7 +30,8 @@ func main() { WithCommands(endpointsCmd, handlerCmd, healthCmd, auditCmd). WithInitTasks(func(_ *cobra.Command, _ []string) (err error) { return initGRPCConnection() - }) + }). + WithLogger() cliApp.RootCommand().PersistentFlags().StringVar(&inetMockSocketPath, "socket-path", "unix:///var/run/inetmock.sock", "Path to the INetMock socket file") cliApp.RootCommand().PersistentFlags().StringVarP(&outputFormat, "format", "f", "table", "Output format to use. Possible values: table, json, yaml") @@ -42,16 +44,15 @@ func main() { currentUser = uuid.New().String() } - watchEventsCmd.PersistentFlags().StringVar( - &listenerName, - "listener-name", - currentUser, - "set listener name - defaults to the current username, if the user cannot be determined a random UUID will be used", - ) - auditCmd.AddCommand(watchEventsCmd, addFileCmd, removeFileCmd) + hostname := "." + if hn, err := os.Hostname(); err == nil { + hostname = hn + } + + watchEventsCmd.PersistentFlags().StringVar(&listenerName, "listener-name", fmt.Sprintf("%s\\%s is watching", hostname, currentUser), "set listener name - defaults to the current username, if the user cannot be determined a random UUID will be used") + auditCmd.AddCommand(listSinksCmd, watchEventsCmd, addFileCmd, removeFileCmd, readFileCmd) cliApp.MustRun() - } func initGRPCConnection() (err error) { diff --git a/internal/rpc/audit_server.go b/internal/rpc/audit_server.go index 96bdc63..0fcfa8a 100644 --- a/internal/rpc/audit_server.go +++ b/internal/rpc/audit_server.go @@ -17,6 +17,12 @@ type auditServer struct { eventStream audit.EventStream } +func (a *auditServer) ListSinks(context.Context, *ListSinksRequest) (*ListSinksResponse, error) { + return &ListSinksResponse{ + Sinks: a.eventStream.Sinks(), + }, nil +} + func (a *auditServer) WatchEvents(req *WatchEventsRequest, srv Audit_WatchEventsServer) (err error) { a.logger.Info("watcher attached", zap.String("name", req.WatcherName)) err = a.eventStream.RegisterSink(sink.NewGRPCSink(srv.Context(), req.WatcherName, func(ev audit.Event) {