From 9fee039d93dd5ba0773422388c2210672098d306 Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Sun, 24 Dec 2023 03:54:49 -0800 Subject: [PATCH] Add PipeCreateErrorCallback to handle pipe creation errors (#290) * Add PipeCreateErrorCallback to handle pipe creation errors * Add test for FailtobanPipeCreateFail * Fix missing newline at end of file in pluginbase.go --- cmd/sshpiperd/daemon.go | 8 + cmd/sshpiperd/internal/plugin/chain.go | 9 + cmd/sshpiperd/internal/plugin/grpc.go | 14 +- e2e/failtoban_test.go | 153 +++++++++++ libplugin/plugin.pb.go | 344 +++++++++++++++++-------- libplugin/plugin.proto | 27 +- libplugin/plugin_grpc.pb.go | 38 ++- libplugin/pluginbase.go | 16 ++ plugin/failtoban/main.go | 7 +- 9 files changed, 499 insertions(+), 117 deletions(-) diff --git a/cmd/sshpiperd/daemon.go b/cmd/sshpiperd/daemon.go index 4cb81c2f..dd04f7f6 100644 --- a/cmd/sshpiperd/daemon.go +++ b/cmd/sshpiperd/daemon.go @@ -196,9 +196,17 @@ func (d *daemon) run() error { case p = <-pipec: case err := <-errorc: log.Debugf("connection from %v establishing failed reason: %v", c.RemoteAddr(), err) + if d.config.PipeCreateErrorCallback != nil { + d.config.PipeCreateErrorCallback(c, err) + } + return case <-time.After(d.loginGraceTime): log.Debugf("pipe establishing timeout, disconnected connection from %v", c.RemoteAddr()) + if d.config.PipeCreateErrorCallback != nil { + d.config.PipeCreateErrorCallback(c, fmt.Errorf("pipe establishing timeout")) + } + return } diff --git a/cmd/sshpiperd/internal/plugin/chain.go b/cmd/sshpiperd/internal/plugin/chain.go index 2225327b..8b12e9ab 100644 --- a/cmd/sshpiperd/internal/plugin/chain.go +++ b/cmd/sshpiperd/internal/plugin/chain.go @@ -2,6 +2,7 @@ package plugin import ( "fmt" + "net" "github.com/google/uuid" log "github.com/sirupsen/logrus" @@ -157,5 +158,13 @@ func (cp *ChainPlugins) InstallPiperConfig(config *GrpcPluginConfig) error { } } + config.PipeCreateErrorCallback = func(conn net.Conn, err error) { + for _, p := range cp.pluginsCallback { + if p.PipeCreateErrorCallback != nil { + p.PipeCreateErrorCallback(conn, err) + } + } + } + return nil } diff --git a/cmd/sshpiperd/internal/plugin/grpc.go b/cmd/sshpiperd/internal/plugin/grpc.go index f51aabc0..d0d24622 100644 --- a/cmd/sshpiperd/internal/plugin/grpc.go +++ b/cmd/sshpiperd/internal/plugin/grpc.go @@ -22,8 +22,9 @@ import ( type GrpcPluginConfig struct { ssh.PiperConfig - PipeStartCallback func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) - PipeErrorCallback func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext, err error) + PipeCreateErrorCallback func(conn net.Conn, err error) + PipeStartCallback func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) + PipeErrorCallback func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext, err error) } type GrpcPlugin struct { @@ -130,6 +131,8 @@ func (g *GrpcPlugin) InstallPiperConfig(config *GrpcPluginConfig) error { config.PipeStartCallback = g.PipeStartCallback case "PipeError": config.PipeErrorCallback = g.PipeErrorCallback + case "PipeCreateError": + config.PipeCreateErrorCallback = g.PipeCreateErrorCallback default: return fmt.Errorf("unknown callback %s", c) } @@ -503,6 +506,13 @@ func (g *GrpcPlugin) BannerCallback(conn ssh.ConnMetadata, challengeCtx ssh.Chal return reply.GetMessage() } +func (g *GrpcPlugin) PipeCreateErrorCallback(conn net.Conn, err error) { + _, _ = g.client.PipeCreateErrorNotice(context.Background(), &libplugin.PipeCreateErrorNoticeRequest{ + FromAddr: conn.RemoteAddr().String(), + Error: err.Error(), + }) +} + func (g *GrpcPlugin) PipeStartCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) { meta := toMeta(challengeCtx, conn) _, _ = g.client.PipeStartNotice(context.Background(), &libplugin.PipeStartNoticeRequest{ diff --git a/e2e/failtoban_test.go b/e2e/failtoban_test.go index 412b1b38..65cda8f3 100644 --- a/e2e/failtoban_test.go +++ b/e2e/failtoban_test.go @@ -1,10 +1,15 @@ package e2e_test import ( + "fmt" "io" + "os" + "path" "strings" "testing" "time" + + "github.com/google/uuid" ) func TestFailtoban(t *testing.T) { @@ -87,3 +92,151 @@ func TestFailtoban(t *testing.T) { } } + +func TestFailtobanPipeCreateFail(t *testing.T) { + piperaddr, piperport := nextAvailablePiperAddress() + + piper, _, _, err := runCmd("/sshpiperd/sshpiperd", + "-p", + piperport, + "/sshpiperd/plugins/workingdir", + "--root", + workingdir, + "--", + "/sshpiperd/plugins/failtoban", + "--max-failures", + "3", + ) + + if err != nil { + t.Errorf("failed to run sshpiperd: %v", err) + } + + defer killCmd(piper) + + waitForEndpointReady(piperaddr) + + ensureWorkingDirectory() + + // ensure username works with password + userdir := path.Join(workingdir, "bypassword") + + { + if err := os.MkdirAll(userdir, 0700); err != nil { + t.Errorf("failed to create working directory %s: %v", userdir, err) + } + + if err := os.WriteFile(path.Join(userdir, "sshpiper_upstream"), []byte("user@host-password:2222"), 0400); err != nil { + t.Errorf("failed to write upstream file: %v", err) + } + } + + { + b, err := runAndGetStdout( + "ssh-keyscan", + "-p", + "2222", + "host-password", + ) + + if err != nil { + t.Errorf("failed to run ssh-keyscan: %v", err) + } + + if err := os.WriteFile(path.Join(userdir, "known_hosts"), b, 0400); err != nil { + t.Errorf("failed to write known_hosts: %v", err) + } + } + + { + randtext := uuid.New().String() + targetfie := uuid.New().String() + + c, stdin, stdout, err := runCmd( + "ssh", + "-v", + "-o", + "StrictHostKeyChecking=no", + "-o", + "UserKnownHostsFile=/dev/null", + "-p", + piperport, + "-l", + "bypassword", + "127.0.0.1", + fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), + ) + + if err != nil { + t.Errorf("failed to ssh to piper-workingdir, %v", err) + } + + defer killCmd(c) + + enterPassword(stdin, stdout, "pass") + + time.Sleep(time.Second) // wait for file flush + + checkSharedFileContent(t, targetfie, randtext) + } + + { + + // run 5 times to trigger ban + for i := 0; i < 3; i++ { + c, stdin, stdout, err := runCmd( + "ssh", + "-v", + "-o", + "StrictHostKeyChecking=no", + "-o", + "UserKnownHostsFile=/dev/null", + "-p", + piperport, + "-l", + fmt.Sprintf("notexist_%v", i), + "127.0.0.1", + ) + + if err != nil { + t.Errorf("ssh fail") + } + + enterPassword(stdin, stdout, "notapass") + killCmd(c) + } + } + + // run with good user + { + c, _, stdout, err := runCmd( + "ssh", + "-v", + "-o", + "StrictHostKeyChecking=no", + "-o", + "UserKnownHostsFile=/dev/null", + "-p", + piperport, + "-l", + "bypassword", + "127.0.0.1", + ) + + if err != nil { + t.Errorf("failed to ssh to workingdir, %v", err) + } + + defer killCmd(c) + + _ = c.Wait() + + time.Sleep(time.Second) // TODO ugly workaround, wait for stdout flush + + s, _ := io.ReadAll(stdout) + + if !strings.Contains(string(s), "Connection closed by 127.0.0.1") { + t.Errorf("expected connection closed by") + } + } +} diff --git a/libplugin/plugin.pb.go b/libplugin/plugin.pb.go index 01b5d0cb..e2e30f4f 100644 --- a/libplugin/plugin.pb.go +++ b/libplugin/plugin.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.20.1 +// protoc-gen-go v1.28.1 +// protoc v4.25.1 // source: plugin.proto package libplugin @@ -145,6 +145,7 @@ type Upstream struct { UserName string `protobuf:"bytes,3,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` IgnoreHostKey bool `protobuf:"varint,4,opt,name=ignore_host_key,json=ignoreHostKey,proto3" json:"ignore_host_key,omitempty"` // Types that are assignable to Auth: + // // *Upstream_None // *Upstream_Password // *Upstream_PrivateKey @@ -2059,6 +2060,99 @@ func (*PipeErrorNoticeResponse) Descriptor() ([]byte, []int) { return file_plugin_proto_rawDescGZIP(), []int{36} } +type PipeCreateErrorNoticeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FromAddr string `protobuf:"bytes,1,opt,name=from_addr,json=fromAddr,proto3" json:"from_addr,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *PipeCreateErrorNoticeRequest) Reset() { + *x = PipeCreateErrorNoticeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipeCreateErrorNoticeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipeCreateErrorNoticeRequest) ProtoMessage() {} + +func (x *PipeCreateErrorNoticeRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipeCreateErrorNoticeRequest.ProtoReflect.Descriptor instead. +func (*PipeCreateErrorNoticeRequest) Descriptor() ([]byte, []int) { + return file_plugin_proto_rawDescGZIP(), []int{37} +} + +func (x *PipeCreateErrorNoticeRequest) GetFromAddr() string { + if x != nil { + return x.FromAddr + } + return "" +} + +func (x *PipeCreateErrorNoticeRequest) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +type PipeCreateErrorNoticeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PipeCreateErrorNoticeResponse) Reset() { + *x = PipeCreateErrorNoticeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipeCreateErrorNoticeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipeCreateErrorNoticeResponse) ProtoMessage() {} + +func (x *PipeCreateErrorNoticeResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipeCreateErrorNoticeResponse.ProtoReflect.Descriptor instead. +func (*PipeCreateErrorNoticeResponse) Descriptor() ([]byte, []int) { + return file_plugin_proto_rawDescGZIP(), []int{38} +} + type KeyboardInteractivePromptRequest_Question struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2071,7 +2165,7 @@ type KeyboardInteractivePromptRequest_Question struct { func (x *KeyboardInteractivePromptRequest_Question) Reset() { *x = KeyboardInteractivePromptRequest_Question{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_proto_msgTypes[38] + mi := &file_plugin_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2084,7 +2178,7 @@ func (x *KeyboardInteractivePromptRequest_Question) String() string { func (*KeyboardInteractivePromptRequest_Question) ProtoMessage() {} func (x *KeyboardInteractivePromptRequest_Question) ProtoReflect() protoreflect.Message { - mi := &file_plugin_proto_msgTypes[38] + mi := &file_plugin_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2336,87 +2430,101 @@ var file_plugin_proto_rawDesc = []byte{ 0x6e, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x19, 0x0a, 0x17, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, - 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x4d, 0x0a, - 0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x4e, - 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, - 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x4b, 0x45, 0x59, - 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x42, 0x4f, 0x41, 0x52, 0x44, 0x5f, 0x49, - 0x4e, 0x54, 0x45, 0x52, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x32, 0xfc, 0x08, 0x0a, - 0x0e, 0x53, 0x73, 0x68, 0x50, 0x69, 0x70, 0x65, 0x72, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, - 0x36, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x4c, 0x6f, 0x67, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x43, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x4e, - 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6c, - 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, - 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, - 0x08, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x62, 0x70, + 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x0a, + 0x1c, 0x50, 0x69, 0x70, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x22, 0x1f, 0x0a, 0x1d, 0x50, 0x69, 0x70, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2a, 0x4d, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, + 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, + 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x55, 0x42, 0x4c, 0x49, + 0x43, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x42, 0x4f, 0x41, + 0x52, 0x44, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, + 0x32, 0xec, 0x09, 0x0a, 0x0e, 0x53, 0x73, 0x68, 0x50, 0x69, 0x70, 0x65, 0x72, 0x50, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x12, 0x36, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x69, + 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x4c, + 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x12, 0x1e, 0x2e, 0x6c, + 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, + 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x54, 0x0a, 0x0d, 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, + 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, + 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, + 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, + 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x45, 0x0a, 0x08, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1a, 0x2e, + 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, + 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x41, 0x75, 0x74, 0x68, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, - 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, - 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, - 0x17, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, 0x29, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x1a, 0x29, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x01, - 0x30, 0x01, 0x12, 0x78, 0x0a, 0x19, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, - 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, - 0x2b, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x55, 0x70, 0x73, 0x74, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x50, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, + 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1f, 0x2e, 0x6c, + 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x75, 0x0a, 0x17, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, 0x29, 0x2e, 0x6c, + 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, + 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x29, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x78, 0x0a, 0x19, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, - 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6c, - 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x06, - 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x42, 0x61, 0x6e, - 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, - 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1f, - 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x50, 0x69, 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x55, + 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x06, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x6c, + 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, + 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x15, 0x50, 0x69, 0x70, + 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, + 0x63, 0x65, 0x12, 0x27, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, + 0x69, 0x70, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, + 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6c, 0x69, + 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x50, 0x69, 0x70, 0x65, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, + 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x62, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4e, - 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x5a, 0x0a, 0x0f, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, - 0x63, 0x65, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, - 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x67, 0x31, 0x32, 0x33, 0x2f, - 0x73, 0x73, 0x68, 0x70, 0x69, 0x70, 0x65, 0x72, 0x2f, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, + 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x25, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x67, + 0x31, 0x32, 0x33, 0x2f, 0x73, 0x73, 0x68, 0x70, 0x69, 0x70, 0x65, 0x72, 0x2f, 0x6c, 0x69, 0x62, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2432,7 +2540,7 @@ func file_plugin_proto_rawDescGZIP() []byte { } var file_plugin_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 39) +var file_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 41) var file_plugin_proto_goTypes = []interface{}{ (AuthMethod)(0), // 0: libplugin.AuthMethod (*ConnMeta)(nil), // 1: libplugin.ConnMeta @@ -2472,8 +2580,10 @@ var file_plugin_proto_goTypes = []interface{}{ (*PipeStartNoticeResponse)(nil), // 35: libplugin.PipeStartNoticeResponse (*PipeErrorNoticeRequest)(nil), // 36: libplugin.PipeErrorNoticeRequest (*PipeErrorNoticeResponse)(nil), // 37: libplugin.PipeErrorNoticeResponse - nil, // 38: libplugin.UpstreamNextPluginAuth.MetaEntry - (*KeyboardInteractivePromptRequest_Question)(nil), // 39: libplugin.KeyboardInteractivePromptRequest.Question + (*PipeCreateErrorNoticeRequest)(nil), // 38: libplugin.PipeCreateErrorNoticeRequest + (*PipeCreateErrorNoticeResponse)(nil), // 39: libplugin.PipeCreateErrorNoticeResponse + nil, // 40: libplugin.UpstreamNextPluginAuth.MetaEntry + (*KeyboardInteractivePromptRequest_Question)(nil), // 41: libplugin.KeyboardInteractivePromptRequest.Question } var file_plugin_proto_depIdxs = []int32{ 3, // 0: libplugin.Upstream.none:type_name -> libplugin.UpstreamNoneAuth @@ -2481,7 +2591,7 @@ var file_plugin_proto_depIdxs = []int32{ 5, // 2: libplugin.Upstream.private_key:type_name -> libplugin.UpstreamPrivateKeyAuth 6, // 3: libplugin.Upstream.remote_signer:type_name -> libplugin.UpstreamRemoteSignerAuth 7, // 4: libplugin.Upstream.next_plugin:type_name -> libplugin.UpstreamNextPluginAuth - 38, // 5: libplugin.UpstreamNextPluginAuth.meta:type_name -> libplugin.UpstreamNextPluginAuth.MetaEntry + 40, // 5: libplugin.UpstreamNextPluginAuth.meta:type_name -> libplugin.UpstreamNextPluginAuth.MetaEntry 1, // 6: libplugin.NewConnectionRequest.meta:type_name -> libplugin.ConnMeta 1, // 7: libplugin.NextAuthMethodsRequest.meta:type_name -> libplugin.ConnMeta 0, // 8: libplugin.NextAuthMethodsResponse.methods:type_name -> libplugin.AuthMethod @@ -2491,7 +2601,7 @@ var file_plugin_proto_depIdxs = []int32{ 2, // 12: libplugin.PasswordAuthResponse.upstream:type_name -> libplugin.Upstream 1, // 13: libplugin.PublicKeyAuthRequest.meta:type_name -> libplugin.ConnMeta 2, // 14: libplugin.PublicKeyAuthResponse.upstream:type_name -> libplugin.Upstream - 39, // 15: libplugin.KeyboardInteractivePromptRequest.questions:type_name -> libplugin.KeyboardInteractivePromptRequest.Question + 41, // 15: libplugin.KeyboardInteractivePromptRequest.questions:type_name -> libplugin.KeyboardInteractivePromptRequest.Question 1, // 16: libplugin.KeyboardInteractiveMetaResponse.meta:type_name -> libplugin.ConnMeta 2, // 17: libplugin.KeyboardInteractiveFinishRequest.upstream:type_name -> libplugin.Upstream 23, // 18: libplugin.KeyboardInteractiveAuthMessage.prompt_request:type_name -> libplugin.KeyboardInteractivePromptRequest @@ -2516,23 +2626,25 @@ var file_plugin_proto_depIdxs = []int32{ 28, // 37: libplugin.SshPiperPlugin.UpstreamAuthFailureNotice:input_type -> libplugin.UpstreamAuthFailureNoticeRequest 30, // 38: libplugin.SshPiperPlugin.Banner:input_type -> libplugin.BannerRequest 32, // 39: libplugin.SshPiperPlugin.VerifyHostKey:input_type -> libplugin.VerifyHostKeyRequest - 34, // 40: libplugin.SshPiperPlugin.PipeStartNotice:input_type -> libplugin.PipeStartNoticeRequest - 36, // 41: libplugin.SshPiperPlugin.PipeErrorNotice:input_type -> libplugin.PipeErrorNoticeRequest - 9, // 42: libplugin.SshPiperPlugin.Logs:output_type -> libplugin.Log - 11, // 43: libplugin.SshPiperPlugin.ListCallbacks:output_type -> libplugin.ListCallbackResponse - 13, // 44: libplugin.SshPiperPlugin.NewConnection:output_type -> libplugin.NewConnectionResponse - 15, // 45: libplugin.SshPiperPlugin.NextAuthMethods:output_type -> libplugin.NextAuthMethodsResponse - 17, // 46: libplugin.SshPiperPlugin.NoneAuth:output_type -> libplugin.NoneAuthResponse - 19, // 47: libplugin.SshPiperPlugin.PasswordAuth:output_type -> libplugin.PasswordAuthResponse - 21, // 48: libplugin.SshPiperPlugin.PublicKeyAuth:output_type -> libplugin.PublicKeyAuthResponse - 27, // 49: libplugin.SshPiperPlugin.KeyboardInteractiveAuth:output_type -> libplugin.KeyboardInteractiveAuthMessage - 29, // 50: libplugin.SshPiperPlugin.UpstreamAuthFailureNotice:output_type -> libplugin.UpstreamAuthFailureNoticeResponse - 31, // 51: libplugin.SshPiperPlugin.Banner:output_type -> libplugin.BannerResponse - 33, // 52: libplugin.SshPiperPlugin.VerifyHostKey:output_type -> libplugin.VerifyHostKeyResponse - 35, // 53: libplugin.SshPiperPlugin.PipeStartNotice:output_type -> libplugin.PipeStartNoticeResponse - 37, // 54: libplugin.SshPiperPlugin.PipeErrorNotice:output_type -> libplugin.PipeErrorNoticeResponse - 42, // [42:55] is the sub-list for method output_type - 29, // [29:42] is the sub-list for method input_type + 38, // 40: libplugin.SshPiperPlugin.PipeCreateErrorNotice:input_type -> libplugin.PipeCreateErrorNoticeRequest + 34, // 41: libplugin.SshPiperPlugin.PipeStartNotice:input_type -> libplugin.PipeStartNoticeRequest + 36, // 42: libplugin.SshPiperPlugin.PipeErrorNotice:input_type -> libplugin.PipeErrorNoticeRequest + 9, // 43: libplugin.SshPiperPlugin.Logs:output_type -> libplugin.Log + 11, // 44: libplugin.SshPiperPlugin.ListCallbacks:output_type -> libplugin.ListCallbackResponse + 13, // 45: libplugin.SshPiperPlugin.NewConnection:output_type -> libplugin.NewConnectionResponse + 15, // 46: libplugin.SshPiperPlugin.NextAuthMethods:output_type -> libplugin.NextAuthMethodsResponse + 17, // 47: libplugin.SshPiperPlugin.NoneAuth:output_type -> libplugin.NoneAuthResponse + 19, // 48: libplugin.SshPiperPlugin.PasswordAuth:output_type -> libplugin.PasswordAuthResponse + 21, // 49: libplugin.SshPiperPlugin.PublicKeyAuth:output_type -> libplugin.PublicKeyAuthResponse + 27, // 50: libplugin.SshPiperPlugin.KeyboardInteractiveAuth:output_type -> libplugin.KeyboardInteractiveAuthMessage + 29, // 51: libplugin.SshPiperPlugin.UpstreamAuthFailureNotice:output_type -> libplugin.UpstreamAuthFailureNoticeResponse + 31, // 52: libplugin.SshPiperPlugin.Banner:output_type -> libplugin.BannerResponse + 33, // 53: libplugin.SshPiperPlugin.VerifyHostKey:output_type -> libplugin.VerifyHostKeyResponse + 39, // 54: libplugin.SshPiperPlugin.PipeCreateErrorNotice:output_type -> libplugin.PipeCreateErrorNoticeResponse + 35, // 55: libplugin.SshPiperPlugin.PipeStartNotice:output_type -> libplugin.PipeStartNoticeResponse + 37, // 56: libplugin.SshPiperPlugin.PipeErrorNotice:output_type -> libplugin.PipeErrorNoticeResponse + 43, // [43:57] is the sub-list for method output_type + 29, // [29:43] is the sub-list for method input_type 29, // [29:29] is the sub-list for extension type_name 29, // [29:29] is the sub-list for extension extendee 0, // [0:29] is the sub-list for field type_name @@ -2988,7 +3100,31 @@ func file_plugin_proto_init() { return nil } } + file_plugin_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipeCreateErrorNoticeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } file_plugin_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipeCreateErrorNoticeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*KeyboardInteractivePromptRequest_Question); i { case 0: return &v.state @@ -3021,7 +3157,7 @@ func file_plugin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_plugin_proto_rawDesc, NumEnums: 1, - NumMessages: 39, + NumMessages: 41, NumExtensions: 0, NumServices: 1, }, diff --git a/libplugin/plugin.proto b/libplugin/plugin.proto index 2fc88c8b..b9e716f5 100644 --- a/libplugin/plugin.proto +++ b/libplugin/plugin.proto @@ -50,15 +50,16 @@ service SshPiperPlugin { rpc Logs(StartLogRequest) returns (stream Log) {} rpc ListCallbacks(ListCallbackRequest) returns (ListCallbackResponse) {} - rpc NewConnection(NewConnectionRequest) returns (NewConnectionResponse) {}; - rpc NextAuthMethods(NextAuthMethodsRequest) returns (NextAuthMethodsResponse) {}; - rpc NoneAuth(NoneAuthRequest) returns (NoneAuthResponse) {}; - rpc PasswordAuth(PasswordAuthRequest) returns (PasswordAuthResponse) {}; - rpc PublicKeyAuth(PublicKeyAuthRequest) returns (PublicKeyAuthResponse) {}; - rpc KeyboardInteractiveAuth(stream KeyboardInteractiveAuthMessage) returns (stream KeyboardInteractiveAuthMessage); - rpc UpstreamAuthFailureNotice(UpstreamAuthFailureNoticeRequest) returns (UpstreamAuthFailureNoticeResponse) {}; - rpc Banner(BannerRequest) returns (BannerResponse) {}; + rpc NewConnection(NewConnectionRequest) returns (NewConnectionResponse) {} + rpc NextAuthMethods(NextAuthMethodsRequest) returns (NextAuthMethodsResponse) {} + rpc NoneAuth(NoneAuthRequest) returns (NoneAuthResponse) {} + rpc PasswordAuth(PasswordAuthRequest) returns (PasswordAuthResponse) {} + rpc PublicKeyAuth(PublicKeyAuthRequest) returns (PublicKeyAuthResponse) {} + rpc KeyboardInteractiveAuth(stream KeyboardInteractiveAuthMessage) returns (stream KeyboardInteractiveAuthMessage) {} + rpc UpstreamAuthFailureNotice(UpstreamAuthFailureNoticeRequest) returns (UpstreamAuthFailureNoticeResponse) {} + rpc Banner(BannerRequest) returns (BannerResponse) {} rpc VerifyHostKey (VerifyHostKeyRequest) returns (VerifyHostKeyResponse) {} + rpc PipeCreateErrorNotice(PipeCreateErrorNoticeRequest) returns (PipeCreateErrorNoticeResponse) {} rpc PipeStartNotice(PipeStartNoticeRequest) returns (PipeStartNoticeResponse) {} rpc PipeErrorNotice(PipeErrorNoticeRequest) returns (PipeErrorNoticeResponse) {} } @@ -206,4 +207,12 @@ message PipeErrorNoticeRequest { } message PipeErrorNoticeResponse { -} \ No newline at end of file +} + +message PipeCreateErrorNoticeRequest { + string from_addr = 1; + string error = 2; +} + +message PipeCreateErrorNoticeResponse { +} diff --git a/libplugin/plugin_grpc.pb.go b/libplugin/plugin_grpc.pb.go index 943286cd..e19623a6 100644 --- a/libplugin/plugin_grpc.pb.go +++ b/libplugin/plugin_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v3.20.1 +// - protoc v4.25.1 // source: plugin.proto package libplugin @@ -33,6 +33,7 @@ type SshPiperPluginClient interface { UpstreamAuthFailureNotice(ctx context.Context, in *UpstreamAuthFailureNoticeRequest, opts ...grpc.CallOption) (*UpstreamAuthFailureNoticeResponse, error) Banner(ctx context.Context, in *BannerRequest, opts ...grpc.CallOption) (*BannerResponse, error) VerifyHostKey(ctx context.Context, in *VerifyHostKeyRequest, opts ...grpc.CallOption) (*VerifyHostKeyResponse, error) + PipeCreateErrorNotice(ctx context.Context, in *PipeCreateErrorNoticeRequest, opts ...grpc.CallOption) (*PipeCreateErrorNoticeResponse, error) PipeStartNotice(ctx context.Context, in *PipeStartNoticeRequest, opts ...grpc.CallOption) (*PipeStartNoticeResponse, error) PipeErrorNotice(ctx context.Context, in *PipeErrorNoticeRequest, opts ...grpc.CallOption) (*PipeErrorNoticeResponse, error) } @@ -189,6 +190,15 @@ func (c *sshPiperPluginClient) VerifyHostKey(ctx context.Context, in *VerifyHost return out, nil } +func (c *sshPiperPluginClient) PipeCreateErrorNotice(ctx context.Context, in *PipeCreateErrorNoticeRequest, opts ...grpc.CallOption) (*PipeCreateErrorNoticeResponse, error) { + out := new(PipeCreateErrorNoticeResponse) + err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/PipeCreateErrorNotice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *sshPiperPluginClient) PipeStartNotice(ctx context.Context, in *PipeStartNoticeRequest, opts ...grpc.CallOption) (*PipeStartNoticeResponse, error) { out := new(PipeStartNoticeResponse) err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/PipeStartNotice", in, out, opts...) @@ -222,6 +232,7 @@ type SshPiperPluginServer interface { UpstreamAuthFailureNotice(context.Context, *UpstreamAuthFailureNoticeRequest) (*UpstreamAuthFailureNoticeResponse, error) Banner(context.Context, *BannerRequest) (*BannerResponse, error) VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyResponse, error) + PipeCreateErrorNotice(context.Context, *PipeCreateErrorNoticeRequest) (*PipeCreateErrorNoticeResponse, error) PipeStartNotice(context.Context, *PipeStartNoticeRequest) (*PipeStartNoticeResponse, error) PipeErrorNotice(context.Context, *PipeErrorNoticeRequest) (*PipeErrorNoticeResponse, error) mustEmbedUnimplementedSshPiperPluginServer() @@ -264,6 +275,9 @@ func (UnimplementedSshPiperPluginServer) Banner(context.Context, *BannerRequest) func (UnimplementedSshPiperPluginServer) VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method VerifyHostKey not implemented") } +func (UnimplementedSshPiperPluginServer) PipeCreateErrorNotice(context.Context, *PipeCreateErrorNoticeRequest) (*PipeCreateErrorNoticeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PipeCreateErrorNotice not implemented") +} func (UnimplementedSshPiperPluginServer) PipeStartNotice(context.Context, *PipeStartNoticeRequest) (*PipeStartNoticeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method PipeStartNotice not implemented") } @@ -492,6 +506,24 @@ func _SshPiperPlugin_VerifyHostKey_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _SshPiperPlugin_PipeCreateErrorNotice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PipeCreateErrorNoticeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SshPiperPluginServer).PipeCreateErrorNotice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/libplugin.SshPiperPlugin/PipeCreateErrorNotice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SshPiperPluginServer).PipeCreateErrorNotice(ctx, req.(*PipeCreateErrorNoticeRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _SshPiperPlugin_PipeStartNotice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(PipeStartNoticeRequest) if err := dec(in); err != nil { @@ -571,6 +603,10 @@ var SshPiperPlugin_ServiceDesc = grpc.ServiceDesc{ MethodName: "VerifyHostKey", Handler: _SshPiperPlugin_VerifyHostKey_Handler, }, + { + MethodName: "PipeCreateErrorNotice", + Handler: _SshPiperPlugin_PipeCreateErrorNotice_Handler, + }, { MethodName: "PipeStartNotice", Handler: _SshPiperPlugin_PipeStartNotice_Handler, diff --git a/libplugin/pluginbase.go b/libplugin/pluginbase.go index 9d06e2a4..4a958010 100644 --- a/libplugin/pluginbase.go +++ b/libplugin/pluginbase.go @@ -55,6 +55,8 @@ type SshPiperPluginConfig struct { VerifyHostKeyCallback func(conn ConnMetadata, hostname, netaddr string, key []byte) error + PipeCreateErrorCallback func(remoteAddr string, err error) + PipeStartCallback func(conn ConnMetadata) PipeErrorCallback func(conn ConnMetadata, err error) @@ -192,6 +194,10 @@ func (s *server) ListCallbacks(ctx context.Context, req *ListCallbackRequest) (* cb = append(cb, "PipeError") } + if s.config.PipeCreateErrorCallback != nil { + cb = append(cb, "PipeCreateError") + } + return &ListCallbackResponse{ Callbacks: cb, }, nil @@ -423,3 +429,13 @@ func (s *server) PipeErrorNotice(ctx context.Context, req *PipeErrorNoticeReques return &PipeErrorNoticeResponse{}, nil } + +func (s *server) PipeCreateErrorNotice(ctx context.Context, req *PipeCreateErrorNoticeRequest) (*PipeCreateErrorNoticeResponse, error) { + if s.config.PipeCreateErrorCallback == nil { + return nil, status.Errorf(codes.Unimplemented, "method PipeCreateErrorNotice not implemented") + } + + s.config.PipeCreateErrorCallback(req.FromAddr, fmt.Errorf(req.Error)) + + return &PipeCreateErrorNoticeResponse{}, nil +} diff --git a/plugin/failtoban/main.go b/plugin/failtoban/main.go index c0fe4534..2b71b9ac 100644 --- a/plugin/failtoban/main.go +++ b/plugin/failtoban/main.go @@ -64,7 +64,12 @@ func main() { UpstreamAuthFailureCallback: func(conn libplugin.ConnMetadata, method string, err error, allowmethods []string) { ip, _, _ := net.SplitHostPort(conn.RemoteAddr()) failed, _ := cache.IncrementInt(ip, 1) - log.Debugf("failtoban: %v auth failed %v times, max allowed %v", ip, failed, maxFailures) + log.Debugf("failtoban: %v auth failed. current status: fail %v times, max allowed %v", ip, failed, maxFailures) + }, + PipeCreateErrorCallback: func(remoteAddr string, err error) { + ip, _, _ := net.SplitHostPort(remoteAddr) + failed, _ := cache.IncrementInt(ip, 1) + log.Debugf("failtoban: %v pipe create failed, reason %v. current status: fail %v times, max allowed %v", ip, err, failed, maxFailures) }, }, nil },