From 7dba81efb5f4a4a583a39040ad079f0be0b57d30 Mon Sep 17 00:00:00 2001 From: Joseph Lewis III Date: Wed, 11 Dec 2024 20:31:50 -0800 Subject: [PATCH 1/3] Started adding session termination event #39 --- cmd/playground.go | 3 +- core/honeypot.go | 19 ++- core/logger/log.pb.go | 332 ++++++++++++++++++++++++------------- core/logger/log.pb.json.go | 16 ++ core/logger/log.proto | 13 ++ core/logger/report.go | 8 + core/vos/io.go | 41 +++++ 7 files changed, 316 insertions(+), 116 deletions(-) diff --git a/cmd/playground.go b/cmd/playground.go index 4710640..4b730f5 100644 --- a/cmd/playground.go +++ b/cmd/playground.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "io" - "io/ioutil" "log" "net" "os" @@ -72,7 +71,7 @@ var playgroundCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - dir, err := ioutil.TempDir("", "playground") + dir, err := os.MkdirTemp("", "playground") if err != nil { return err } diff --git a/core/honeypot.go b/core/honeypot.go index 48c247f..2d7a6d4 100644 --- a/core/honeypot.go +++ b/core/honeypot.go @@ -148,7 +148,8 @@ func (h *Honeypot) Close() error { } func (h *Honeypot) HandleConnection(s ssh.Session) error { - sessionID := fmt.Sprintf("%d", time.Now().UnixNano()) + sessionStartTime := time.Now() + sessionID := fmt.Sprintf("%d", sessionStartTime.UnixNano()) sessionLogger := h.logger.NewSession(sessionID) // Log panics to prevent a single connection from bringing down the whole @@ -194,7 +195,21 @@ func (h *Honeypot) HandleConnection(s ssh.Session) error { defer logFd.Close() // Start logging the terminal interactions - vio := ttylog.NewRecorder(vos.NewVIOAdapter(s, s, s), ttylog.NewAsciicastLogSink(logFd)) + readCounter := vos.NewCounter(s, func(b byte) bool { + return b == 127 || // Delete + b == 8 // Backspace + }) + vio := ttylog.NewRecorder(vos.NewVIOAdapter(readCounter, s, s), ttylog.NewAsciicastLogSink(logFd)) + + defer func() { + sessionLogger.Record(&logger.LogEntry_SessionEnded{ + SessionEnded: &logger.SessionEnded{ + DurationMs: time.Since(sessionStartTime).Milliseconds(), + HumanKeypressCount: int64(readCounter.MatchedTotal), + StdinByteCount: int64(readCounter.Total), + }, + }) + }() procName := h.configuration.OS.DefaultShell procArgs := []string{procName} diff --git a/core/logger/log.pb.go b/core/logger/log.pb.go index 7aa1432..7085108 100644 --- a/core/logger/log.pb.go +++ b/core/logger/log.pb.go @@ -194,6 +194,7 @@ type LogEntry struct { // *LogEntry_Download // *LogEntry_Panic // *LogEntry_HoneypotEvent + // *LogEntry_SessionEnded LogType isLogEntry_LogType `protobuf_oneof:"log_type"` } @@ -341,6 +342,13 @@ func (x *LogEntry) GetHoneypotEvent() *HoneypotEvent { return nil } +func (x *LogEntry) GetSessionEnded() *SessionEnded { + if x, ok := x.GetLogType().(*LogEntry_SessionEnded); ok { + return x.SessionEnded + } + return nil +} + type isLogEntry_LogType interface { isLogEntry_LogType() } @@ -398,6 +406,10 @@ type LogEntry_HoneypotEvent struct { HoneypotEvent *HoneypotEvent `protobuf:"bytes,27,opt,name=honeypot_event,json=honeypotEvent,proto3,oneof"` } +type LogEntry_SessionEnded struct { + SessionEnded *SessionEnded `protobuf:"bytes,28,opt,name=session_ended,json=sessionEnded,proto3,oneof"` +} + func (*LogEntry_LoginAttempt) isLogEntry_LogType() {} func (*LogEntry_FilesystemOperation) isLogEntry_LogType() {} @@ -424,6 +436,8 @@ func (*LogEntry_Panic) isLogEntry_LogType() {} func (*LogEntry_HoneypotEvent) isLogEntry_LogType() {} +func (*LogEntry_SessionEnded) isLogEntry_LogType() {} + type FilesystemOp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1255,10 +1269,77 @@ func (x *HoneypotEvent) GetEventType() HoneypotEvent_Type { return HoneypotEvent_UNKNOWN } +// Summary reported at the end of a session. +type SessionEnded struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Session duration in milliseconds. + DurationMs int64 `protobuf:"varint,1,opt,name=duration_ms,json=durationMs,proto3" json:"duration_ms,omitempty"` + // Number of characters that looked "human" passed through stdin. + HumanKeypressCount int64 `protobuf:"varint,2,opt,name=human_keypress_count,json=humanKeypressCount,proto3" json:"human_keypress_count,omitempty"` + // Number of bytes written through stdin. + StdinByteCount int64 `protobuf:"varint,3,opt,name=stdin_byte_count,json=stdinByteCount,proto3" json:"stdin_byte_count,omitempty"` +} + +func (x *SessionEnded) Reset() { + *x = SessionEnded{} + if protoimpl.UnsafeEnabled { + mi := &file_log_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SessionEnded) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SessionEnded) ProtoMessage() {} + +func (x *SessionEnded) ProtoReflect() protoreflect.Message { + mi := &file_log_proto_msgTypes[14] + 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 SessionEnded.ProtoReflect.Descriptor instead. +func (*SessionEnded) Descriptor() ([]byte, []int) { + return file_log_proto_rawDescGZIP(), []int{14} +} + +func (x *SessionEnded) GetDurationMs() int64 { + if x != nil { + return x.DurationMs + } + return 0 +} + +func (x *SessionEnded) GetHumanKeypressCount() int64 { + if x != nil { + return x.HumanKeypressCount + } + return 0 +} + +func (x *SessionEnded) GetStdinByteCount() int64 { + if x != nil { + return x.StdinByteCount + } + return 0 +} + var File_log_proto protoreflect.FileDescriptor var file_log_proto_rawDesc = []byte{ - 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa1, 0x06, 0x0a, 0x08, + 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd7, 0x06, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x69, 0x63, @@ -1307,108 +1388,120 @@ var file_log_proto_rawDesc = []byte{ 0x61, 0x6e, 0x69, 0x63, 0x12, 0x37, 0x0a, 0x0e, 0x68, 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x48, 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, - 0x68, 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x0a, - 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x0f, 0x22, - 0x0e, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4f, 0x70, 0x22, - 0xbe, 0x02, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, - 0x12, 0x28, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x10, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, - 0x64, 0x72, 0x12, 0x33, 0x0a, 0x15, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x14, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x77, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x61, 0x77, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x22, 0x20, 0x0a, 0x0a, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x54, 0x59, 0x4c, 0x6f, 0x67, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x4c, 0x6f, 0x73, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x33, 0x0a, - 0x15, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x72, - 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x65, 0x6e, - 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x50, 0x61, 0x74, 0x68, 0x22, 0xe8, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, - 0x77, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, + 0x68, 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x34, 0x0a, + 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x1c, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x6e, + 0x64, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x6e, + 0x64, 0x65, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x4a, + 0x04, 0x08, 0x03, 0x10, 0x0f, 0x22, 0x0e, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x4f, 0x70, 0x22, 0xbe, 0x02, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x41, + 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x28, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x15, 0x65, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x77, 0x5f, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x61, + 0x77, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0x20, 0x0a, 0x0a, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x54, + 0x59, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x73, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x52, + 0x75, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x59, 0x0a, 0x14, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, - 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, - 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x4e, 0x4f, - 0x54, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, - 0x10, 0x0a, 0x0c, 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, - 0x03, 0x22, 0x69, 0x0a, 0x0e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x70, 0x74, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, 0x73, 0x50, 0x74, 0x79, 0x22, 0x1e, 0x0a, 0x08, - 0x4f, 0x70, 0x65, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xbf, 0x01, 0x0a, - 0x11, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x69, - 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x5f, 0x73, 0x75, 0x6d, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x53, 0x75, 0x6d, 0x22, 0x66, - 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x50, 0x0a, 0x08, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, - 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x41, 0x0a, 0x05, 0x50, 0x61, 0x6e, 0x69, - 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x22, 0x72, 0x0a, 0x0d, 0x48, - 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x0a, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x13, 0x2e, 0x48, 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x2d, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, - 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x54, 0x45, 0x10, 0x02, 0x2a, - 0x38, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x6f, 0x73, 0x65, 0x70, 0x68, 0x6c, 0x65, - 0x77, 0x69, 0x73, 0x34, 0x32, 0x2f, 0x68, 0x6f, 0x6e, 0x65, 0x79, 0x73, 0x73, 0x68, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x61, 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x15, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x14, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x61, 0x74, 0x68, 0x22, 0xe8, 0x01, 0x0a, + 0x0e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x59, 0x0a, 0x14, + 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x01, + 0x12, 0x13, 0x0a, 0x0f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x4d, 0x45, 0x4e, + 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, 0x5f, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x22, 0x69, 0x0a, 0x0e, 0x54, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x61, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, + 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x15, 0x0a, 0x06, 0x69, + 0x73, 0x5f, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, 0x73, 0x50, + 0x74, 0x79, 0x22, 0x1e, 0x0a, 0x08, 0x4f, 0x70, 0x65, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x22, 0xbf, 0x01, 0x0a, 0x11, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x49, 0x6e, + 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, + 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x6d, 0x6f, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x6d, + 0x6f, 0x64, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, + 0x64, 0x53, 0x75, 0x6d, 0x22, 0x66, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x50, 0x0a, 0x08, + 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x41, + 0x0a, 0x05, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, + 0x65, 0x22, 0x72, 0x0a, 0x0d, 0x48, 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, 0x74, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x48, 0x6f, 0x6e, 0x65, 0x79, 0x70, 0x6f, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x2d, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, + 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, + 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, + 0x41, 0x54, 0x45, 0x10, 0x02, 0x22, 0x8b, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x75, 0x6d, 0x61, 0x6e, + 0x5f, 0x6b, 0x65, 0x79, 0x70, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x4b, 0x65, 0x79, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x74, 0x64, + 0x69, 0x6e, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x74, 0x64, 0x69, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x2a, 0x38, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, 0x42, 0x2f, 0x5a, + 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x6f, 0x73, 0x65, + 0x70, 0x68, 0x6c, 0x65, 0x77, 0x69, 0x73, 0x34, 0x32, 0x2f, 0x68, 0x6f, 0x6e, 0x65, 0x79, 0x73, + 0x73, 0x68, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1424,7 +1517,7 @@ func file_log_proto_rawDescGZIP() []byte { } var file_log_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_log_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_log_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_log_proto_goTypes = []interface{}{ (OperationResult)(0), // 0: OperationResult (UnknownCommand_UnknownCommandStatus)(0), // 1: UnknownCommand.UnknownCommandStatus @@ -1443,6 +1536,7 @@ var file_log_proto_goTypes = []interface{}{ (*Download)(nil), // 14: Download (*Panic)(nil), // 15: Panic (*HoneypotEvent)(nil), // 16: HoneypotEvent + (*SessionEnded)(nil), // 17: SessionEnded } var file_log_proto_depIdxs = []int32{ 5, // 0: LogEntry.login_attempt:type_name -> LoginAttempt @@ -1458,14 +1552,15 @@ var file_log_proto_depIdxs = []int32{ 14, // 10: LogEntry.download:type_name -> Download 15, // 11: LogEntry.panic:type_name -> Panic 16, // 12: LogEntry.honeypot_event:type_name -> HoneypotEvent - 0, // 13: LoginAttempt.result:type_name -> OperationResult - 1, // 14: UnknownCommand.status:type_name -> UnknownCommand.UnknownCommandStatus - 2, // 15: HoneypotEvent.event_type:type_name -> HoneypotEvent.Type - 16, // [16:16] is the sub-list for method output_type - 16, // [16:16] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 17, // 13: LogEntry.session_ended:type_name -> SessionEnded + 0, // 14: LoginAttempt.result:type_name -> OperationResult + 1, // 15: UnknownCommand.status:type_name -> UnknownCommand.UnknownCommandStatus + 2, // 16: HoneypotEvent.event_type:type_name -> HoneypotEvent.Type + 17, // [17:17] is the sub-list for method output_type + 17, // [17:17] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_log_proto_init() } @@ -1642,6 +1737,18 @@ func file_log_proto_init() { return nil } } + file_log_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SessionEnded); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_log_proto_msgTypes[0].OneofWrappers = []interface{}{ (*LogEntry_LoginAttempt)(nil), @@ -1657,6 +1764,7 @@ func file_log_proto_init() { (*LogEntry_Download)(nil), (*LogEntry_Panic)(nil), (*LogEntry_HoneypotEvent)(nil), + (*LogEntry_SessionEnded)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -1664,7 +1772,7 @@ func file_log_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_log_proto_rawDesc, NumEnums: 3, - NumMessages: 14, + NumMessages: 15, NumExtensions: 0, NumServices: 0, }, diff --git a/core/logger/log.pb.json.go b/core/logger/log.pb.json.go index dfd16b1..9871f1c 100644 --- a/core/logger/log.pb.json.go +++ b/core/logger/log.pb.json.go @@ -230,3 +230,19 @@ func (msg *HoneypotEvent) UnmarshalJSON(b []byte) error { DiscardUnknown: false, }.Unmarshal(b, msg) } + +// MarshalJSON implements json.Marshaler +func (msg *SessionEnded) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: false, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *SessionEnded) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} diff --git a/core/logger/log.proto b/core/logger/log.proto index b9a53af..8ee608d 100644 --- a/core/logger/log.proto +++ b/core/logger/log.proto @@ -28,6 +28,7 @@ message LogEntry { Download download = 25; Panic panic = 26; HoneypotEvent honeypot_event = 27; + SessionEnded session_ended = 28; }; } @@ -163,3 +164,15 @@ message HoneypotEvent { // Context about what was going on before the panic. Type event_type = 1; } + +// Summary reported at the end of a session. +message SessionEnded { + // Session duration in milliseconds. + int64 duration_ms = 1; + + // Number of characters that looked "human" passed through stdin. + int64 human_keypress_count = 2; + + // Number of bytes written through stdin. + int64 stdin_byte_count = 3; +} \ No newline at end of file diff --git a/core/logger/report.go b/core/logger/report.go index 742bfce..50c28d5 100644 --- a/core/logger/report.go +++ b/core/logger/report.go @@ -77,6 +77,10 @@ type InteractiveSession struct { TerminalName string `json:"terminal_name"` IsPty bool `json:"is_pty"` + DurationMs int64 `json:"duration_ms,omitempty"` + HumanKeypressCount int64 `json:"human_keypress_count,omitempty"` + StdinByteCount int64 `json:"stdin_byte_count,omitempty"` + Commands []string `json:"commands"` Downloads []string `json:"downloads"` } @@ -101,6 +105,10 @@ func (i *InteractiveSession) Update(le *LogEntry) { i.IsPty = event.TerminalUpdate.GetIsPty() case *LogEntry_OpenTtyLog: i.TTYLog = event.OpenTtyLog.GetName() + case *LogEntry_SessionEnded: + i.DurationMs = event.SessionEnded.DurationMs + i.HumanKeypressCount = event.SessionEnded.HumanKeypressCount + i.StdinByteCount = event.SessionEnded.StdinByteCount } } diff --git a/core/vos/io.go b/core/vos/io.go index b42e46c..441807a 100644 --- a/core/vos/io.go +++ b/core/vos/io.go @@ -85,3 +85,44 @@ func (*devNull) Close() error { func (*devNull) Write(b []byte) (int, error) { return len(b), nil } + +// Counter counts the number of matching and total bytes through a reader. +type Counter struct { + test func(byte) bool + wrapped io.ReadCloser + + // Total number of bytes read. + Total int + // Total number of matched bytes. + MatchedTotal int +} + +// NewCounter creates a new counter over the wrapped stream applying test to +// each byte to update the TestTotal property. +func NewCounter(wrapped io.ReadCloser, test func(byte) bool) *Counter { + return &Counter{ + test: test, + wrapped: wrapped, + } +} + +var _ io.ReadCloser = (*Counter)(nil) + +// Read implements io.Reader. +func (c *Counter) Read(data []byte) (int, error) { + cnt, err := c.wrapped.Read(data) + c.Total += cnt + + for i := 0; i < cnt; i++ { + if c.test(data[i]) { + c.MatchedTotal++ + } + } + + return cnt, err +} + +// Close implemnts io.Closer. +func (c *Counter) Close() error { + return c.wrapped.Close() +} From ed60fefaaad7e89dec6c65642e0e24fa8aca5c10 Mon Sep 17 00:00:00 2001 From: Joseph Lewis III Date: Wed, 11 Dec 2024 21:45:22 -0800 Subject: [PATCH 2/3] Made playground use session --- cmd/playground.go | 118 +++++++++++++++++++++++++------------- core/config/initialize.go | 2 +- core/honeypot.go | 18 +++++- 3 files changed, 96 insertions(+), 42 deletions(-) diff --git a/cmd/playground.go b/cmd/playground.go index 4b730f5..19f73ec 100644 --- a/cmd/playground.go +++ b/cmd/playground.go @@ -1,27 +1,37 @@ package cmd import ( - "fmt" + "context" "io" "log" "net" "os" "path/filepath" "strings" - "time" - "github.com/josephlewis42/honeyssh/commands" + "github.com/anmitsu/go-shlex" + "github.com/gliderlabs/ssh" + "github.com/josephlewis42/honeyssh/core" "github.com/josephlewis42/honeyssh/core/config" - "github.com/josephlewis42/honeyssh/core/logger" "github.com/josephlewis42/honeyssh/core/vos" "github.com/spf13/cobra" ) type playgroundSession struct { - user string - out io.Writer + user string + out io.Writer + in io.Reader + rawCommand string + subsystem string + environ []string + pty ssh.Pty + + exitCalled bool + exitCode int } +var _ core.SessionInfo = (*playgroundSession)(nil) + func (p *playgroundSession) User() string { return p.user } @@ -31,7 +41,8 @@ func (p *playgroundSession) RemoteAddr() net.Addr { } func (p *playgroundSession) Exit(code int) error { - os.Exit(code) + p.exitCalled = true + p.exitCode = code return nil } @@ -39,6 +50,41 @@ func (p *playgroundSession) Write(b []byte) (int, error) { return p.out.Write(b) } +func (p *playgroundSession) Command() []string { + // Ignore the error, it doesn't matter for the playground. + cmd, _ := shlex.Split(p.rawCommand, true) + return cmd +} + +func (p *playgroundSession) RawCommand() string { + return p.rawCommand +} +func (p *playgroundSession) Subsystem() string { + return p.subsystem +} + +func (p *playgroundSession) Context() context.Context { + return context.Background() +} + +func (p *playgroundSession) Environ() []string { + return p.environ +} + +func (p *playgroundSession) Pty() (ssh.Pty, <-chan ssh.Window, bool) { + output := make(<-chan ssh.Window) + return p.pty, output, true +} + +func (p *playgroundSession) Read(b []byte) (int, error) { + return p.in.Read(b) +} + +func (p *playgroundSession) Close() error { + // Close does nothing in playground + return nil +} + type SSHSession interface { User() string RemoteAddr() net.Addr @@ -87,48 +133,42 @@ var playgroundCmd = &cobra.Command{ // a real one -- it's surprisingly convincing. cfg.Uname.Nodename = "playground🍯" - fs, err := vos.NewVFSFromConfig(cfg) + playgroundLogger.Printf("Logging to: file://%s\n", dir) + playgroundLogger.Printf("See logs with: tail -f %s\n", filepath.Join(dir, config.AppLogName)) + playgroundLogger.Println(strings.Repeat("=", 80)) + + honeypot, err := core.NewHoneypot(cfg, io.Discard) if err != nil { return err } - logFd, err := cfg.OpenAppLog() - if err != nil { + session := &playgroundSession{ + out: cmd.OutOrStdout(), + in: cmd.InOrStdin(), + user: "root", + rawCommand: "/bin/sh", + subsystem: "", + environ: []string{}, + pty: ssh.Pty{ + Term: "playground", + Window: ssh.Window{ + Width: 80, + Height: 40, + }, + }, + } + + if err := honeypot.HandleConnection(session); err != nil { return err } - defer logFd.Close() - logRecorder := logger.NewJsonLinesLogRecorder(logFd) - playgroundLogger.Printf("Logging to: file://%s\n", dir) - playgroundLogger.Printf("See logs with: tail -f %s\n", filepath.Join(dir, logFd.Name())) playgroundLogger.Println(strings.Repeat("=", 80)) - - sharedOS := vos.NewSharedOS(fs, commands.BuiltinProcessResolver, cfg, time.Now) - tenantOS := vos.NewTenantOS(sharedOS, logRecorder.NewSession("playground"), &playgroundSession{ - out: cmd.OutOrStdout(), - user: "root", - }) - // TODO: Connect to the real PTY - tenantOS.SetPTY(vos.PTY{ - Width: 80, - Height: 40, - Term: "playground", - IsPTY: true, - }) - - initProc := tenantOS.LoginProc() - - runner, err := initProc.StartProcess("/bin/sh", []string{}, &vos.ProcAttr{ - Dir: "/", - Env: initProc.Environ(), - Files: &osVIO{}, - }) - if err != nil { - return err + if session.exitCalled { + playgroundLogger.Printf("Session ended, exit code: %d\n", session.exitCode) + } else { + playgroundLogger.Println("Session ended, exit not called") } - exitCode := runner.Run() - fmt.Fprintf(cmd.OutOrStdout(), "Exit code: %d\n", exitCode) return nil }, } diff --git a/core/config/initialize.go b/core/config/initialize.go index dc09a46..9c7880a 100644 --- a/core/config/initialize.go +++ b/core/config/initialize.go @@ -16,7 +16,7 @@ import ( "github.com/spf13/afero" ) -// Initialize creates the honeypot configuartion in the given directory. +// Initialize creates the honeypot configuration in the given directory. func Initialize(path string, logger *log.Logger) (*Configuration, error) { // Make sure path exists full, err := filepath.Abs(path) diff --git a/core/honeypot.go b/core/honeypot.go index 2d7a6d4..0afee8d 100644 --- a/core/honeypot.go +++ b/core/honeypot.go @@ -147,7 +147,21 @@ func (h *Honeypot) Close() error { return h.toClose.Close() } -func (h *Honeypot) HandleConnection(s ssh.Session) error { +// Minimal properties from ssh.Session needed for the honeypot. +type SessionInfo interface { + Context() context.Context + Environ() []string + RawCommand() string + Subsystem() string + Command() []string + Pty() (ssh.Pty, <-chan ssh.Window, bool) + vos.SSHSession + io.ReadCloser +} + +var _ SessionInfo = (ssh.Session)(nil) + +func (h *Honeypot) HandleConnection(s SessionInfo) error { sessionStartTime := time.Now() sessionID := fmt.Sprintf("%d", sessionStartTime.UnixNano()) sessionLogger := h.logger.NewSession(sessionID) @@ -180,7 +194,7 @@ func (h *Honeypot) HandleConnection(s ssh.Session) error { }, }) - // Set up I/O and loging. + // Set up I/O and logging. logFileName := fmt.Sprintf("%s.%s", time.Now().Format(time.RFC3339Nano), ttylog.AsciicastFileExt) sessionLogger.Record(&logger.LogEntry_OpenTtyLog{ OpenTtyLog: &logger.OpenTTYLog{ From 1e742a3d0a3c4cee0e9ff501affeb232c3ab86f3 Mon Sep 17 00:00:00 2001 From: Joseph Lewis III Date: Wed, 11 Dec 2024 21:55:18 -0800 Subject: [PATCH 3/3] Ran gofmt --- cmd/builtins.go | 2 +- cmd/init.go | 2 +- cmd/root.go | 2 +- cmd/serve.go | 2 +- commands/env_test.go | 2 +- core/vos/fs.go | 2 +- core/vos/fs_test.go | 2 +- core/vos/sharedos.go | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/builtins.go b/cmd/builtins.go index 13e7c49..4119f99 100644 --- a/cmd/builtins.go +++ b/cmd/builtins.go @@ -5,8 +5,8 @@ import ( "sort" "strings" - "github.com/spf13/cobra" "github.com/josephlewis42/honeyssh/commands" + "github.com/spf13/cobra" ) // serveCmd represents the serve command diff --git a/cmd/init.go b/cmd/init.go index 32d7e4e..1a48737 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,8 +3,8 @@ package cmd import ( "log" - "github.com/spf13/cobra" "github.com/josephlewis42/honeyssh/core/config" + "github.com/spf13/cobra" ) // initCmd intializes the honeypot configuration diff --git a/cmd/root.go b/cmd/root.go index b2c092a..1a493fb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,8 +5,8 @@ import ( "io/fs" "log" - "github.com/spf13/cobra" "github.com/josephlewis42/honeyssh/core/config" + "github.com/spf13/cobra" ) var cfgPath string diff --git a/cmd/serve.go b/cmd/serve.go index f798fd8..68e4f1e 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -8,8 +8,8 @@ import ( "syscall" "time" - "github.com/spf13/cobra" "github.com/josephlewis42/honeyssh/core" + "github.com/spf13/cobra" ) // serveCmd represents the serve command diff --git a/commands/env_test.go b/commands/env_test.go index 6c496e8..c86ea60 100644 --- a/commands/env_test.go +++ b/commands/env_test.go @@ -3,8 +3,8 @@ package commands import ( "testing" - "github.com/stretchr/testify/assert" "github.com/josephlewis42/honeyssh/core/vos/vostest" + "github.com/stretchr/testify/assert" ) func TestEnv(t *testing.T) { diff --git a/core/vos/fs.go b/core/vos/fs.go index 732e54e..f910457 100644 --- a/core/vos/fs.go +++ b/core/vos/fs.go @@ -12,11 +12,11 @@ import ( "strings" "time" - "github.com/spf13/afero" "github.com/josephlewis42/honeyssh/core/config" "github.com/josephlewis42/honeyssh/third_party/cowfs" "github.com/josephlewis42/honeyssh/third_party/memmapfs" "github.com/josephlewis42/honeyssh/third_party/realpath" + "github.com/spf13/afero" ) func NewVFSFromConfig(configuration *config.Configuration) (VFS, error) { diff --git a/core/vos/fs_test.go b/core/vos/fs_test.go index 8208711..369fd81 100644 --- a/core/vos/fs_test.go +++ b/core/vos/fs_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" + "github.com/josephlewis42/honeyssh/third_party/memmapfs" "github.com/spf13/afero" "github.com/stretchr/testify/assert" - "github.com/josephlewis42/honeyssh/third_party/memmapfs" ) func FSTestCase(t *testing.T, suite FSTestSuite, testPath string) *FSTestCaseSetup { diff --git a/core/vos/sharedos.go b/core/vos/sharedos.go index 05fce0b..a1c304d 100644 --- a/core/vos/sharedos.go +++ b/core/vos/sharedos.go @@ -4,8 +4,8 @@ import ( "sync/atomic" "time" - "github.com/spf13/afero" "github.com/josephlewis42/honeyssh/core/config" + "github.com/spf13/afero" ) // ProcessFunc is a "process" that can be run.