Added a watch_json() plugin (#3298)

This is based on the watch_syslog() plugin. Also added tests and
bugfixes to watch_syslog()

Also suppress detailed errors to non-API HTTP requests like the public
directory handler.
This commit is contained in:
Mike Cohen
2024-02-20 01:29:57 +10:00
committed by GitHub
parent 9133e7419b
commit 16287fa5f7
17 changed files with 919 additions and 324 deletions
+7 -26
View File
@@ -10,29 +10,9 @@ import (
"www.velocidex.com/golang/velociraptor/constants"
"www.velocidex.com/golang/velociraptor/json"
"www.velocidex.com/golang/velociraptor/logging"
http_utils "www.velocidex.com/golang/velociraptor/utils/http"
)
// Record the status of the request so we can log it.
type statusRecorder struct {
http.ResponseWriter
http.Flusher
status int
error []byte
}
func (self *statusRecorder) WriteHeader(code int) {
self.status = code
self.ResponseWriter.WriteHeader(code)
}
func (self *statusRecorder) Write(buf []byte) (int, error) {
if self.status == 500 {
self.error = buf
}
return self.ResponseWriter.Write(buf)
}
func GetUserInfo(ctx context.Context,
config_obj *config_proto.Config) *api_proto.VelociraptorUser {
result := &api_proto.VelociraptorUser{}
@@ -51,22 +31,23 @@ func GetUserInfo(ctx context.Context,
func GetLoggingHandler(config_obj *config_proto.Config) func(http.Handler) http.Handler {
logger := logging.GetLogger(config_obj, &logging.GUIComponent)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rec := &statusRecorder{
rec := &http_utils.StatusRecorder{
w,
w.(http.Flusher),
200, nil}
defer func() {
if rec.status == 500 {
if rec.Status == 500 {
logger.WithFields(
logrus.Fields{
"method": r.Method,
"url": r.URL.Path,
"remote": r.RemoteAddr,
"error": string(rec.error),
"error": string(rec.Error),
"user-agent": r.UserAgent(),
"status": rec.status,
"status": rec.Status,
"user": GetUserInfo(
r.Context(), config_obj).Name,
}).Error("")
@@ -78,7 +59,7 @@ func GetLoggingHandler(config_obj *config_proto.Config) func(http.Handler) http.
"url": r.URL.Path,
"remote": r.RemoteAddr,
"user-agent": r.UserAgent(),
"status": rec.status,
"status": rec.Status,
"user": GetUserInfo(
r.Context(), config_obj).Name,
}).Info("")
+6 -26
View File
@@ -27,29 +27,9 @@ import (
"www.velocidex.com/golang/velociraptor/constants"
"www.velocidex.com/golang/velociraptor/json"
"www.velocidex.com/golang/velociraptor/logging"
http_utils "www.velocidex.com/golang/velociraptor/utils/http"
)
// Record the status of the request so we can log it.
type statusRecorder struct {
http.ResponseWriter
http.Flusher
status int
error []byte
}
func (self *statusRecorder) WriteHeader(code int) {
self.status = code
self.ResponseWriter.WriteHeader(code)
}
func (self *statusRecorder) Write(buf []byte) (int, error) {
if self.status == 500 {
self.error = buf
}
return self.ResponseWriter.Write(buf)
}
func GetUserInfo(ctx context.Context,
config_obj *config_proto.Config) *api_proto.VelociraptorUser {
result := &api_proto.VelociraptorUser{}
@@ -70,20 +50,20 @@ func GetLoggingHandler(config_obj *config_proto.Config) func(http.Handler) http.
logger := logging.GetLogger(config_obj, &logging.GUIComponent)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rec := &statusRecorder{
rec := &http_utils.StatusRecorder{
w,
w.(http.Flusher),
200, nil}
defer func() {
if rec.status == 500 {
if rec.Status == 500 {
logger.WithFields(
logrus.Fields{
"method": r.Method,
"url": r.URL.Path,
"remote": r.RemoteAddr,
"error": string(rec.error),
"error": string(rec.Error),
"user-agent": r.UserAgent(),
"status": rec.status,
"status": rec.Status,
"user": GetUserInfo(
r.Context(), config_obj).Name,
}).Error("")
@@ -95,7 +75,7 @@ func GetLoggingHandler(config_obj *config_proto.Config) func(http.Handler) http.
"url": r.URL.Path,
"remote": r.RemoteAddr,
"user-agent": r.UserAgent(),
"status": rec.status,
"status": rec.Status,
"user": GetUserInfo(
r.Context(), config_obj).Name,
}).Info("")
+2
View File
@@ -309,6 +309,8 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
func initDebugServer(config_obj *config_proto.Config) error {
if *debug_flag {
config_obj.DebugMode = true
logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
logger.Info("<green>Starting</> debug server on <cyan>http://127.0.0.1:%v/debug/pprof", *debug_flag_port)
+213 -176
View File
@@ -3499,9 +3499,15 @@ type Defaults struct {
// Normally the inventory service attempts to download tools in
// its own but if this is set, we prevent any external access.
DisableInventoryServiceExternalAccess bool `protobuf:"varint,34,opt,name=disable_inventory_service_external_access,json=disableInventoryServiceExternalAccess,proto3" json:"disable_inventory_service_external_access,omitempty"`
// Controls how exports work (creating hunt or colletion exports to a zip file).
// Controls how exports work (creating hunt or colletion exports
// to a zip file).
ExportConcurrency int64 `protobuf:"varint,40,opt,name=export_concurrency,json=exportConcurrency,proto3" json:"export_concurrency,omitempty"`
ExportMaxTimeoutSec int64 `protobuf:"varint,41,opt,name=export_max_timeout_sec,json=exportMaxTimeoutSec,proto3" json:"export_max_timeout_sec,omitempty"`
// Watch plugin frequency sleep time in seconds: How often
// watch_syslog() will check for changes (default 3).
WatchPluginFrequency int64 `protobuf:"varint,44,opt,name=watch_plugin_frequency,json=watchPluginFrequency,proto3" json:"watch_plugin_frequency,omitempty"`
// Maximum length of the line that will be parsed (16kb)
WatchPluginBufferSize int64 `protobuf:"varint,45,opt,name=watch_plugin_buffer_size,json=watchPluginBufferSize,proto3" json:"watch_plugin_buffer_size,omitempty"`
}
func (x *Defaults) Reset() {
@@ -3767,6 +3773,20 @@ func (x *Defaults) GetExportMaxTimeoutSec() int64 {
return 0
}
func (x *Defaults) GetWatchPluginFrequency() int64 {
if x != nil {
return x.WatchPluginFrequency
}
return 0
}
func (x *Defaults) GetWatchPluginBufferSize() int64 {
if x != nil {
return x.WatchPluginBufferSize
}
return 0
}
// Configures crypto preferences
type CryptoConfig struct {
state protoimpl.MessageState
@@ -4141,8 +4161,9 @@ type Config struct {
// internally that mark each org's config object. They should
// definitely not be set on the client's config because the client
// does not know or use its own org id.
OrgId string `protobuf:"bytes,36,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"`
OrgName string `protobuf:"bytes,37,opt,name=org_name,json=orgName,proto3" json:"org_name,omitempty"`
OrgId string `protobuf:"bytes,36,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"`
OrgName string `protobuf:"bytes,37,opt,name=org_name,json=orgName,proto3" json:"org_name,omitempty"`
DebugMode bool `protobuf:"varint,41,opt,name=debug_mode,json=debugMode,proto3" json:"debug_mode,omitempty"`
// The services that will run at initialization. Note this is not
// set in the config file by the user but is propagated from the
// startup code.
@@ -4365,6 +4386,13 @@ func (x *Config) GetOrgName() string {
return ""
}
func (x *Config) GetDebugMode() bool {
if x != nil {
return x.DebugMode
}
return false
}
func (x *Config) GetServices() *ServerServicesConfig {
if x != nil {
return x.Services
@@ -5517,7 +5545,7 @@ var file_config_proto_rawDesc = []byte{
0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x45,
0x76, 0x65, 0x6e, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x83, 0x0e, 0x0a, 0x08, 0x44, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0xf2, 0x0e, 0x0a, 0x08, 0x44, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x75, 0x6e, 0x74, 0x5f, 0x65,
0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x03, 0x52, 0x0f, 0x68, 0x75, 0x6e, 0x74, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x48, 0x6f, 0x75,
@@ -5629,179 +5657,188 @@ var file_config_proto_rawDesc = []byte{
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x33, 0x0a, 0x16, 0x65, 0x78,
0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
0x5f, 0x73, 0x65, 0x63, 0x18, 0x29, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x65, 0x78, 0x70, 0x6f,
0x72, 0x74, 0x4d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x22,
0xad, 0x04, 0x0a, 0x0c, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12,
0x7f, 0x0a, 0x17, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74,
0x68, 0x75, 0x6d, 0x62, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
0x42, 0x46, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x40, 0x12, 0x3e, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36,
0x20, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20,
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61,
0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x69, 0x6c,
0x6c, 0x20, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x52, 0x16, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x65, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73,
0x12, 0xd5, 0x01, 0x0a, 0x1d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f,
0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x90, 0x01, 0xe2, 0xfc, 0xe3, 0xc4, 0x01,
0x89, 0x01, 0x12, 0x86, 0x01, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65,
0x20, 0x77, 0x61, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x56, 0x65,
0x6c, 0x6f, 0x63, 0x69, 0x72, 0x61, 0x70, 0x74, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, 0x69, 0x66,
0x69, 0x65, 0x73, 0x20, 0x54, 0x4c, 0x53, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x20, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x73, 0x3a, 0x20, 0x50, 0x4b, 0x49, 0x20, 0x28, 0x74, 0x68, 0x65, 0x20, 0x64,
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x29, 0x2c, 0x20, 0x50, 0x4b, 0x49, 0x5f, 0x4f, 0x52, 0x5f,
0x54, 0x48, 0x55, 0x4d, 0x42, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x2c, 0x20, 0x54, 0x48, 0x55, 0x4d,
0x42, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x52, 0x1b, 0x63, 0x65, 0x72,
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f,
0x77, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x65,
0x61, 0x6b, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43,
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x1e, 0x63, 0x6c,
0x72, 0x74, 0x4d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x12,
0x34, 0x0a, 0x16, 0x77, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f,
0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x03, 0x52,
0x14, 0x77, 0x61, 0x74, 0x63, 0x68, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x46, 0x72, 0x65, 0x71,
0x75, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x18, 0x77, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x70,
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a,
0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x77, 0x61, 0x74, 0x63, 0x68, 0x50, 0x6c,
0x75, 0x67, 0x69, 0x6e, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x22, 0xad,
0x04, 0x0a, 0x0c, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x7f,
0x0a, 0x17, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x68,
0x75, 0x6d, 0x62, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42,
0x46, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x40, 0x12, 0x3e, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20,
0x74, 0x68, 0x75, 0x6d, 0x62, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x63,
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74,
0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c,
0x20, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x52, 0x16, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x65, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x12,
0xd5, 0x01, 0x0a, 0x1d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f,
0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x90, 0x01, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x89,
0x01, 0x12, 0x86, 0x01, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
0x77, 0x61, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x56, 0x65, 0x6c,
0x6f, 0x63, 0x69, 0x72, 0x61, 0x70, 0x74, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69,
0x65, 0x73, 0x20, 0x54, 0x4c, 0x53, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x73, 0x3a, 0x20, 0x50, 0x4b, 0x49, 0x20, 0x28, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x29, 0x2c, 0x20, 0x50, 0x4b, 0x49, 0x5f, 0x4f, 0x52, 0x5f, 0x54,
0x48, 0x55, 0x4d, 0x42, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x2c, 0x20, 0x54, 0x48, 0x55, 0x4d, 0x42,
0x50, 0x52, 0x49, 0x4e, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x52, 0x1b, 0x63, 0x65, 0x72, 0x74,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77,
0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x65, 0x61,
0x6b, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6c,
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01,
0x28, 0x09, 0x52, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22,
0x5d, 0x0a, 0x0a, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a,
0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65,
0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,
0x78, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x22, 0xf0,
0x02, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70,
0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x25,
0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52,
0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x21, 0x0a, 0x02, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50,
0x6f, 0x69, 0x6e, 0x74, 0x52, 0x02, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d,
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70,
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f,
0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f,
0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x07, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x51, 0x4c, 0x45,
0x6e, 0x76, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20,
0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x46, 0x75, 0x6e,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09,
0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e,
0x73, 0x22, 0xf7, 0x0c, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x0a, 0x0f,
0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
0x15, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x63,
0x65, 0x72, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x1c, 0xe2, 0xfc, 0xe3, 0xc4,
0x01, 0x16, 0x12, 0x14, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x66, 0x6f,
0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x4a, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x1d, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x17, 0x12, 0x15,
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a,
0x03, 0x41, 0x50, 0x49, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x2c, 0xe2, 0xfc,
0xe3, 0xc4, 0x01, 0x26, 0x12, 0x24, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x52, 0x50, 0x43, 0x20, 0x41, 0x50, 0x49,
0x20, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x52, 0x03, 0x41, 0x50, 0x49, 0x12,
0x22, 0x0a, 0x03, 0x47, 0x55, 0x49, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x55, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03,
0x47, 0x55, 0x49, 0x12, 0x1f, 0x0a, 0x02, 0x43, 0x41, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x41, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x02, 0x43, 0x41, 0x12, 0x31, 0x0a, 0x08, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64,
0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46,
0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x46,
0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x12, 0x3d, 0x0a, 0x0e, 0x45, 0x78, 0x74, 0x72, 0x61,
0x46, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x46, 0x72, 0x6f,
0x6e, 0x74, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x34, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x09, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x32, 0x0a, 0x09,
0x57, 0x72, 0x69, 0x74, 0x65, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x62, 0x61, 0x63,
0x6b, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x57, 0x72, 0x69, 0x74, 0x65, 0x62, 0x61, 0x63, 0x6b,
0x12, 0x25, 0x0a, 0x04, 0x4d, 0x61, 0x69, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x04, 0x4d, 0x61, 0x69, 0x6c, 0x12, 0x2e, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69,
0x6e, 0x67, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07,
0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x2b, 0x0a, 0x06, 0x4d, 0x69, 0x6e, 0x69, 0x6f,
0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x4d, 0x69, 0x6e, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x4d, 0x69,
0x6e, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18,
0x14, 0x20, 0x01, 0x28, 0x08, 0x42, 0x26, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x20, 0x12, 0x1e, 0x45,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x20, 0x6c, 0x6f,
0x67, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x52, 0x07, 0x76,
0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65,
0x72, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x18, 0x16, 0x20,
0x01, 0x28, 0x09, 0x42, 0x2c, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x26, 0x12, 0x24, 0x50, 0x61, 0x74,
0x68, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x63,
0x65, 0x72, 0x74, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73,
0x2e, 0x52, 0x11, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x43, 0x65, 0x72, 0x74, 0x43,
0x61, 0x63, 0x68, 0x65, 0x12, 0x6e, 0x0a, 0x0a, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69,
0x6e, 0x67, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x42, 0x35, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x2f, 0x12, 0x2d, 0x57, 0x68, 0x65, 0x72, 0x65,
0x20, 0x74, 0x6f, 0x20, 0x62, 0x69, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68,
0x65, 0x75, 0x73, 0x20, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x52, 0x0a, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f,
0x72, 0x69, 0x6e, 0x67, 0x12, 0x7f, 0x0a, 0x0a, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x41, 0x70, 0x69, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x42, 0x48, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x42, 0x12, 0x40, 0x49, 0x66, 0x20, 0x77, 0x65, 0x20,
0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x69, 0x20, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x77, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68,
0x69, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x6c, 0x6f, 0x62,
0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x09, 0x61, 0x70, 0x69, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x8f, 0x01, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x65, 0x78,
0x65, 0x63, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
0x5c, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x56, 0x12, 0x54, 0x49, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73,
0x20, 0x69, 0x73, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x77, 0x65,
0x20, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x6e, 0x61,
0x72, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65,
0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x61,
0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x2e, 0x52, 0x08, 0x61,
0x75, 0x74, 0x6f, 0x65, 0x78, 0x65, 0x63, 0x12, 0x50, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2f, 0xe2, 0xfc,
0xe3, 0xc4, 0x01, 0x29, 0x12, 0x27, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x20, 0x28, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2c, 0x20, 0x77, 0x69, 0x6e,
0x64, 0x6f, 0x77, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x29, 0x52, 0x0a, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x62, 0x66,
0x75, 0x73, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x20,
0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x62, 0x66, 0x75, 0x73, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
0x74, 0x73, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6e,
0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36, 0x0a, 0x0a,
0x72, 0x65, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6d, 0x61, 0x70, 0x70, 0x69,
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x61, 0x70, 0x70,
0x69, 0x6e, 0x67, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x24,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f,
0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f,
0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x73, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12,
0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x27, 0x20, 0x01, 0x28,
0x08, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x42, 0x34, 0x5a, 0x32, 0x77,
0x77, 0x77, 0x2e, 0x76, 0x65, 0x6c, 0x6f, 0x63, 0x69, 0x64, 0x65, 0x78, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x76, 0x65, 0x6c, 0x6f, 0x63, 0x69, 0x72, 0x61,
0x70, 0x74, 0x6f, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x1e, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f,
0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x1b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x65, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x5d,
0x0a, 0x0a, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08,
0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66,
0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x22, 0xf0, 0x02,
0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x25, 0x0a,
0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04,
0x66, 0x72, 0x6f, 0x6d, 0x12, 0x21, 0x0a, 0x02, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x6f,
0x69, 0x6e, 0x74, 0x52, 0x02, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65,
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73,
0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73,
0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x07, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x51, 0x4c, 0x45, 0x6e,
0x76, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03,
0x28, 0x09, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x46, 0x75, 0x6e, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
0x64, 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52,
0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73,
0x22, 0x96, 0x0d, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x0a, 0x0f, 0x61,
0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x15,
0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65,
0x72, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x1c, 0xe2, 0xfc, 0xe3, 0xc4, 0x01,
0x16, 0x12, 0x14, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72,
0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x4a, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x1d, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x17, 0x12, 0x15, 0x43,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x03,
0x41, 0x50, 0x49, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2e, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x2c, 0xe2, 0xfc, 0xe3,
0xc4, 0x01, 0x26, 0x12, 0x24, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x52, 0x50, 0x43, 0x20, 0x41, 0x50, 0x49, 0x20,
0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x52, 0x03, 0x41, 0x50, 0x49, 0x12, 0x22,
0x0a, 0x03, 0x47, 0x55, 0x49, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x55, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x47,
0x55, 0x49, 0x12, 0x1f, 0x0a, 0x02, 0x43, 0x41, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x41, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
0x02, 0x43, 0x41, 0x12, 0x31, 0x0a, 0x08, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x18,
0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x72,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x46, 0x72,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x12, 0x3d, 0x0a, 0x0e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x46,
0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x46, 0x72, 0x6f, 0x6e,
0x74, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x34, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f,
0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x09, 0x44, 0x61, 0x74, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x32, 0x0a, 0x09, 0x57,
0x72, 0x69, 0x74, 0x65, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x62, 0x61, 0x63, 0x6b,
0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x57, 0x72, 0x69, 0x74, 0x65, 0x62, 0x61, 0x63, 0x6b, 0x12,
0x25, 0x0a, 0x04, 0x4d, 0x61, 0x69, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x04, 0x4d, 0x61, 0x69, 0x6c, 0x12, 0x2e, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e,
0x67, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x4c,
0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x2b, 0x0a, 0x06, 0x4d, 0x69, 0x6e, 0x69, 0x6f, 0x6e,
0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d,
0x69, 0x6e, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x4d, 0x69, 0x6e,
0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x14,
0x20, 0x01, 0x28, 0x08, 0x42, 0x26, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x20, 0x12, 0x1e, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x20, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x20, 0x6c, 0x6f, 0x67,
0x67, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x52, 0x07, 0x76, 0x65,
0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72,
0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x18, 0x16, 0x20, 0x01,
0x28, 0x09, 0x42, 0x2c, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x26, 0x12, 0x24, 0x50, 0x61, 0x74, 0x68,
0x20, 0x74, 0x6f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65,
0x72, 0x74, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e,
0x52, 0x11, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x43, 0x65, 0x72, 0x74, 0x43, 0x61,
0x63, 0x68, 0x65, 0x12, 0x6e, 0x0a, 0x0a, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e,
0x67, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x42, 0x35, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x2f, 0x12, 0x2d, 0x57, 0x68, 0x65, 0x72, 0x65, 0x20,
0x74, 0x6f, 0x20, 0x62, 0x69, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65,
0x75, 0x73, 0x20, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e,
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x52, 0x0a, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
0x69, 0x6e, 0x67, 0x12, 0x7f, 0x0a, 0x0a, 0x61, 0x70, 0x69, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x41, 0x70, 0x69, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
0x48, 0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x42, 0x12, 0x40, 0x49, 0x66, 0x20, 0x77, 0x65, 0x20, 0x6c,
0x6f, 0x61, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x69, 0x20, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x20, 0x77, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68, 0x69,
0x73, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61,
0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x09, 0x61, 0x70, 0x69, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x8f, 0x01, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x65, 0x78, 0x65,
0x63, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x41, 0x75, 0x74, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x5c,
0xe2, 0xfc, 0xe3, 0xc4, 0x01, 0x56, 0x12, 0x54, 0x49, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20,
0x69, 0x73, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x77, 0x65, 0x20,
0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72,
0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e,
0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x61, 0x75,
0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x2e, 0x52, 0x08, 0x61, 0x75,
0x74, 0x6f, 0x65, 0x78, 0x65, 0x63, 0x12, 0x50, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2f, 0xe2, 0xfc, 0xe3,
0xc4, 0x01, 0x29, 0x12, 0x27, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x20, 0x28, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2c, 0x20, 0x77, 0x69, 0x6e, 0x64,
0x6f, 0x77, 0x73, 0x2c, 0x20, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x29, 0x52, 0x0a, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x62, 0x66, 0x75,
0x73, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x20, 0x20,
0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x62, 0x66, 0x75, 0x73, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
0x73, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
0x74, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x74,
0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6e, 0x61,
0x6c, 0x79, 0x73, 0x69, 0x73, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36, 0x0a, 0x0a, 0x72,
0x65, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e,
0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x61, 0x70, 0x70, 0x69,
0x6e, 0x67, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x24, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72,
0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72,
0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6d,
0x6f, 0x64, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67,
0x4d, 0x6f, 0x64, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a,
0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x27, 0x20, 0x01, 0x28, 0x08, 0x52,
0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x42, 0x34, 0x5a, 0x32, 0x77, 0x77, 0x77,
0x2e, 0x76, 0x65, 0x6c, 0x6f, 0x63, 0x69, 0x64, 0x65, 0x78, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67,
0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x76, 0x65, 0x6c, 0x6f, 0x63, 0x69, 0x72, 0x61, 0x70, 0x74,
0x6f, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
+10 -2
View File
@@ -1098,9 +1098,17 @@ message Defaults {
// its own but if this is set, we prevent any external access.
bool disable_inventory_service_external_access = 34;
// Controls how exports work (creating hunt or colletion exports to a zip file).
// Controls how exports work (creating hunt or colletion exports
// to a zip file).
int64 export_concurrency = 40;
int64 export_max_timeout_sec = 41;
// Watch plugin frequency sleep time in seconds: How often
// watch_syslog() will check for changes (default 3).
int64 watch_plugin_frequency = 44;
// Maximum length of the line that will be parsed (16kb)
int64 watch_plugin_buffer_size = 45;
}
// Configures crypto preferences
@@ -1281,7 +1289,7 @@ message Config {
// does not know or use its own org id.
string org_id = 36;
string org_name = 37;
bool debug_mode = 41;
// The services that will run at initialization. Note this is not
// set in the config file by the user but is propagated from the
+1
View File
@@ -106,6 +106,7 @@ var (
"defaults.max_batch_wait",
"defaults.disable_inventory_service_external_access",
"lockdown",
"debug_mode",
}
)
+19 -1
View File
@@ -506,7 +506,7 @@ GUI:
- administrator
# How long to keep the session alive between auth flows - default 24 hours
default_session_expiry_min: 8000
default_session_expiry_min: 1440
# Used by the multi authenticator to provide multiple
# authenticators. NOTE: Sub authenticators must be oauth based
@@ -1188,6 +1188,21 @@ defaults:
export_concurrency: 10
export_max_timeout_sec: 600
# The server maintains an index of all hunts in order to quickly
# allow the GUI to filter/sort them. This setting controls how often
# to rebuild the hunt index (default 600 sec). You probably dont
# need to change it.
hunt_dispatcher_refresh_sec: 600
# Total number of cell versions we keep for undo/redo support.
notebook_versions: 5
# Watch plugin frequency sleep time in seconds: How often
# watch_syslog() will check for changes (default 3).
watch_plugin_frequency: 3
# Maximum length of the line that will be parsed (16kb)
watch_plugin_buffer_size: 16384
# The Velociraptor server may be placed into "lockdown" mode. While in
# lockdown mode certain permissions are denied - even for
@@ -1197,6 +1212,9 @@ defaults:
# lockdown to false and restarting the server.
lockdown: false
# This will be set when Velociraptor is started with the --debug flag.
debug_mode: false
# This configuration applies for minions. On minions this will
# override the settings elsewhere in the config file allowing an easy
# way to manage the difference between minions and master nodes.
+35 -5
View File
@@ -1425,6 +1425,37 @@
tools on the client.
https://docs.velociraptor.app/docs/extending_vql/#using-external-tools
NOTE: The plugin receives an array of arguments which are passed
to the `execve()` system call as an array (on Windows they are
properly escaped into a command line). This means that you do not
need to escape or quote any special characters in the command.
We noticed people often do this or variations on it:
```vql
LET PathToCacls = "C:/Program Files"
LET CommandLine <= "cacls.exe " + '"' + PathToCacls + '"'
SELECT * FROM execve(argv=["powershell", "-c", CommandLine])
```
While this appears to work it is incorrect, fragile and
susceptible to a simple shell injection (for example if the
`PathToCacls` contains quotes).
As a rule we prefer to not run commands through the shell at all
since it is not needed and unsafe. The correct approach is always
to split the `argv` into an array of distinct arguments:
```vql
LET PathToCacls = "C:/Program Files"
SELECT * FROM execve(argv=["cacls.exe", PathToCacls])
```
This calls the program directly and is not susceptible to escaping
or quoting issues (since there is no shell involved). Additionally
it does not invoke powershell which means that any execution
artifacts are not trampled by this VQL.
type: Plugin
args:
- name: argv
@@ -6896,14 +6927,14 @@
- name: xattr
description: |
Query a file for the specified extended attribute.
If no attributes are provided, this function will return all extended attributes
If no attributes are provided, this function will return all extended attributes
for the file.
Please note: this API is not reliable, so please provided extended attributes
where possible.
Note: This function only works on Mac and Linux.
Note: This function only works on Mac and Linux.
type: Function
args:
- name: filename
@@ -7057,4 +7088,3 @@
category: plugin
metadata:
permissions: FILESYSTEM_READ
+23 -27
View File
@@ -27,6 +27,7 @@ import (
"math/rand"
"net/http"
"net/url"
"os"
"sync/atomic"
"time"
@@ -47,10 +48,12 @@ import (
"www.velocidex.com/golang/velociraptor/constants"
crypto_proto "www.velocidex.com/golang/velociraptor/crypto/proto"
"www.velocidex.com/golang/velociraptor/logging"
http_utils "www.velocidex.com/golang/velociraptor/utils/http"
)
var (
packetTooLargeError = errors.New("Packet too large!")
notFoundError = errors.New("Not Found")
currentConnections = promauto.NewGauge(prometheus.GaugeOpts{
Name: "client_comms_current_connections",
@@ -684,33 +687,12 @@ func send_client_messages(server_obj *Server) http.Handler {
})
}
// Record the status of the request so we can log it.
type statusRecorder struct {
http.ResponseWriter
http.Flusher
status int
error []byte
}
func (self *statusRecorder) WriteHeader(code int) {
self.status = code
self.ResponseWriter.WriteHeader(code)
}
func (self *statusRecorder) Write(buf []byte) (int, error) {
if self.status == 500 {
self.error = buf
}
return self.ResponseWriter.Write(buf)
}
func GetLoggingHandler(config_obj *config_proto.Config,
handler string) func(http.Handler) http.Handler {
logger := logging.GetLogger(config_obj, &logging.FrontendComponent)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rec := &statusRecorder{
rec := &http_utils.StatusRecorder{
w,
w.(http.Flusher),
200, nil}
@@ -722,7 +704,7 @@ func GetLoggingHandler(config_obj *config_proto.Config,
"url": r.URL.Path,
"remote": r.RemoteAddr,
"user-agent": r.UserAgent(),
"status": rec.status,
"status": rec.Status,
"handler": handler,
}).Info("Access to handler")
}()
@@ -740,7 +722,7 @@ func downloadPublic(
// make sure the prefix is correct
for i, p := range prefix {
if len(components) <= i || p != components[i] {
returnError(w, 404, "Not Found")
returnError(config_obj, w, 404, notFoundError)
return
}
}
@@ -748,7 +730,7 @@ func downloadPublic(
file_store_factory := file_store.GetFileStore(config_obj)
fd, err := file_store_factory.ReadFile(path_spec)
if err != nil {
returnError(w, 404, err.Error())
returnError(config_obj, w, 404, err)
return
}
@@ -764,9 +746,23 @@ func downloadPublic(
})
}
func returnError(w http.ResponseWriter, code int, message string) {
func returnError(
config_obj *config_proto.Config,
w http.ResponseWriter, code int, err error) {
w.WriteHeader(code)
_, _ = w.Write([]byte(html.EscapeString(message)))
if config_obj.DebugMode {
_, _ = w.Write([]byte(html.EscapeString(err.Error())))
return
}
// In production provide generic errors.
if errors.Is(err, os.ErrNotExist) {
_, _ = w.Write([]byte("Not Found"))
} else {
_, _ = w.Write([]byte("Error"))
}
}
// Calculate QPS
+3 -2
View File
@@ -6,6 +6,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
http_utils "www.velocidex.com/golang/velociraptor/utils/http"
)
var (
@@ -20,13 +21,13 @@ var (
func RecordHTTPStats(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rec := &statusRecorder{
rec := &http_utils.StatusRecorder{
w,
w.(http.Flusher),
200, nil}
next.ServeHTTP(rec, r)
status := fmt.Sprintf("%v", rec.status)
status := fmt.Sprintf("%v", rec.Status)
httpErrorStatusCounters.WithLabelValues(status).Inc()
})
}
+24
View File
@@ -0,0 +1,24 @@
package http
import "net/http"
// Record the status of the request so we can log it.
type StatusRecorder struct {
http.ResponseWriter
http.Flusher
Status int
Error []byte
}
func (self *StatusRecorder) WriteHeader(code int) {
self.Status = code
self.ResponseWriter.WriteHeader(code)
}
func (self *StatusRecorder) Write(buf []byte) (int, error) {
if self.Status == 500 {
self.Error = buf
}
return self.ResponseWriter.Write(buf)
}
+103 -4
View File
@@ -31,11 +31,14 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
"www.velocidex.com/golang/velociraptor/accessors"
"www.velocidex.com/golang/velociraptor/acls"
"www.velocidex.com/golang/velociraptor/artifacts"
config_proto "www.velocidex.com/golang/velociraptor/config/proto"
"www.velocidex.com/golang/velociraptor/json"
utils "www.velocidex.com/golang/velociraptor/utils"
"www.velocidex.com/golang/velociraptor/vql"
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
"www.velocidex.com/golang/velociraptor/vql/functions"
"www.velocidex.com/golang/velociraptor/vql/parsers/syslog"
"www.velocidex.com/golang/vfilter"
"www.velocidex.com/golang/vfilter/arg_parser"
)
@@ -526,9 +529,10 @@ func (self _IndexAssociativeProtocol) GetMembers(
}
type WriteJSONPluginArgs struct {
Filename *accessors.OSPath `vfilter:"required,field=filename,doc=CSV files to open"`
Accessor string `vfilter:"optional,field=accessor,doc=The accessor to use"`
Query vfilter.StoredQuery `vfilter:"required,field=query,doc=query to write into the file."`
Filename *accessors.OSPath `vfilter:"required,field=filename,doc=CSV files to open"`
Accessor string `vfilter:"optional,field=accessor,doc=The accessor to use"`
Query vfilter.StoredQuery `vfilter:"required,field=query,doc=query to write into the file."`
BufferSize int `vfilter:"optional,field=buffer_size,doc=Maximum size of buffer before flushing to file."`
}
type WriteJSONPlugin struct{}
@@ -549,6 +553,10 @@ func (self WriteJSONPlugin) Call(
return
}
if arg.BufferSize == 0 {
arg.BufferSize = BUFF_SIZE
}
var writer *bufio.Writer
switch arg.Accessor {
@@ -575,7 +583,7 @@ func (self WriteJSONPlugin) Call(
}
defer file.Close()
writer = bufio.NewWriterSize(file, BUFF_SIZE)
writer = bufio.NewWriterSize(file, arg.BufferSize)
defer writer.Flush()
default:
@@ -613,6 +621,96 @@ func (self WriteJSONPlugin) Info(scope vfilter.Scope, type_map *vfilter.TypeMap)
}
}
type WatchJsonlPlugin struct{}
func (self WatchJsonlPlugin) Info(scope vfilter.Scope, type_map *vfilter.TypeMap) *vfilter.PluginInfo {
return &vfilter.PluginInfo{
Name: "watch_jsonl",
Doc: "Watch a jsonl file and stream events from it.",
ArgType: type_map.AddType(scope, &syslog.ScannerPluginArgs{}),
Metadata: vql.VQLMetadata().Permissions(acls.FILESYSTEM_READ).Build(),
}
}
func (self WatchJsonlPlugin) Call(
ctx context.Context, scope vfilter.Scope,
args *ordereddict.Dict) <-chan vfilter.Row {
output_chan := make(chan vfilter.Row)
go func() {
defer close(output_chan)
arg := &syslog.ScannerPluginArgs{}
err := arg_parser.ExtractArgsWithContext(ctx, scope, args, arg)
if err != nil {
scope.Log("watch_jsonl: %v", err)
return
}
err = vql_subsystem.CheckFilesystemAccess(scope, arg.Accessor)
if err != nil {
scope.Log("watch_jsonl: %v", err)
return
}
// This plugin needs to be running on clients which have no
// server config object.
client_config_obj, ok := artifacts.GetConfig(scope)
if !ok {
scope.Log("watch_jsonl: unable to get config")
return
}
config_obj := &config_proto.Config{Client: client_config_obj}
event_channel := make(chan vfilter.Row)
// Register the output channel as a listener to the
// global event.
for _, filename := range arg.Filenames {
cancel := syslog.GlobalSyslogService(config_obj).Register(
filename, arg.Accessor, ctx, scope,
event_channel)
defer cancel()
}
// Wait until the query is complete.
for {
select {
case <-ctx.Done():
return
case event, ok := <-event_channel:
if !ok {
return
}
// Get the line from the event
line := vql_subsystem.GetStringFromRow(scope, event, "Line")
if line == "" {
continue
}
json_event := ordereddict.NewDict()
err := json.Unmarshal([]byte(line), json_event)
if err != nil {
scope.Log("Invalid jsonl: %v\n", line)
continue
}
select {
case <-ctx.Done():
return
case output_chan <- json_event:
}
}
}
}()
return output_chan
}
func init() {
vql_subsystem.RegisterFunction(&ParseJsonFunction{})
vql_subsystem.RegisterFunction(&ParseJsonArray{})
@@ -623,4 +721,5 @@ func init() {
vql_subsystem.RegisterPlugin(&ParseJsonArrayPlugin{})
vql_subsystem.RegisterPlugin(&ParseJsonlPlugin{})
vql_subsystem.RegisterPlugin(&WriteJSONPlugin{})
vql_subsystem.RegisterPlugin(&WatchJsonlPlugin{})
}
+1 -1
View File
@@ -150,7 +150,7 @@ func (self WatchAuditdPlugin) Call(
}
}()
scanner := _WatchSyslogPlugin{}
scanner := WatchSyslogPlugin{}
for row := range scanner.Call(ctx, scope, args) {
line, pres := scope.Associative(row, "Line")
if !pres {
@@ -0,0 +1,59 @@
{
"Short Lines": [
{
"Line": "0123456701"
},
{
"Line": "0123456702"
},
{
"Line": "0123456703"
}
],
"Long Lines": [
{
"Line": "01234567890123456701"
},
{
"Line": "01234567890123456702"
},
{
"Line": "01234567890123456703"
}
],
"Broken Line": [
{
"Line": "0123456no crlfand this is crlf"
}
],
"Broken Buffer": [
{
"Line": "01234567890123456701,01234567890123456702,01234567"
},
{
"Line": "890123456703"
}
],
"Lines after file rewind": [
{
"Line": "0123456701"
},
{
"Line": "0123456702"
},
{
"Line": "0123456703"
}
],
"Empty lines": [
{
"Line": ""
},
{
"Line": ""
},
{
"Line": ""
}
]
}
+4 -4
View File
@@ -94,9 +94,9 @@ func (self ScannerPlugin) Call(
return output_chan
}
type _WatchSyslogPlugin struct{}
type WatchSyslogPlugin struct{}
func (self _WatchSyslogPlugin) Call(
func (self WatchSyslogPlugin) Call(
ctx context.Context,
scope vfilter.Scope,
args *ordereddict.Dict) <-chan vfilter.Row {
@@ -160,7 +160,7 @@ func (self _WatchSyslogPlugin) Call(
return output_chan
}
func (self _WatchSyslogPlugin) Info(scope vfilter.Scope, type_map *vfilter.TypeMap) *vfilter.PluginInfo {
func (self WatchSyslogPlugin) Info(scope vfilter.Scope, type_map *vfilter.TypeMap) *vfilter.PluginInfo {
return &vfilter.PluginInfo{
Name: "watch_syslog",
Doc: "Watch a syslog file and stream events from it. ",
@@ -193,6 +193,6 @@ func maybeOpenGzip(scope vfilter.Scope,
}
func init() {
vql_subsystem.RegisterPlugin(&_WatchSyslogPlugin{})
vql_subsystem.RegisterPlugin(&WatchSyslogPlugin{})
vql_subsystem.RegisterPlugin(&ScannerPlugin{})
}
+107 -50
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"io"
"os"
"sync"
"time"
@@ -16,11 +17,6 @@ import (
"www.velocidex.com/golang/vfilter"
)
const (
FREQUENCY = 3 * time.Second
BUFFER_SIZE = 16 * 1024
)
var (
mu sync.Mutex
gSyslogService *SyslogWatcherService
@@ -43,10 +39,30 @@ type SyslogWatcherService struct {
config_obj *config_proto.Config
registrations map[string][]*Handle
sleep_time time.Duration
buffer_size int64
monitor_count int
}
func NewSyslogWatcherService(config_obj *config_proto.Config) *SyslogWatcherService {
sleep_time := 3 * time.Second
buffer_size := int64(16 * 1024)
if config_obj.Defaults != nil {
if config_obj.Defaults.WatchPluginFrequency > 0 {
sleep_time = time.Second * time.Duration(
config_obj.Defaults.WatchPluginFrequency)
}
if config_obj.Defaults.WatchPluginBufferSize > 0 {
buffer_size = config_obj.Defaults.WatchPluginBufferSize
}
}
return &SyslogWatcherService{
sleep_time: sleep_time,
buffer_size: buffer_size,
config_obj: config_obj,
registrations: make(map[string][]*Handle),
}
@@ -126,7 +142,7 @@ func (self *SyslogWatcherService) StartMonitoring(
cursor = self.monitorOnce(filename, accessor_name, accessor, cursor)
time.Sleep(FREQUENCY)
time.Sleep(self.sleep_time)
}
}
@@ -148,7 +164,7 @@ func (self *SyslogWatcherService) findLastLineOffset(
defer fd.Close()
cursor.file_size = stat.Size()
last_offset := cursor.file_size - BUFFER_SIZE
last_offset := cursor.file_size - self.buffer_size
if last_offset < 0 {
last_offset = 0
}
@@ -159,7 +175,7 @@ func (self *SyslogWatcherService) findLastLineOffset(
return cursor
}
buff := make([]byte, BUFFER_SIZE)
buff := make([]byte, self.buffer_size)
n, err := fd.Read(buff)
if err != nil && err != io.EOF {
return cursor
@@ -182,7 +198,10 @@ func (self *SyslogWatcherService) monitorOnce(
cursor *Cursor) *Cursor {
self.mu.Lock()
defer self.mu.Unlock()
defer func() {
self.monitor_count++
self.mu.Unlock()
}()
stat, err := accessor.LstatWithOSPath(filename)
if err != nil {
@@ -198,7 +217,7 @@ func (self *SyslogWatcherService) monitorOnce(
// file was truncated.
if stat.Size() < cursor.file_size {
logger := logging.GetLogger(self.config_obj, &logging.ClientComponent)
logger.Info("File size (%v) is smaller than last know size (%v) - assuming file was truncated. Will start reading at the start again.",
logger.Info("File size (%v) is smaller than last known size (%v) - assuming file was truncated. Will start reading at the start again.",
stat.Size(), cursor.file_size)
cursor.last_line_offset = 0
}
@@ -217,55 +236,86 @@ func (self *SyslogWatcherService) monitorOnce(
}
defer fd.Close()
// File must be seekable
pos, err := fd.Seek(cursor.last_line_offset, 0)
if err != nil {
return cursor
}
for {
// Only read up to the last offset in case the file grows more.
to_read := cursor.file_size - cursor.last_line_offset
if to_read > self.buffer_size {
to_read = self.buffer_size
}
// File is truncated, start reading from the front again.
if cursor.last_line_offset != pos {
cursor.last_line_offset = 0
return cursor
}
if to_read == 0 {
return cursor
}
buff := make([]byte, BUFFER_SIZE)
n, err := fd.Read(buff)
if err != nil && err != io.EOF {
return cursor
}
// File must be seekable
pos, err := fd.Seek(cursor.last_line_offset, os.SEEK_SET)
if err != nil {
return cursor
}
buff = buff[:n]
offset := 0
// File is truncated, start reading from the front again.
if cursor.last_line_offset != pos {
cursor.last_line_offset = 0
return cursor
}
// Read whole lines inside the buffer
for len(buff)-offset > 0 {
new_lf := bytes.IndexByte(buff[offset:], '\n')
if new_lf > 0 {
new_handles := self.distributeLine(
string(buff[offset:offset+new_lf]), filename, key, handles)
buff := make([]byte, to_read)
n, err := fd.Read(buff)
if err != nil && err != io.EOF {
return cursor
}
// No more listeners - we dont care any more.
if len(new_handles) == 0 {
delete(self.registrations, key)
buff = buff[:n]
// Read whole lines inside the buffer
for len(buff) > 0 {
new_lf := bytes.IndexByte(buff, '\n')
if new_lf >= 0 {
cursor.last_line_offset += int64(new_lf) + 1
handles = self.distributeLine(
string(buff[:new_lf]), filename, key, handles)
// No more listeners - we dont care any more.
if len(handles) == 0 {
return cursor
}
// Advance the cursor to the next line
buff = buff[new_lf+1:]
// Get next line
continue
}
// The entire buffer does not contain lf at all - we skip
// the entire buffer and hope to get a line on the next
// read.
if len(buff) == n {
// The last line is just too long for the buffer. We
// break it up into multiple lines to fit.
if cursor.file_size-cursor.last_line_offset >
self.buffer_size {
cursor.last_line_offset += int64(len(buff))
handles = self.distributeLine(
string(buff), filename, key, handles)
// No more listeners - we dont care any more.
if len(handles) == 0 {
return cursor
}
// Drop the buffer and read some more.
break
}
return cursor
}
// Update the registrations - possibly
// omitting finished listeners.
self.registrations[key] = new_handles
handles = new_handles
cursor.last_line_offset += int64(new_lf) + 1
offset += new_lf + 1
} else {
// The buffer does not contain lf at all - we
// skip the entire buffer and hope to get a
// line on the next read.
cursor.last_line_offset += int64(len(buff) - offset)
return cursor
// Abandon this buffer and try again
break
}
}
return cursor
@@ -290,6 +340,13 @@ func (self *SyslogWatcherService) distributeLine(
}
}
// Update the registrations - possibly omitting finished
// listeners.
if len(new_handles) == 0 {
delete(self.registrations, key)
}
self.registrations[key] = new_handles
return new_handles
}
+302
View File
@@ -0,0 +1,302 @@
package syslog
import (
"context"
"io/ioutil"
"os"
"sync"
"testing"
"time"
"github.com/Velocidex/ordereddict"
"github.com/sebdah/goldie"
"github.com/stretchr/testify/suite"
"www.velocidex.com/golang/velociraptor/accessors"
config_proto "www.velocidex.com/golang/velociraptor/config/proto"
"www.velocidex.com/golang/velociraptor/file_store/test_utils"
"www.velocidex.com/golang/velociraptor/json"
"www.velocidex.com/golang/velociraptor/logging"
"www.velocidex.com/golang/velociraptor/services"
"www.velocidex.com/golang/velociraptor/vql/acl_managers"
"www.velocidex.com/golang/velociraptor/vtesting"
"www.velocidex.com/golang/velociraptor/vtesting/assert"
"www.velocidex.com/golang/vfilter"
_ "www.velocidex.com/golang/velociraptor/accessors/file"
)
type SyslogWatcherTestSuite struct {
test_utils.TestSuite
mu sync.Mutex
result []vfilter.Row
output_chan chan vfilter.Row
scope vfilter.Scope
temp_file string
filename *accessors.OSPath
wg sync.WaitGroup
cancel func()
}
func (self *SyslogWatcherTestSuite) SetupTest() {
self.TestSuite.SetupTest()
// Change the normal frequency to very long so it does not
// interfere with our test.
self.ConfigObj.Defaults = &config_proto.Defaults{
WatchPluginFrequency: 10000,
// Make the buffer small so we can test exceeding it
WatchPluginBufferSize: 50,
}
self.output_chan = make(chan vfilter.Row)
ctx, cancel := context.WithCancel(self.Ctx)
self.cancel = cancel
self.wg.Add(1)
go func() {
defer self.wg.Done()
defer close(self.output_chan)
for {
select {
case <-ctx.Done():
return
case item, ok := <-self.output_chan:
if !ok {
return
}
self.mu.Lock()
self.result = append(self.result, item)
self.mu.Unlock()
}
}
}()
builder := services.ScopeBuilder{
Config: self.ConfigObj,
ACLManager: acl_managers.NullACLManager{},
Logger: logging.NewPlainLogger(
self.ConfigObj, &logging.FrontendComponent),
Env: ordereddict.NewDict(),
}
manager, err := services.GetRepositoryManager(self.ConfigObj)
assert.NoError(self.T(), err)
self.scope = manager.BuildScope(builder)
fd, err := ioutil.TempFile("", "tmp")
assert.NoError(self.T(), err)
fd.Close()
self.temp_file = fd.Name()
self.truncateFile()
self.filename, err = accessors.NewGenericOSPath(self.temp_file)
assert.NoError(self.T(), err)
}
func (self *SyslogWatcherTestSuite) TearDownTest() {
self.cancel()
self.wg.Wait()
}
func (self *SyslogWatcherTestSuite) truncateFile() {
fd, err := os.OpenFile(self.temp_file,
os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
assert.NoError(self.T(), err)
fd.Close()
}
func (self *SyslogWatcherTestSuite) appendData(data string) {
fd, err := os.OpenFile(self.temp_file,
os.O_RDWR|os.O_CREATE, 0600)
assert.NoError(self.T(), err)
defer fd.Close()
fd.Seek(0, os.SEEK_END)
_, err = fd.Write([]byte(data))
assert.NoError(self.T(), err)
}
func (self *SyslogWatcherTestSuite) clearLines() {
self.mu.Lock()
defer self.mu.Unlock()
self.result = nil
}
func (self *SyslogWatcherTestSuite) getLines() []vfilter.Row {
self.mu.Lock()
defer self.mu.Unlock()
result := []vfilter.Row{}
for _, i := range self.result {
result = append(result, i)
}
return result
}
func (self *SyslogWatcherTestSuite) TestSyslogReader() {
service := NewSyslogWatcherService(self.ConfigObj)
golden := ordereddict.NewDict()
// Register a watcher
accessor, err := accessors.GetAccessor("file", self.scope)
assert.NoError(self.T(), err)
ctx := context.Background()
closer := service.Register(self.filename, "file",
ctx, self.scope, self.output_chan)
defer closer()
// Registering a watcher will scan the file once. We need to wait
// until it is over before we start the test.
vtesting.WaitUntil(time.Second, self.T(), func() bool {
service.mu.Lock()
defer service.mu.Unlock()
return service.monitor_count > 0
})
// Find the last line
cursor := service.findLastLineOffset(self.filename, accessor)
assert.Equal(self.T(), cursor.last_line_offset, int64(0))
self.appendData("This is the first line\nSecond Line\n")
// Find the last line again
cursor = service.findLastLineOffset(self.filename, accessor)
assert.Equal(self.T(), cursor.last_line_offset, int64(35))
assert.Equal(self.T(), cursor.file_size, int64(35))
// Pull the next line off - no new data
cursor = service.monitorOnce(self.filename, "file", accessor, cursor)
assert.Equal(self.T(), cursor.last_line_offset, int64(35))
// Write 3 short full lines (10 bytes + \n * 3 = 33 bytes). This
// should fit in a single buffer read (50 bytes)
self.appendData("0123456701\n0123456702\n0123456703\n")
cursor = service.monitorOnce(self.filename, "file", accessor, cursor)
// Make sure we consumed all the data since it ends on \n
assert.Equal(self.T(), cursor.last_line_offset, cursor.file_size)
// Wait for 3 messages to be distributed
vtesting.WaitUntil(time.Second, self.T(), func() bool {
return len(self.getLines()) == 3
})
golden.Set("Short Lines", self.getLines())
self.clearLines()
// Write 3 longer full lines (20 bytes + \n * 3 = 63 bytes). While
// each line should fit in the buffer (50 bytes) the whole data
// since the last cursor will not at once.
self.appendData("01234567890123456701\n01234567890123456702\n01234567890123456703\n")
cursor = service.monitorOnce(self.filename, "file", accessor, cursor)
assert.Equal(self.T(), cursor.last_line_offset, cursor.file_size)
// Wait for 3 messages to be distributed
vtesting.WaitUntil(time.Second, self.T(), func() bool {
return len(self.getLines()) == 3
})
golden.Set("Long Lines", self.getLines())
// Write data with no crlf should not advance cursor.
// each line should fit in the buffer (50 bytes) the whole data
// since the last cursor will not at once.
self.appendData("0123456no crlf")
self.clearLines()
cursor = service.monitorOnce(self.filename, "file", accessor, cursor)
// Cursor has not consumed the last line because there is no \n
assert.True(self.T(), cursor.last_line_offset < cursor.file_size)
self.appendData("and this is crlf\n")
cursor = service.monitorOnce(self.filename, "file", accessor, cursor)
// All data is consumed
assert.Equal(self.T(), cursor.last_line_offset, cursor.file_size)
// Wait for 3 messages to be distributed
vtesting.WaitUntil(time.Second, self.T(), func() bool {
return len(self.getLines()) == 1
})
golden.Set("Broken Line", self.getLines())
self.clearLines()
// Now send a single line which is too long to fit in the
// buffer. Total length 63 bytes
self.appendData("01234567890123456701,01234567890123456702,01234567890123456703\n")
// We still read all the way to the end but we split the data over
// multiple lines.
cursor = service.monitorOnce(self.filename, "file", accessor, cursor)
assert.Equal(self.T(), cursor.last_line_offset, cursor.file_size)
// Message is broken across multiple lines because it is too long
// to fit in one buffer.
vtesting.WaitUntil(time.Second, self.T(), func() bool {
return len(self.getLines()) == 2
})
golden.Set("Broken Buffer", self.getLines())
// Now test file truncation.
self.truncateFile()
self.clearLines()
// Write 3 short full lines (10 bytes + \n * 3 = 33 bytes). This
// should fit in a single buffer read (50 bytes)
self.appendData("0123456701\n0123456702\n0123456703\n")
// The new curser is rewinded to the start of the file.
old_curser := *cursor
new_cursor := service.monitorOnce(self.filename, "file", accessor, cursor)
assert.True(self.T(), new_cursor.last_line_offset < old_curser.last_line_offset)
assert.Equal(self.T(), new_cursor.last_line_offset, new_cursor.file_size)
vtesting.WaitUntil(time.Second, self.T(), func() bool {
return len(self.getLines()) == 3
})
golden.Set("Lines after file rewind", self.getLines())
self.clearLines()
// Now write empty lines.
self.appendData("\n\n\n")
// Lines are consumed
cursor = service.monitorOnce(self.filename, "file", accessor, cursor)
assert.Equal(self.T(), cursor.last_line_offset, cursor.file_size)
vtesting.WaitUntil(time.Second, self.T(), func() bool {
return len(self.getLines()) == 3
})
golden.Set("Empty lines", self.getLines())
goldie.Assert(self.T(), "TestSyslogReader",
json.MustMarshalIndent(golden))
}
func TestSyslogWatcher(t *testing.T) {
suite.Run(t, &SyslogWatcherTestSuite{})
}