From 5978beb7ad856824233bc2c9f066a743b2c220ff Mon Sep 17 00:00:00 2001 From: William Findlay Date: Thu, 15 Feb 2024 12:33:47 -0500 Subject: [PATCH] filters: implement capability filters Implement a new export filter for process capabilities. The filter includes support for matching effective, permitted, and inheritable capability sets, with various match behaviours. Behaviours include: - `any`: matches if one or more listed capabilities appear in process caps - `all`: matches if all listed capabilities appear in process caps - `exactly`: matches if listed capabilities are exactly equal to process caps - `none`: matches if no listed capabilities are in process caps The following are some example uses. Match when effective capabilities include either CAP_SYS_ADMIN or CAP_BPF: {"capabilities": {"effective": {"any": ["CAP_SYS_ADMIN", "CAP_BPF"]}}} Match when permitted capabilities do not include CAP_SYS_ADMIN: {"capabilities": {"permitted": {"none": ["CAP_SYS_ADMIN", "CAP_BPF"]}}} Match when inheritable capabilities include either CAP_SYS_ADMIN and CAP_BPF: {"capabilities": {"inheritable": {"all": ["CAP_SYS_ADMIN", "CAP_BPF"]}}} Match when inheritable capabilities are exactly CAP_SYS_ADMIN: {"capabilities": {"inheritable": {"exactly": ["CAP_SYS_ADMIN"]}}} Signed-off-by: William Findlay --- api/v1/README.md | 41 ++ api/v1/tetragon/events.pb.go | 629 ++++++++++++------ api/v1/tetragon/events.pb.json.go | 32 + api/v1/tetragon/events.proto | 252 ++++--- docs/content/en/docs/reference/grpc-api.md | 27 + go.mod | 1 + go.sum | 2 + pkg/filters/caps.go | 114 ++++ pkg/filters/caps_test.go | 201 ++++++ pkg/filters/filters.go | 1 + pkg/filters/filters_test.go | 10 +- .../tetragon/api/v1/tetragon/events.pb.go | 629 ++++++++++++------ .../api/v1/tetragon/events.pb.json.go | 32 + .../tetragon/api/v1/tetragon/events.proto | 252 ++++--- .../deckarep/golang-set/v2/.gitignore | 23 + .../github.com/deckarep/golang-set/v2/LICENSE | 22 + .../deckarep/golang-set/v2/README.md | 181 +++++ .../deckarep/golang-set/v2/iterator.go | 58 ++ .../deckarep/golang-set/v2/new_improved.jpeg | Bin 0 -> 120935 bytes .../github.com/deckarep/golang-set/v2/set.go | 255 +++++++ .../deckarep/golang-set/v2/sorted.go | 42 ++ .../deckarep/golang-set/v2/threadsafe.go | 299 +++++++++ .../deckarep/golang-set/v2/threadunsafe.go | 330 +++++++++ vendor/modules.txt | 3 + 24 files changed, 2813 insertions(+), 623 deletions(-) create mode 100644 pkg/filters/caps.go create mode 100644 pkg/filters/caps_test.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/.gitignore create mode 100644 vendor/github.com/deckarep/golang-set/v2/LICENSE create mode 100644 vendor/github.com/deckarep/golang-set/v2/README.md create mode 100644 vendor/github.com/deckarep/golang-set/v2/iterator.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg create mode 100644 vendor/github.com/deckarep/golang-set/v2/set.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/sorted.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/threadsafe.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/threadunsafe.go diff --git a/api/v1/README.md b/api/v1/README.md index 9f3030e7b30..89bcdf81782 100644 --- a/api/v1/README.md +++ b/api/v1/README.md @@ -59,6 +59,8 @@ - [tetragon/events.proto](#tetragon_events-proto) - [AggregationInfo](#tetragon-AggregationInfo) - [AggregationOptions](#tetragon-AggregationOptions) + - [CapFilter](#tetragon-CapFilter) + - [CapFilterSet](#tetragon-CapFilterSet) - [FieldFilter](#tetragon-FieldFilter) - [Filter](#tetragon-Filter) - [GetEventsRequest](#tetragon-GetEventsRequest) @@ -1109,6 +1111,44 @@ AggregationOptions defines configuration options for aggregating events. + + +### CapFilter +Filter over a set of Linux process capabilities. See `message Capabilities` +for more info. WARNING: Multiple sets are ANDed. For example, if the +permitted filter matches, but the effective filter does not, the filter will +NOT match. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| permitted | [CapFilterSet](#tetragon-CapFilterSet) | | Filter over the set of permitted capabilities. | +| effective | [CapFilterSet](#tetragon-CapFilterSet) | | Filter over the set of effective capabilities. | +| inheritable | [CapFilterSet](#tetragon-CapFilterSet) | | Filter over the set of inheritable capabilities. | + + + + + + + + +### CapFilterSet +Capability set to filter over. NOTE: you may specify only ONE set here. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| any | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set contains any of the capabilities defined in this filter. | +| all | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set contains all of the capabilities defined in this filter. | +| exactly | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set exactly matches all of the capabilities defined in this filter. | +| none | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set contains none of the capabilities defined in this filter. | + + + + + + ### FieldFilter @@ -1145,6 +1185,7 @@ AggregationOptions defines configuration options for aggregating events. | arguments_regex | [string](#string) | repeated | Filter by process.arguments field using RE2 regular expression syntax: https://github.com/google/re2/wiki/Syntax | | labels | [string](#string) | repeated | Filter events by pod labels using Kubernetes label selector syntax: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors Note that this filter never matches events without the pod field (i.e. host process events). | | policy_names | [string](#string) | repeated | Filter events by tracing policy names | +| capabilities | [CapFilter](#tetragon-CapFilter) | | Filter events by Linux process capability | diff --git a/api/v1/tetragon/events.pb.go b/api/v1/tetragon/events.pb.go index 18065b1ffe4..64b259fbe4f 100644 --- a/api/v1/tetragon/events.pb.go +++ b/api/v1/tetragon/events.pb.go @@ -173,6 +173,8 @@ type Filter struct { Labels []string `protobuf:"bytes,9,rep,name=labels,proto3" json:"labels,omitempty"` // Filter events by tracing policy names PolicyNames []string `protobuf:"bytes,10,rep,name=policy_names,json=policyNames,proto3" json:"policy_names,omitempty"` + // Filter events by Linux process capability + Capabilities *CapFilter `protobuf:"bytes,11,opt,name=capabilities,proto3" json:"capabilities,omitempty"` } func (x *Filter) Reset() { @@ -277,6 +279,159 @@ func (x *Filter) GetPolicyNames() []string { return nil } +func (x *Filter) GetCapabilities() *CapFilter { + if x != nil { + return x.Capabilities + } + return nil +} + +// Filter over a set of Linux process capabilities. See `message Capabilities` +// for more info. WARNING: Multiple sets are ANDed. For example, if the +// permitted filter matches, but the effective filter does not, the filter will +// NOT match. +type CapFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Filter over the set of permitted capabilities. + Permitted *CapFilterSet `protobuf:"bytes,1,opt,name=permitted,proto3" json:"permitted,omitempty"` + // Filter over the set of effective capabilities. + Effective *CapFilterSet `protobuf:"bytes,2,opt,name=effective,proto3" json:"effective,omitempty"` + // Filter over the set of inheritable capabilities. + Inheritable *CapFilterSet `protobuf:"bytes,3,opt,name=inheritable,proto3" json:"inheritable,omitempty"` +} + +func (x *CapFilter) Reset() { + *x = CapFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_tetragon_events_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CapFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CapFilter) ProtoMessage() {} + +func (x *CapFilter) ProtoReflect() protoreflect.Message { + mi := &file_tetragon_events_proto_msgTypes[1] + 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 CapFilter.ProtoReflect.Descriptor instead. +func (*CapFilter) Descriptor() ([]byte, []int) { + return file_tetragon_events_proto_rawDescGZIP(), []int{1} +} + +func (x *CapFilter) GetPermitted() *CapFilterSet { + if x != nil { + return x.Permitted + } + return nil +} + +func (x *CapFilter) GetEffective() *CapFilterSet { + if x != nil { + return x.Effective + } + return nil +} + +func (x *CapFilter) GetInheritable() *CapFilterSet { + if x != nil { + return x.Inheritable + } + return nil +} + +// Capability set to filter over. NOTE: you may specify only ONE set here. +type CapFilterSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Match if the capability set contains any of the capabilities defined in this filter. + Any []CapabilitiesType `protobuf:"varint,1,rep,packed,name=any,proto3,enum=tetragon.CapabilitiesType" json:"any,omitempty"` + // Match if the capability set contains all of the capabilities defined in this filter. + All []CapabilitiesType `protobuf:"varint,2,rep,packed,name=all,proto3,enum=tetragon.CapabilitiesType" json:"all,omitempty"` + // Match if the capability set exactly matches all of the capabilities defined in this filter. + Exactly []CapabilitiesType `protobuf:"varint,3,rep,packed,name=exactly,proto3,enum=tetragon.CapabilitiesType" json:"exactly,omitempty"` + // Match if the capability set contains none of the capabilities defined in this filter. + None []CapabilitiesType `protobuf:"varint,4,rep,packed,name=none,proto3,enum=tetragon.CapabilitiesType" json:"none,omitempty"` +} + +func (x *CapFilterSet) Reset() { + *x = CapFilterSet{} + if protoimpl.UnsafeEnabled { + mi := &file_tetragon_events_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CapFilterSet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CapFilterSet) ProtoMessage() {} + +func (x *CapFilterSet) ProtoReflect() protoreflect.Message { + mi := &file_tetragon_events_proto_msgTypes[2] + 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 CapFilterSet.ProtoReflect.Descriptor instead. +func (*CapFilterSet) Descriptor() ([]byte, []int) { + return file_tetragon_events_proto_rawDescGZIP(), []int{2} +} + +func (x *CapFilterSet) GetAny() []CapabilitiesType { + if x != nil { + return x.Any + } + return nil +} + +func (x *CapFilterSet) GetAll() []CapabilitiesType { + if x != nil { + return x.All + } + return nil +} + +func (x *CapFilterSet) GetExactly() []CapabilitiesType { + if x != nil { + return x.Exactly + } + return nil +} + +func (x *CapFilterSet) GetNone() []CapabilitiesType { + if x != nil { + return x.None + } + return nil +} + type FieldFilter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -295,7 +450,7 @@ type FieldFilter struct { func (x *FieldFilter) Reset() { *x = FieldFilter{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[1] + mi := &file_tetragon_events_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -308,7 +463,7 @@ func (x *FieldFilter) String() string { func (*FieldFilter) ProtoMessage() {} func (x *FieldFilter) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[1] + mi := &file_tetragon_events_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -321,7 +476,7 @@ func (x *FieldFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use FieldFilter.ProtoReflect.Descriptor instead. func (*FieldFilter) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{1} + return file_tetragon_events_proto_rawDescGZIP(), []int{3} } func (x *FieldFilter) GetEventSet() []EventType { @@ -381,7 +536,7 @@ type GetEventsRequest struct { func (x *GetEventsRequest) Reset() { *x = GetEventsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[2] + mi := &file_tetragon_events_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -394,7 +549,7 @@ func (x *GetEventsRequest) String() string { func (*GetEventsRequest) ProtoMessage() {} func (x *GetEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[2] + mi := &file_tetragon_events_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -407,7 +562,7 @@ func (x *GetEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetEventsRequest.ProtoReflect.Descriptor instead. func (*GetEventsRequest) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{2} + return file_tetragon_events_proto_rawDescGZIP(), []int{4} } func (x *GetEventsRequest) GetAllowList() []*Filter { @@ -455,7 +610,7 @@ type AggregationOptions struct { func (x *AggregationOptions) Reset() { *x = AggregationOptions{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[3] + mi := &file_tetragon_events_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -468,7 +623,7 @@ func (x *AggregationOptions) String() string { func (*AggregationOptions) ProtoMessage() {} func (x *AggregationOptions) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[3] + mi := &file_tetragon_events_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -481,7 +636,7 @@ func (x *AggregationOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use AggregationOptions.ProtoReflect.Descriptor instead. func (*AggregationOptions) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{3} + return file_tetragon_events_proto_rawDescGZIP(), []int{5} } func (x *AggregationOptions) GetWindowSize() *durationpb.Duration { @@ -511,7 +666,7 @@ type AggregationInfo struct { func (x *AggregationInfo) Reset() { *x = AggregationInfo{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[4] + mi := &file_tetragon_events_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -524,7 +679,7 @@ func (x *AggregationInfo) String() string { func (*AggregationInfo) ProtoMessage() {} func (x *AggregationInfo) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[4] + mi := &file_tetragon_events_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -537,7 +692,7 @@ func (x *AggregationInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AggregationInfo.ProtoReflect.Descriptor instead. func (*AggregationInfo) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{4} + return file_tetragon_events_proto_rawDescGZIP(), []int{6} } func (x *AggregationInfo) GetCount() uint64 { @@ -558,7 +713,7 @@ type RateLimitInfo struct { func (x *RateLimitInfo) Reset() { *x = RateLimitInfo{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[5] + mi := &file_tetragon_events_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -571,7 +726,7 @@ func (x *RateLimitInfo) String() string { func (*RateLimitInfo) ProtoMessage() {} func (x *RateLimitInfo) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[5] + mi := &file_tetragon_events_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -584,7 +739,7 @@ func (x *RateLimitInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use RateLimitInfo.ProtoReflect.Descriptor instead. func (*RateLimitInfo) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{5} + return file_tetragon_events_proto_rawDescGZIP(), []int{7} } func (x *RateLimitInfo) GetNumberOfDroppedProcessEvents() uint64 { @@ -628,7 +783,7 @@ type GetEventsResponse struct { func (x *GetEventsResponse) Reset() { *x = GetEventsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[6] + mi := &file_tetragon_events_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -641,7 +796,7 @@ func (x *GetEventsResponse) String() string { func (*GetEventsResponse) ProtoMessage() {} func (x *GetEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[6] + mi := &file_tetragon_events_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -654,7 +809,7 @@ func (x *GetEventsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetEventsResponse.ProtoReflect.Descriptor instead. func (*GetEventsResponse) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{6} + return file_tetragon_events_proto_rawDescGZIP(), []int{8} } func (m *GetEventsResponse) GetEvent() isGetEventsResponse_Event { @@ -806,143 +961,173 @@ var file_tetragon_events_proto_rawDesc = []byte{ 0x0a, 0x15, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x1a, 0x17, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x74, 0x72, - 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x02, - 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x61, - 0x72, 0x79, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, - 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x68, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x68, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x69, - 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x69, 0x64, - 0x53, 0x65, 0x74, 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, + 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x67, 0x6f, 0x6e, 0x2f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, + 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9f, 0x03, 0x0a, 0x06, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, + 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x69, 0x64, 0x5f, 0x73, + 0x65, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x69, 0x64, 0x53, 0x65, 0x74, + 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x53, + 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, + 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x72, 0x65, 0x67, + 0x65, 0x78, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, + 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0xb1, 0x01, 0x0a, + 0x09, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x53, 0x65, 0x74, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, + 0x12, 0x34, 0x0a, 0x09, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, + 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x65, 0x74, 0x52, 0x09, 0x65, 0x66, 0x66, + 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, + 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x53, 0x65, 0x74, 0x52, 0x0b, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x22, 0xd0, 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x65, + 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1a, + 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x12, + 0x2c, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, + 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x34, 0x0a, + 0x07, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1a, + 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x65, 0x78, 0x61, 0x63, + 0x74, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x6e, + 0x6f, 0x6e, 0x65, 0x22, 0xee, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x72, 0x65, 0x67, - 0x65, 0x78, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x52, 0x65, 0x67, - 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, - 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x72, 0x67, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xee, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, - 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, - 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x74, - 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x44, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, - 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x22, 0xfd, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2d, 0x0a, - 0x09, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x08, 0x64, 0x65, 0x6e, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x13, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x74, 0x72, - 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x22, 0x80, 0x01, 0x0a, 0x12, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, - 0x0a, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, - 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x27, 0x0a, 0x0f, 0x41, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, - 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x22, 0x57, 0x0a, 0x0d, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, 0x0a, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, - 0x66, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1c, - 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x50, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xab, 0x05, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, - 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, - 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x48, - 0x00, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x12, 0x3a, - 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x4c, 0x0a, 0x12, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, - 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x72, 0x61, 0x63, 0x65, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0e, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x75, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, - 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x26, - 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x18, 0xc0, 0xb8, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x48, 0x00, - 0x52, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xc1, 0xb8, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x52, 0x61, 0x74, - 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x61, - 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x10, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xea, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, - 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, - 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2a, 0xb1, 0x01, 0x0a, 0x09, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x4e, 0x44, 0x45, - 0x46, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x45, - 0x58, 0x45, 0x43, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, - 0x5f, 0x45, 0x58, 0x49, 0x54, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, - 0x53, 0x53, 0x5f, 0x4b, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x50, - 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x50, 0x4f, 0x49, 0x4e, - 0x54, 0x10, 0x0a, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x4c, - 0x4f, 0x41, 0x44, 0x45, 0x52, 0x10, 0x0b, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, - 0x53, 0x53, 0x5f, 0x55, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x0c, 0x12, 0x0a, 0x0a, 0x04, 0x54, - 0x45, 0x53, 0x54, 0x10, 0xc0, 0xb8, 0x02, 0x12, 0x15, 0x0a, 0x0f, 0x52, 0x41, 0x54, 0x45, 0x5f, - 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0xc1, 0xb8, 0x02, 0x2a, 0x2d, - 0x0a, 0x11, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x00, - 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x58, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x01, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, + 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, + 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x53, 0x65, 0x74, 0x22, 0xfd, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, + 0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x09, 0x64, 0x65, + 0x6e, 0x79, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, + 0x08, 0x64, 0x65, 0x6e, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x13, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, + 0x6e, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x73, 0x22, 0x80, 0x01, 0x0a, 0x12, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x77, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x27, 0x0a, 0x0f, 0x41, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x57, 0x0a, 0x0d, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x46, 0x0a, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x64, + 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1c, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x4f, 0x66, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xab, 0x05, 0x0a, 0x11, 0x47, 0x65, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x48, 0x00, 0x52, 0x0b, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x12, 0x3a, 0x0a, 0x0c, 0x70, + 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x5f, 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x4c, 0x0a, 0x12, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x5f, 0x75, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x18, 0xc0, 0xb8, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x65, + 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xc1, 0xb8, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x61, 0x74, 0x65, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0xe9, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x10, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xea, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x07, + 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2a, 0xb1, 0x01, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x10, 0x00, + 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x45, 0x43, + 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x45, 0x58, + 0x49, 0x54, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, + 0x4b, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x43, + 0x45, 0x53, 0x53, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x0a, + 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x4c, 0x4f, 0x41, 0x44, + 0x45, 0x52, 0x10, 0x0b, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, + 0x55, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x0c, 0x12, 0x0a, 0x0a, 0x04, 0x54, 0x45, 0x53, 0x54, + 0x10, 0xc0, 0xb8, 0x02, 0x12, 0x15, 0x0a, 0x0f, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x4c, 0x49, 0x4d, + 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0xc1, 0xb8, 0x02, 0x2a, 0x2d, 0x0a, 0x11, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x45, 0x58, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -958,56 +1143,67 @@ func file_tetragon_events_proto_rawDescGZIP() []byte { } var file_tetragon_events_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_tetragon_events_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_tetragon_events_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_tetragon_events_proto_goTypes = []interface{}{ (EventType)(0), // 0: tetragon.EventType (FieldFilterAction)(0), // 1: tetragon.FieldFilterAction (*Filter)(nil), // 2: tetragon.Filter - (*FieldFilter)(nil), // 3: tetragon.FieldFilter - (*GetEventsRequest)(nil), // 4: tetragon.GetEventsRequest - (*AggregationOptions)(nil), // 5: tetragon.AggregationOptions - (*AggregationInfo)(nil), // 6: tetragon.AggregationInfo - (*RateLimitInfo)(nil), // 7: tetragon.RateLimitInfo - (*GetEventsResponse)(nil), // 8: tetragon.GetEventsResponse - (*wrapperspb.BoolValue)(nil), // 9: google.protobuf.BoolValue - (*fieldmaskpb.FieldMask)(nil), // 10: google.protobuf.FieldMask - (*durationpb.Duration)(nil), // 11: google.protobuf.Duration - (*ProcessExec)(nil), // 12: tetragon.ProcessExec - (*ProcessExit)(nil), // 13: tetragon.ProcessExit - (*ProcessKprobe)(nil), // 14: tetragon.ProcessKprobe - (*ProcessTracepoint)(nil), // 15: tetragon.ProcessTracepoint - (*ProcessLoader)(nil), // 16: tetragon.ProcessLoader - (*ProcessUprobe)(nil), // 17: tetragon.ProcessUprobe - (*Test)(nil), // 18: tetragon.Test - (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp + (*CapFilter)(nil), // 3: tetragon.CapFilter + (*CapFilterSet)(nil), // 4: tetragon.CapFilterSet + (*FieldFilter)(nil), // 5: tetragon.FieldFilter + (*GetEventsRequest)(nil), // 6: tetragon.GetEventsRequest + (*AggregationOptions)(nil), // 7: tetragon.AggregationOptions + (*AggregationInfo)(nil), // 8: tetragon.AggregationInfo + (*RateLimitInfo)(nil), // 9: tetragon.RateLimitInfo + (*GetEventsResponse)(nil), // 10: tetragon.GetEventsResponse + (*wrapperspb.BoolValue)(nil), // 11: google.protobuf.BoolValue + (CapabilitiesType)(0), // 12: tetragon.CapabilitiesType + (*fieldmaskpb.FieldMask)(nil), // 13: google.protobuf.FieldMask + (*durationpb.Duration)(nil), // 14: google.protobuf.Duration + (*ProcessExec)(nil), // 15: tetragon.ProcessExec + (*ProcessExit)(nil), // 16: tetragon.ProcessExit + (*ProcessKprobe)(nil), // 17: tetragon.ProcessKprobe + (*ProcessTracepoint)(nil), // 18: tetragon.ProcessTracepoint + (*ProcessLoader)(nil), // 19: tetragon.ProcessLoader + (*ProcessUprobe)(nil), // 20: tetragon.ProcessUprobe + (*Test)(nil), // 21: tetragon.Test + (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp } var file_tetragon_events_proto_depIdxs = []int32{ - 9, // 0: tetragon.Filter.health_check:type_name -> google.protobuf.BoolValue + 11, // 0: tetragon.Filter.health_check:type_name -> google.protobuf.BoolValue 0, // 1: tetragon.Filter.event_set:type_name -> tetragon.EventType - 0, // 2: tetragon.FieldFilter.event_set:type_name -> tetragon.EventType - 10, // 3: tetragon.FieldFilter.fields:type_name -> google.protobuf.FieldMask - 1, // 4: tetragon.FieldFilter.action:type_name -> tetragon.FieldFilterAction - 9, // 5: tetragon.FieldFilter.invert_event_set:type_name -> google.protobuf.BoolValue - 2, // 6: tetragon.GetEventsRequest.allow_list:type_name -> tetragon.Filter - 2, // 7: tetragon.GetEventsRequest.deny_list:type_name -> tetragon.Filter - 5, // 8: tetragon.GetEventsRequest.aggregation_options:type_name -> tetragon.AggregationOptions - 3, // 9: tetragon.GetEventsRequest.field_filters:type_name -> tetragon.FieldFilter - 11, // 10: tetragon.AggregationOptions.window_size:type_name -> google.protobuf.Duration - 12, // 11: tetragon.GetEventsResponse.process_exec:type_name -> tetragon.ProcessExec - 13, // 12: tetragon.GetEventsResponse.process_exit:type_name -> tetragon.ProcessExit - 14, // 13: tetragon.GetEventsResponse.process_kprobe:type_name -> tetragon.ProcessKprobe - 15, // 14: tetragon.GetEventsResponse.process_tracepoint:type_name -> tetragon.ProcessTracepoint - 16, // 15: tetragon.GetEventsResponse.process_loader:type_name -> tetragon.ProcessLoader - 17, // 16: tetragon.GetEventsResponse.process_uprobe:type_name -> tetragon.ProcessUprobe - 18, // 17: tetragon.GetEventsResponse.test:type_name -> tetragon.Test - 7, // 18: tetragon.GetEventsResponse.rate_limit_info:type_name -> tetragon.RateLimitInfo - 19, // 19: tetragon.GetEventsResponse.time:type_name -> google.protobuf.Timestamp - 6, // 20: tetragon.GetEventsResponse.aggregation_info:type_name -> tetragon.AggregationInfo - 21, // [21:21] is the sub-list for method output_type - 21, // [21:21] is the sub-list for method input_type - 21, // [21:21] is the sub-list for extension type_name - 21, // [21:21] is the sub-list for extension extendee - 0, // [0:21] is the sub-list for field type_name + 3, // 2: tetragon.Filter.capabilities:type_name -> tetragon.CapFilter + 4, // 3: tetragon.CapFilter.permitted:type_name -> tetragon.CapFilterSet + 4, // 4: tetragon.CapFilter.effective:type_name -> tetragon.CapFilterSet + 4, // 5: tetragon.CapFilter.inheritable:type_name -> tetragon.CapFilterSet + 12, // 6: tetragon.CapFilterSet.any:type_name -> tetragon.CapabilitiesType + 12, // 7: tetragon.CapFilterSet.all:type_name -> tetragon.CapabilitiesType + 12, // 8: tetragon.CapFilterSet.exactly:type_name -> tetragon.CapabilitiesType + 12, // 9: tetragon.CapFilterSet.none:type_name -> tetragon.CapabilitiesType + 0, // 10: tetragon.FieldFilter.event_set:type_name -> tetragon.EventType + 13, // 11: tetragon.FieldFilter.fields:type_name -> google.protobuf.FieldMask + 1, // 12: tetragon.FieldFilter.action:type_name -> tetragon.FieldFilterAction + 11, // 13: tetragon.FieldFilter.invert_event_set:type_name -> google.protobuf.BoolValue + 2, // 14: tetragon.GetEventsRequest.allow_list:type_name -> tetragon.Filter + 2, // 15: tetragon.GetEventsRequest.deny_list:type_name -> tetragon.Filter + 7, // 16: tetragon.GetEventsRequest.aggregation_options:type_name -> tetragon.AggregationOptions + 5, // 17: tetragon.GetEventsRequest.field_filters:type_name -> tetragon.FieldFilter + 14, // 18: tetragon.AggregationOptions.window_size:type_name -> google.protobuf.Duration + 15, // 19: tetragon.GetEventsResponse.process_exec:type_name -> tetragon.ProcessExec + 16, // 20: tetragon.GetEventsResponse.process_exit:type_name -> tetragon.ProcessExit + 17, // 21: tetragon.GetEventsResponse.process_kprobe:type_name -> tetragon.ProcessKprobe + 18, // 22: tetragon.GetEventsResponse.process_tracepoint:type_name -> tetragon.ProcessTracepoint + 19, // 23: tetragon.GetEventsResponse.process_loader:type_name -> tetragon.ProcessLoader + 20, // 24: tetragon.GetEventsResponse.process_uprobe:type_name -> tetragon.ProcessUprobe + 21, // 25: tetragon.GetEventsResponse.test:type_name -> tetragon.Test + 9, // 26: tetragon.GetEventsResponse.rate_limit_info:type_name -> tetragon.RateLimitInfo + 22, // 27: tetragon.GetEventsResponse.time:type_name -> google.protobuf.Timestamp + 8, // 28: tetragon.GetEventsResponse.aggregation_info:type_name -> tetragon.AggregationInfo + 29, // [29:29] is the sub-list for method output_type + 29, // [29:29] is the sub-list for method input_type + 29, // [29:29] is the sub-list for extension type_name + 29, // [29:29] is the sub-list for extension extendee + 0, // [0:29] is the sub-list for field type_name } func init() { file_tetragon_events_proto_init() } @@ -1016,6 +1212,7 @@ func file_tetragon_events_proto_init() { return } file_tetragon_tetragon_proto_init() + file_tetragon_capabilities_proto_init() if !protoimpl.UnsafeEnabled { file_tetragon_events_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Filter); i { @@ -1030,7 +1227,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FieldFilter); i { + switch v := v.(*CapFilter); i { case 0: return &v.state case 1: @@ -1042,7 +1239,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetEventsRequest); i { + switch v := v.(*CapFilterSet); i { case 0: return &v.state case 1: @@ -1054,7 +1251,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AggregationOptions); i { + switch v := v.(*FieldFilter); i { case 0: return &v.state case 1: @@ -1066,7 +1263,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AggregationInfo); i { + switch v := v.(*GetEventsRequest); i { case 0: return &v.state case 1: @@ -1078,7 +1275,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RateLimitInfo); i { + switch v := v.(*AggregationOptions); i { case 0: return &v.state case 1: @@ -1090,6 +1287,30 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AggregationInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tetragon_events_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RateLimitInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tetragon_events_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetEventsResponse); i { case 0: return &v.state @@ -1102,7 +1323,7 @@ func file_tetragon_events_proto_init() { } } } - file_tetragon_events_proto_msgTypes[6].OneofWrappers = []interface{}{ + file_tetragon_events_proto_msgTypes[8].OneofWrappers = []interface{}{ (*GetEventsResponse_ProcessExec)(nil), (*GetEventsResponse_ProcessExit)(nil), (*GetEventsResponse_ProcessKprobe)(nil), @@ -1118,7 +1339,7 @@ func file_tetragon_events_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tetragon_events_proto_rawDesc, NumEnums: 2, - NumMessages: 7, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/api/v1/tetragon/events.pb.json.go b/api/v1/tetragon/events.pb.json.go index 19b46bed27b..161fd1783e8 100644 --- a/api/v1/tetragon/events.pb.json.go +++ b/api/v1/tetragon/events.pb.json.go @@ -23,6 +23,38 @@ func (msg *Filter) UnmarshalJSON(b []byte) error { }.Unmarshal(b, msg) } +// MarshalJSON implements json.Marshaler +func (msg *CapFilter) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: true, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *CapFilter) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} + +// MarshalJSON implements json.Marshaler +func (msg *CapFilterSet) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: true, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *CapFilterSet) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} + // MarshalJSON implements json.Marshaler func (msg *FieldFilter) MarshalJSON() ([]byte, error) { return protojson.MarshalOptions{ diff --git a/api/v1/tetragon/events.proto b/api/v1/tetragon/events.proto index 54cbcfc6eab..81ccd53f795 100644 --- a/api/v1/tetragon/events.proto +++ b/api/v1/tetragon/events.proto @@ -6,6 +6,7 @@ syntax = "proto3"; package tetragon; import "tetragon/tetragon.proto"; +import "tetragon/capabilities.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; import "google/protobuf/timestamp.proto"; @@ -18,132 +19,169 @@ import "google/protobuf/field_mask.proto"; enum EventType { UNDEF = 0; - PROCESS_EXEC = 1; - PROCESS_EXIT = 5; - PROCESS_KPROBE = 9; - PROCESS_TRACEPOINT = 10; - PROCESS_LOADER = 11; - PROCESS_UPROBE = 12; + PROCESS_EXEC = 1; + PROCESS_EXIT = 5; + PROCESS_KPROBE = 9; + PROCESS_TRACEPOINT = 10; + PROCESS_LOADER = 11; + PROCESS_UPROBE = 12; - TEST = 40000; - RATE_LIMIT_INFO = 40001; + TEST = 40000; + RATE_LIMIT_INFO = 40001; } -message Filter { - repeated string binary_regex = 1; - repeated string namespace = 2; - google.protobuf.BoolValue health_check = 3; - repeated uint32 pid = 4; - // Filter by the PID of a process and any of its descendants. Note that this filter is - // intended for testing and development purposes only and should not be used in - // production. In particular, PID cycling in the OS over longer periods of time may - // cause unexpected events to pass this filter. - repeated uint32 pid_set = 5; - repeated EventType event_set = 6; - // Filter by process.pod.name field using RE2 regular expression syntax: - // https://github.com/google/re2/wiki/Syntax - repeated string pod_regex = 7; - // Filter by process.arguments field using RE2 regular expression syntax: - // https://github.com/google/re2/wiki/Syntax - repeated string arguments_regex = 8; - // Filter events by pod labels using Kubernetes label selector syntax: - // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors - // Note that this filter never matches events without the pod field (i.e. - // host process events). - repeated string labels = 9; - // Filter events by tracing policy names - repeated string policy_names = 10; +message Filter +{ + repeated string binary_regex = 1; + repeated string namespace = 2; + google.protobuf.BoolValue health_check = 3; + repeated uint32 pid = 4; + // Filter by the PID of a process and any of its descendants. Note that this filter is + // intended for testing and development purposes only and should not be used in + // production. In particular, PID cycling in the OS over longer periods of time may + // cause unexpected events to pass this filter. + repeated uint32 pid_set = 5; + repeated EventType event_set = 6; + // Filter by process.pod.name field using RE2 regular expression syntax: + // https://github.com/google/re2/wiki/Syntax + repeated string pod_regex = 7; + // Filter by process.arguments field using RE2 regular expression syntax: + // https://github.com/google/re2/wiki/Syntax + repeated string arguments_regex = 8; + // Filter events by pod labels using Kubernetes label selector syntax: + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors + // Note that this filter never matches events without the pod field (i.e. + // host process events). + repeated string labels = 9; + // Filter events by tracing policy names + repeated string policy_names = 10; + // Filter events by Linux process capability + CapFilter capabilities = 11; +} + +// Filter over a set of Linux process capabilities. See `message Capabilities` +// for more info. WARNING: Multiple sets are ANDed. For example, if the +// permitted filter matches, but the effective filter does not, the filter will +// NOT match. +message CapFilter +{ + // Filter over the set of permitted capabilities. + CapFilterSet permitted = 1; + // Filter over the set of effective capabilities. + CapFilterSet effective = 2; + // Filter over the set of inheritable capabilities. + CapFilterSet inheritable = 3; +} + +// Capability set to filter over. NOTE: you may specify only ONE set here. +message CapFilterSet +{ + // Match if the capability set contains any of the capabilities defined in this filter. + repeated CapabilitiesType any = 1; + // Match if the capability set contains all of the capabilities defined in this filter. + repeated CapabilitiesType all = 2; + // Match if the capability set exactly matches all of the capabilities defined in this filter. + repeated CapabilitiesType exactly = 3; + // Match if the capability set contains none of the capabilities defined in this filter. + repeated CapabilitiesType none = 4; } // Determines the behavior of a field filter enum FieldFilterAction { - INCLUDE = 0; - EXCLUDE = 1; + INCLUDE = 0; + EXCLUDE = 1; } -message FieldFilter { - // Event types to filter or undefined to filter over all event types. - repeated EventType event_set = 1; - // Fields to include or exclude. - google.protobuf.FieldMask fields = 2; - // Whether to include or exclude fields. - FieldFilterAction action = 3; - // Whether or not the event set filter should be inverted. - google.protobuf.BoolValue invert_event_set = 4; +message FieldFilter +{ + // Event types to filter or undefined to filter over all event types. + repeated EventType event_set = 1; + // Fields to include or exclude. + google.protobuf.FieldMask fields = 2; + // Whether to include or exclude fields. + FieldFilterAction action = 3; + // Whether or not the event set filter should be inverted. + google.protobuf.BoolValue invert_event_set = 4; } -message GetEventsRequest { - // allow_list specifies a list of filters to apply to only return certain - // events. If multiple filters are specified, at least one of them has to - // match for an event to be included in the results. - repeated Filter allow_list = 1; - // deny_list specifies a list of filters to apply to exclude certain events - // from the results. If multiple filters are specified, at least one of - // them has to match for an event to be excluded. - // If both allow_list and deny_list are specified, the results contain the - // set difference allow_list - deny_list. - repeated Filter deny_list = 2; - // aggregation_options configures aggregation options for this request. - // If this field is not set, responses will not be aggregated. - // Note that currently only process_accept and process_connect events are - // aggregated. Other events remain unaggregated. - AggregationOptions aggregation_options = 3; - // Fields to include or exclude for events in the GetEventsResponse. Omitting this - // field implies that all fields will be included. Exclusion always takes precedence - // over inclusion in the case of conflicts. - repeated FieldFilter field_filters = 4; +message GetEventsRequest +{ + // allow_list specifies a list of filters to apply to only return certain + // events. If multiple filters are specified, at least one of them has to + // match for an event to be included in the results. + repeated Filter allow_list = 1; + // deny_list specifies a list of filters to apply to exclude certain events + // from the results. If multiple filters are specified, at least one of + // them has to match for an event to be excluded. + // If both allow_list and deny_list are specified, the results contain the + // set difference allow_list - deny_list. + repeated Filter deny_list = 2; + // aggregation_options configures aggregation options for this request. + // If this field is not set, responses will not be aggregated. + // Note that currently only process_accept and process_connect events are + // aggregated. Other events remain unaggregated. + AggregationOptions aggregation_options = 3; + // Fields to include or exclude for events in the GetEventsResponse. Omitting this + // field implies that all fields will be included. Exclusion always takes precedence + // over inclusion in the case of conflicts. + repeated FieldFilter field_filters = 4; } // AggregationOptions defines configuration options for aggregating events. -message AggregationOptions { - // Aggregation window size. Defaults to 15 seconds if this field is not set. - google.protobuf.Duration window_size = 1; - // Size of the buffer for the aggregator to receive incoming events. If the - // buffer becomes full, the aggregator will log a warning and start dropping - // incoming events. - uint64 channel_buffer_size = 2; +message AggregationOptions +{ + // Aggregation window size. Defaults to 15 seconds if this field is not set. + google.protobuf.Duration window_size = 1; + // Size of the buffer for the aggregator to receive incoming events. If the + // buffer becomes full, the aggregator will log a warning and start dropping + // incoming events. + uint64 channel_buffer_size = 2; } // AggregationInfo contains information about aggregation results. -message AggregationInfo { - // Total count of events in this aggregation time window. - uint64 count = 1; +message AggregationInfo +{ + // Total count of events in this aggregation time window. + uint64 count = 1; } -message RateLimitInfo { - uint64 number_of_dropped_process_events = 1; +message RateLimitInfo +{ + uint64 number_of_dropped_process_events = 1; } -message GetEventsResponse { - // The type-specific fields of an event. - // - // NOTE: Numbers must stay in sync with enum EventType. - oneof event { - // ProcessExec event includes information about the execution of - // binaries and other related process metadata. - ProcessExec process_exec = 1; - // ProcessExit event indicates how and when a process terminates. - ProcessExit process_exit = 5; - // ProcessKprobe event contains information about the pre-defined - // functions and the process that invoked them. - ProcessKprobe process_kprobe = 9; - // ProcessTracepoint contains information about the pre-defined - // tracepoint and the process that invoked them. - ProcessTracepoint process_tracepoint = 10; - ProcessLoader process_loader = 11; - ProcessUprobe process_uprobe = 12; - - Test test = 40000; - RateLimitInfo rate_limit_info = 40001; - } - // Name of the node where this event was observed. - string node_name = 1000; - // Timestamp at which this event was observed. - // For an aggregated response, this field to set to the timestamp at which - // the event was observed for the first time in a given aggregation time window. - google.protobuf.Timestamp time = 1001; - - // aggregation_info contains information about aggregation results. This field - // is set only for aggregated responses. - AggregationInfo aggregation_info = 1002; +message GetEventsResponse +{ + // The type-specific fields of an event. + // + // NOTE: Numbers must stay in sync with enum EventType. + oneof event + { + // ProcessExec event includes information about the execution of + // binaries and other related process metadata. + ProcessExec process_exec = 1; + // ProcessExit event indicates how and when a process terminates. + ProcessExit process_exit = 5; + // ProcessKprobe event contains information about the pre-defined + // functions and the process that invoked them. + ProcessKprobe process_kprobe = 9; + // ProcessTracepoint contains information about the pre-defined + // tracepoint and the process that invoked them. + ProcessTracepoint process_tracepoint = 10; + ProcessLoader process_loader = 11; + ProcessUprobe process_uprobe = 12; + + Test test = 40000; + RateLimitInfo rate_limit_info = 40001; + } + // Name of the node where this event was observed. + string node_name = 1000; + // Timestamp at which this event was observed. + // For an aggregated response, this field to set to the timestamp at which + // the event was observed for the first time in a given aggregation time window. + google.protobuf.Timestamp time = 1001; + + // aggregation_info contains information about aggregation results. This field + // is set only for aggregated responses. + AggregationInfo aggregation_info = 1002; } diff --git a/docs/content/en/docs/reference/grpc-api.md b/docs/content/en/docs/reference/grpc-api.md index 39f5e8cf446..260e3edc22b 100644 --- a/docs/content/en/docs/reference/grpc-api.md +++ b/docs/content/en/docs/reference/grpc-api.md @@ -676,6 +676,32 @@ AggregationOptions defines configuration options for aggregating events. | window_size | [google.protobuf.Duration](#google-protobuf-Duration) | | Aggregation window size. Defaults to 15 seconds if this field is not set. | | channel_buffer_size | [uint64](#uint64) | | Size of the buffer for the aggregator to receive incoming events. If the buffer becomes full, the aggregator will log a warning and start dropping incoming events. | + + +### CapFilter +Filter over a set of Linux process capabilities. See `message Capabilities` +for more info. WARNING: Multiple sets are ANDed. For example, if the +permitted filter matches, but the effective filter does not, the filter will +NOT match. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| permitted | [CapFilterSet](#tetragon-CapFilterSet) | | Filter over the set of permitted capabilities. | +| effective | [CapFilterSet](#tetragon-CapFilterSet) | | Filter over the set of effective capabilities. | +| inheritable | [CapFilterSet](#tetragon-CapFilterSet) | | Filter over the set of inheritable capabilities. | + + + +### CapFilterSet +Capability set to filter over. NOTE: you may specify only ONE set here. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| any | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set contains any of the capabilities defined in this filter. | +| all | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set contains all of the capabilities defined in this filter. | +| exactly | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set exactly matches all of the capabilities defined in this filter. | +| none | [CapabilitiesType](#tetragon-CapabilitiesType) | repeated | Match if the capability set contains none of the capabilities defined in this filter. | + ### FieldFilter @@ -703,6 +729,7 @@ AggregationOptions defines configuration options for aggregating events. | arguments_regex | [string](#string) | repeated | Filter by process.arguments field using RE2 regular expression syntax: https://github.com/google/re2/wiki/Syntax | | labels | [string](#string) | repeated | Filter events by pod labels using Kubernetes label selector syntax: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors Note that this filter never matches events without the pod field (i.e. host process events). | | policy_names | [string](#string) | repeated | Filter events by tracing policy names | +| capabilities | [CapFilter](#tetragon-CapFilter) | | Filter events by Linux process capability | diff --git a/go.mod b/go.mod index 5112825d81b..99eedf990f6 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/cilium/tetragon/pkg/k8s v0.0.0-00010101000000-000000000000 github.com/containerd/cgroups v1.1.0 github.com/containerd/containerd v1.7.13 + github.com/deckarep/golang-set/v2 v2.6.0 github.com/fatih/color v1.16.0 github.com/go-openapi/strfmt v0.22.0 github.com/gogo/protobuf v1.3.2 diff --git a/go.sum b/go.sum index 67c573559f3..3e9b5ac1a80 100644 --- a/go.sum +++ b/go.sum @@ -107,6 +107,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v25.0.1+incompatible h1:k5TYd5rIVQRSqcTwCID+cyVA0yRg86+Pcrz1ls0/frA= diff --git a/pkg/filters/caps.go b/pkg/filters/caps.go new file mode 100644 index 00000000000..7c57debe6ef --- /dev/null +++ b/pkg/filters/caps.go @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package filters + +import ( + "context" + "fmt" + "strings" + + "github.com/cilium/tetragon/api/v1/tetragon" + hubbleV1 "github.com/cilium/tetragon/pkg/oldhubble/api/v1" + hubbleFilters "github.com/cilium/tetragon/pkg/oldhubble/filters" + mapset "github.com/deckarep/golang-set/v2" + "google.golang.org/protobuf/reflect/protoreflect" +) + +func filterSingleCapSet(caps []tetragon.CapabilitiesType, filters *tetragon.CapFilterSet) bool { + if filters == nil { + return true + } + + filterset := mapset.NewSet[tetragon.CapabilitiesType]() + + capset := mapset.NewSet[tetragon.CapabilitiesType]() + capset.Append(caps...) + + if filters.Any != nil && len(filters.Any) > 0 { + filterset.Append(filters.Any...) + return capset.ContainsAny(filterset.ToSlice()...) + } + + if filters.All != nil && len(filters.All) > 0 { + filterset.Append(filters.All...) + return capset.Intersect(filterset).Equal(filterset) + } + + if filters.Exactly != nil && len(filters.Exactly) > 0 { + filterset.Append(filters.Exactly...) + return capset.Equal(filterset) + } + + if filters.None != nil && len(filters.None) > 0 { + filterset.Append(filters.None...) + return capset.Intersect(filterset).IsEmpty() + } + + return false +} + +func filterByCaps(filter *tetragon.CapFilter) (hubbleFilters.FilterFunc, error) { + return func(ev *hubbleV1.Event) bool { + process := GetProcess(ev) + if process == nil { + return false + } + if process.ProcessCredentials == nil { + return false + } + caps := process.ProcessCredentials.Caps + if caps == nil { + return false + } + + return filterSingleCapSet(caps.Effective, filter.Effective) && + filterSingleCapSet(caps.Inheritable, filter.Inheritable) && + filterSingleCapSet(caps.Permitted, filter.Permitted) + }, nil +} + +type CapsFilter struct{} + +func ensure_single_set_defined(filter *tetragon.CapFilterSet) error { + if filter == nil { + return nil + } + defined := []string{} + filter.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + if v.Interface() == nil { + return true + } + defined = append(defined, string(fd.Name())) + return true + }) + if len(defined) > 1 { + return fmt.Errorf("capability filter may only define one match set, got: %s", strings.Join(defined[:], ", ")) + } + if len(defined) == 0 { + return fmt.Errorf("capability filter must define exactly one match set") + } + return nil +} + +func (f *CapsFilter) OnBuildFilter(_ context.Context, ff *tetragon.Filter) ([]hubbleFilters.FilterFunc, error) { + var fs []hubbleFilters.FilterFunc + if ff.Capabilities != nil { + if err := ensure_single_set_defined(ff.Capabilities.Permitted); err != nil { + return nil, err + } + if err := ensure_single_set_defined(ff.Capabilities.Effective); err != nil { + return nil, err + } + if err := ensure_single_set_defined(ff.Capabilities.Inheritable); err != nil { + return nil, err + } + + capFilters, err := filterByCaps(ff.Capabilities) + if err != nil { + return nil, err + } + fs = append(fs, capFilters) + } + return fs, nil +} diff --git a/pkg/filters/caps_test.go b/pkg/filters/caps_test.go new file mode 100644 index 00000000000..20f2633db78 --- /dev/null +++ b/pkg/filters/caps_test.go @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package filters + +import ( + "context" + "testing" + + "github.com/cilium/tetragon/api/v1/tetragon" + v1 "github.com/cilium/tetragon/pkg/oldhubble/api/v1" + "github.com/stretchr/testify/assert" +) + +func TestCapFilterAny(t *testing.T) { + f := []*tetragon.Filter{{Capabilities: &tetragon.CapFilter{Effective: &tetragon.CapFilterSet{ + Any: []tetragon.CapabilitiesType{tetragon.CapabilitiesType_CAP_SYS_ADMIN, tetragon.CapabilitiesType_CAP_BPF}, + }}}} + fl, err := BuildFilterList(context.Background(), f, []OnBuildFilter{&CapsFilter{}}) + assert.NoError(t, err) + process := tetragon.Process{ProcessCredentials: &tetragon.ProcessCredentials{ + Caps: &tetragon.Capabilities{}, + }} + ev := v1.Event{ + Event: &tetragon.GetEventsResponse{ + Event: &tetragon.GetEventsResponse_ProcessExec{ + ProcessExec: &tetragon.ProcessExec{ + Process: &process, + }, + }, + }, + } + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.True(t, fl.MatchOne(&ev), "both defined") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + tetragon.CapabilitiesType_CAP_SYS_BOOT, + } + assert.True(t, fl.MatchOne(&ev), "both defined with extra") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + } + assert.True(t, fl.MatchOne(&ev), "only cap_bpf defined") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.True(t, fl.MatchOne(&ev), "only cap_sysadmin_defined") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_AUDIT_READ, + } + assert.False(t, fl.MatchOne(&ev), "extra only") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{} + assert.False(t, fl.MatchOne(&ev), "empty") + process.ProcessCredentials.Caps.Effective = nil + assert.False(t, fl.MatchOne(&ev), "nil") +} + +func TestCapFilterAll(t *testing.T) { + f := []*tetragon.Filter{{Capabilities: &tetragon.CapFilter{Effective: &tetragon.CapFilterSet{ + All: []tetragon.CapabilitiesType{tetragon.CapabilitiesType_CAP_SYS_ADMIN, tetragon.CapabilitiesType_CAP_BPF}, + }}}} + fl, err := BuildFilterList(context.Background(), f, []OnBuildFilter{&CapsFilter{}}) + assert.NoError(t, err) + process := tetragon.Process{ProcessCredentials: &tetragon.ProcessCredentials{ + Caps: &tetragon.Capabilities{}, + }} + ev := v1.Event{ + Event: &tetragon.GetEventsResponse{ + Event: &tetragon.GetEventsResponse_ProcessExec{ + ProcessExec: &tetragon.ProcessExec{ + Process: &process, + }, + }, + }, + } + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.True(t, fl.MatchOne(&ev), "both defined") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + tetragon.CapabilitiesType_CAP_SYS_BOOT, + } + assert.True(t, fl.MatchOne(&ev), "both defined with extra") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + } + assert.False(t, fl.MatchOne(&ev), "only cap_bpf defined") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.False(t, fl.MatchOne(&ev), "only cap_sysadmin_defined") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_AUDIT_READ, + } + assert.False(t, fl.MatchOne(&ev), "extra only") + process.ProcessCredentials.Caps.Effective = []tetragon.CapabilitiesType{} + assert.False(t, fl.MatchOne(&ev), "empty") + process.ProcessCredentials.Caps.Effective = nil + assert.False(t, fl.MatchOne(&ev), "nil") +} + +func TestCapFilterExactly(t *testing.T) { + f := []*tetragon.Filter{{Capabilities: &tetragon.CapFilter{Inheritable: &tetragon.CapFilterSet{ + Exactly: []tetragon.CapabilitiesType{tetragon.CapabilitiesType_CAP_SYS_ADMIN, tetragon.CapabilitiesType_CAP_BPF}, + }}}} + fl, err := BuildFilterList(context.Background(), f, []OnBuildFilter{&CapsFilter{}}) + assert.NoError(t, err) + process := tetragon.Process{ProcessCredentials: &tetragon.ProcessCredentials{ + Caps: &tetragon.Capabilities{}, + }} + ev := v1.Event{ + Event: &tetragon.GetEventsResponse{ + Event: &tetragon.GetEventsResponse_ProcessExec{ + ProcessExec: &tetragon.ProcessExec{ + Process: &process, + }, + }, + }, + } + process.ProcessCredentials.Caps.Inheritable = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.True(t, fl.MatchOne(&ev), "both defined") + process.ProcessCredentials.Caps.Inheritable = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + tetragon.CapabilitiesType_CAP_SYS_BOOT, + } + assert.False(t, fl.MatchOne(&ev), "both defined with extra") + process.ProcessCredentials.Caps.Inheritable = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + } + assert.False(t, fl.MatchOne(&ev), "only cap_bpf defined") + process.ProcessCredentials.Caps.Inheritable = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.False(t, fl.MatchOne(&ev), "only cap_sysadmin_defined") + process.ProcessCredentials.Caps.Inheritable = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_AUDIT_READ, + } + assert.False(t, fl.MatchOne(&ev), "extra only") + process.ProcessCredentials.Caps.Inheritable = []tetragon.CapabilitiesType{} + assert.False(t, fl.MatchOne(&ev), "empty") + process.ProcessCredentials.Caps.Inheritable = nil + assert.False(t, fl.MatchOne(&ev), "nil") +} + +func TestCapFilterNone(t *testing.T) { + f := []*tetragon.Filter{{Capabilities: &tetragon.CapFilter{Permitted: &tetragon.CapFilterSet{ + None: []tetragon.CapabilitiesType{tetragon.CapabilitiesType_CAP_SYS_ADMIN, tetragon.CapabilitiesType_CAP_BPF}, + }}}} + fl, err := BuildFilterList(context.Background(), f, []OnBuildFilter{&CapsFilter{}}) + assert.NoError(t, err) + process := tetragon.Process{ProcessCredentials: &tetragon.ProcessCredentials{ + Caps: &tetragon.Capabilities{}, + }} + ev := v1.Event{ + Event: &tetragon.GetEventsResponse{ + Event: &tetragon.GetEventsResponse_ProcessExec{ + ProcessExec: &tetragon.ProcessExec{ + Process: &process, + }, + }, + }, + } + process.ProcessCredentials.Caps.Permitted = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.False(t, fl.MatchOne(&ev), "both defined") + process.ProcessCredentials.Caps.Permitted = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + tetragon.CapabilitiesType_CAP_SYS_BOOT, + } + assert.False(t, fl.MatchOne(&ev), "both defined with extra") + process.ProcessCredentials.Caps.Permitted = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_BPF, + } + assert.False(t, fl.MatchOne(&ev), "only cap_bpf defined") + process.ProcessCredentials.Caps.Permitted = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_SYS_ADMIN, + } + assert.False(t, fl.MatchOne(&ev), "only cap_sysadmin_defined") + process.ProcessCredentials.Caps.Permitted = []tetragon.CapabilitiesType{ + tetragon.CapabilitiesType_CAP_AUDIT_READ, + } + assert.True(t, fl.MatchOne(&ev), "extra only") + process.ProcessCredentials.Caps.Permitted = []tetragon.CapabilitiesType{} + assert.True(t, fl.MatchOne(&ev), "empty") + process.ProcessCredentials.Caps.Permitted = nil + assert.True(t, fl.MatchOne(&ev), "nil") +} diff --git a/pkg/filters/filters.go b/pkg/filters/filters.go index 5ac19226013..de78f6a3e1a 100644 --- a/pkg/filters/filters.go +++ b/pkg/filters/filters.go @@ -93,6 +93,7 @@ var Filters = []OnBuildFilter{ &LabelsFilter{}, &PodRegexFilter{}, &PolicyNamesFilter{}, + &CapsFilter{}, } func GetProcess(event *v1.Event) *tetragon.Process { diff --git a/pkg/filters/filters_test.go b/pkg/filters/filters_test.go index 189483b6d56..a90dc4e6c8b 100644 --- a/pkg/filters/filters_test.go +++ b/pkg/filters/filters_test.go @@ -22,7 +22,8 @@ func TestParseFilterList(t *testing.T) { {"binary_regex":["/usr/sbin/.*"],"namespace":["default"]} {"pid_set":[1]} {"event_set":["PROCESS_EXEC", "PROCESS_EXIT", "PROCESS_KPROBE", "PROCESS_TRACEPOINT"]} -{"arguments_regex":["^--version$","^-a -b -c$"]}` +{"arguments_regex":["^--version$","^-a -b -c$"]} +{"capabilities": {"effective": {"all": ["CAP_BPF", "CAP_SYS_ADMIN"]}}}` filterProto, err := ParseFilterList(f, true) assert.NoError(t, err) if diff := cmp.Diff( @@ -34,9 +35,16 @@ func TestParseFilterList(t *testing.T) { {PidSet: []uint32{1}}, {EventSet: []tetragon.EventType{tetragon.EventType_PROCESS_EXEC, tetragon.EventType_PROCESS_EXIT, tetragon.EventType_PROCESS_KPROBE, tetragon.EventType_PROCESS_TRACEPOINT}}, {ArgumentsRegex: []string{"^--version$", "^-a -b -c$"}}, + {Capabilities: &tetragon.CapFilter{ + Effective: &tetragon.CapFilterSet{ + All: []tetragon.CapabilitiesType{tetragon.CapabilitiesType_CAP_BPF, tetragon.CapabilitiesType_CAP_SYS_ADMIN}, + }, + }}, }, filterProto, cmpopts.IgnoreUnexported(tetragon.Filter{}), + cmpopts.IgnoreUnexported(tetragon.CapFilter{}), + cmpopts.IgnoreUnexported(tetragon.CapFilterSet{}), cmpopts.IgnoreUnexported(wrapperspb.BoolValue{}), ); diff != "" { t.Errorf("filter mismatch (-want +got):\n%s", diff) diff --git a/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.go b/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.go index 18065b1ffe4..64b259fbe4f 100644 --- a/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.go +++ b/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.go @@ -173,6 +173,8 @@ type Filter struct { Labels []string `protobuf:"bytes,9,rep,name=labels,proto3" json:"labels,omitempty"` // Filter events by tracing policy names PolicyNames []string `protobuf:"bytes,10,rep,name=policy_names,json=policyNames,proto3" json:"policy_names,omitempty"` + // Filter events by Linux process capability + Capabilities *CapFilter `protobuf:"bytes,11,opt,name=capabilities,proto3" json:"capabilities,omitempty"` } func (x *Filter) Reset() { @@ -277,6 +279,159 @@ func (x *Filter) GetPolicyNames() []string { return nil } +func (x *Filter) GetCapabilities() *CapFilter { + if x != nil { + return x.Capabilities + } + return nil +} + +// Filter over a set of Linux process capabilities. See `message Capabilities` +// for more info. WARNING: Multiple sets are ANDed. For example, if the +// permitted filter matches, but the effective filter does not, the filter will +// NOT match. +type CapFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Filter over the set of permitted capabilities. + Permitted *CapFilterSet `protobuf:"bytes,1,opt,name=permitted,proto3" json:"permitted,omitempty"` + // Filter over the set of effective capabilities. + Effective *CapFilterSet `protobuf:"bytes,2,opt,name=effective,proto3" json:"effective,omitempty"` + // Filter over the set of inheritable capabilities. + Inheritable *CapFilterSet `protobuf:"bytes,3,opt,name=inheritable,proto3" json:"inheritable,omitempty"` +} + +func (x *CapFilter) Reset() { + *x = CapFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_tetragon_events_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CapFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CapFilter) ProtoMessage() {} + +func (x *CapFilter) ProtoReflect() protoreflect.Message { + mi := &file_tetragon_events_proto_msgTypes[1] + 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 CapFilter.ProtoReflect.Descriptor instead. +func (*CapFilter) Descriptor() ([]byte, []int) { + return file_tetragon_events_proto_rawDescGZIP(), []int{1} +} + +func (x *CapFilter) GetPermitted() *CapFilterSet { + if x != nil { + return x.Permitted + } + return nil +} + +func (x *CapFilter) GetEffective() *CapFilterSet { + if x != nil { + return x.Effective + } + return nil +} + +func (x *CapFilter) GetInheritable() *CapFilterSet { + if x != nil { + return x.Inheritable + } + return nil +} + +// Capability set to filter over. NOTE: you may specify only ONE set here. +type CapFilterSet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Match if the capability set contains any of the capabilities defined in this filter. + Any []CapabilitiesType `protobuf:"varint,1,rep,packed,name=any,proto3,enum=tetragon.CapabilitiesType" json:"any,omitempty"` + // Match if the capability set contains all of the capabilities defined in this filter. + All []CapabilitiesType `protobuf:"varint,2,rep,packed,name=all,proto3,enum=tetragon.CapabilitiesType" json:"all,omitempty"` + // Match if the capability set exactly matches all of the capabilities defined in this filter. + Exactly []CapabilitiesType `protobuf:"varint,3,rep,packed,name=exactly,proto3,enum=tetragon.CapabilitiesType" json:"exactly,omitempty"` + // Match if the capability set contains none of the capabilities defined in this filter. + None []CapabilitiesType `protobuf:"varint,4,rep,packed,name=none,proto3,enum=tetragon.CapabilitiesType" json:"none,omitempty"` +} + +func (x *CapFilterSet) Reset() { + *x = CapFilterSet{} + if protoimpl.UnsafeEnabled { + mi := &file_tetragon_events_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CapFilterSet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CapFilterSet) ProtoMessage() {} + +func (x *CapFilterSet) ProtoReflect() protoreflect.Message { + mi := &file_tetragon_events_proto_msgTypes[2] + 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 CapFilterSet.ProtoReflect.Descriptor instead. +func (*CapFilterSet) Descriptor() ([]byte, []int) { + return file_tetragon_events_proto_rawDescGZIP(), []int{2} +} + +func (x *CapFilterSet) GetAny() []CapabilitiesType { + if x != nil { + return x.Any + } + return nil +} + +func (x *CapFilterSet) GetAll() []CapabilitiesType { + if x != nil { + return x.All + } + return nil +} + +func (x *CapFilterSet) GetExactly() []CapabilitiesType { + if x != nil { + return x.Exactly + } + return nil +} + +func (x *CapFilterSet) GetNone() []CapabilitiesType { + if x != nil { + return x.None + } + return nil +} + type FieldFilter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -295,7 +450,7 @@ type FieldFilter struct { func (x *FieldFilter) Reset() { *x = FieldFilter{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[1] + mi := &file_tetragon_events_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -308,7 +463,7 @@ func (x *FieldFilter) String() string { func (*FieldFilter) ProtoMessage() {} func (x *FieldFilter) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[1] + mi := &file_tetragon_events_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -321,7 +476,7 @@ func (x *FieldFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use FieldFilter.ProtoReflect.Descriptor instead. func (*FieldFilter) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{1} + return file_tetragon_events_proto_rawDescGZIP(), []int{3} } func (x *FieldFilter) GetEventSet() []EventType { @@ -381,7 +536,7 @@ type GetEventsRequest struct { func (x *GetEventsRequest) Reset() { *x = GetEventsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[2] + mi := &file_tetragon_events_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -394,7 +549,7 @@ func (x *GetEventsRequest) String() string { func (*GetEventsRequest) ProtoMessage() {} func (x *GetEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[2] + mi := &file_tetragon_events_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -407,7 +562,7 @@ func (x *GetEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetEventsRequest.ProtoReflect.Descriptor instead. func (*GetEventsRequest) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{2} + return file_tetragon_events_proto_rawDescGZIP(), []int{4} } func (x *GetEventsRequest) GetAllowList() []*Filter { @@ -455,7 +610,7 @@ type AggregationOptions struct { func (x *AggregationOptions) Reset() { *x = AggregationOptions{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[3] + mi := &file_tetragon_events_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -468,7 +623,7 @@ func (x *AggregationOptions) String() string { func (*AggregationOptions) ProtoMessage() {} func (x *AggregationOptions) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[3] + mi := &file_tetragon_events_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -481,7 +636,7 @@ func (x *AggregationOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use AggregationOptions.ProtoReflect.Descriptor instead. func (*AggregationOptions) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{3} + return file_tetragon_events_proto_rawDescGZIP(), []int{5} } func (x *AggregationOptions) GetWindowSize() *durationpb.Duration { @@ -511,7 +666,7 @@ type AggregationInfo struct { func (x *AggregationInfo) Reset() { *x = AggregationInfo{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[4] + mi := &file_tetragon_events_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -524,7 +679,7 @@ func (x *AggregationInfo) String() string { func (*AggregationInfo) ProtoMessage() {} func (x *AggregationInfo) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[4] + mi := &file_tetragon_events_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -537,7 +692,7 @@ func (x *AggregationInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AggregationInfo.ProtoReflect.Descriptor instead. func (*AggregationInfo) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{4} + return file_tetragon_events_proto_rawDescGZIP(), []int{6} } func (x *AggregationInfo) GetCount() uint64 { @@ -558,7 +713,7 @@ type RateLimitInfo struct { func (x *RateLimitInfo) Reset() { *x = RateLimitInfo{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[5] + mi := &file_tetragon_events_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -571,7 +726,7 @@ func (x *RateLimitInfo) String() string { func (*RateLimitInfo) ProtoMessage() {} func (x *RateLimitInfo) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[5] + mi := &file_tetragon_events_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -584,7 +739,7 @@ func (x *RateLimitInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use RateLimitInfo.ProtoReflect.Descriptor instead. func (*RateLimitInfo) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{5} + return file_tetragon_events_proto_rawDescGZIP(), []int{7} } func (x *RateLimitInfo) GetNumberOfDroppedProcessEvents() uint64 { @@ -628,7 +783,7 @@ type GetEventsResponse struct { func (x *GetEventsResponse) Reset() { *x = GetEventsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_tetragon_events_proto_msgTypes[6] + mi := &file_tetragon_events_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -641,7 +796,7 @@ func (x *GetEventsResponse) String() string { func (*GetEventsResponse) ProtoMessage() {} func (x *GetEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_tetragon_events_proto_msgTypes[6] + mi := &file_tetragon_events_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -654,7 +809,7 @@ func (x *GetEventsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetEventsResponse.ProtoReflect.Descriptor instead. func (*GetEventsResponse) Descriptor() ([]byte, []int) { - return file_tetragon_events_proto_rawDescGZIP(), []int{6} + return file_tetragon_events_proto_rawDescGZIP(), []int{8} } func (m *GetEventsResponse) GetEvent() isGetEventsResponse_Event { @@ -806,143 +961,173 @@ var file_tetragon_events_proto_rawDesc = []byte{ 0x0a, 0x15, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x1a, 0x17, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x74, 0x72, - 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x02, - 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x61, - 0x72, 0x79, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, - 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x68, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x68, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x69, - 0x64, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x69, 0x64, - 0x53, 0x65, 0x74, 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, + 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x67, 0x6f, 0x6e, 0x2f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, + 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9f, 0x03, 0x0a, 0x06, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, + 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x69, 0x6e, + 0x61, 0x72, 0x79, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, + 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x69, 0x64, 0x5f, 0x73, + 0x65, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x69, 0x64, 0x53, 0x65, 0x74, + 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x53, + 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, + 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x72, 0x65, 0x67, + 0x65, 0x78, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, + 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, + 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0xb1, 0x01, 0x0a, + 0x09, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x53, 0x65, 0x74, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, + 0x12, 0x34, 0x0a, 0x09, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, + 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x65, 0x74, 0x52, 0x09, 0x65, 0x66, 0x66, + 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, + 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x53, 0x65, 0x74, 0x52, 0x0b, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x22, 0xd0, 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x70, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x53, 0x65, + 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1a, + 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x12, + 0x2c, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, + 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x34, 0x0a, + 0x07, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1a, + 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x65, 0x78, 0x61, 0x63, + 0x74, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x70, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x6e, + 0x6f, 0x6e, 0x65, 0x22, 0xee, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x72, 0x65, 0x67, - 0x65, 0x78, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x52, 0x65, 0x67, - 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, - 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x72, 0x67, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xee, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, - 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x74, 0x72, - 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x74, - 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x44, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, - 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x22, 0xfd, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2d, 0x0a, - 0x09, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x08, 0x64, 0x65, 0x6e, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x13, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x74, 0x72, - 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x22, 0x80, 0x01, 0x0a, 0x12, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, - 0x0a, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, - 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x27, 0x0a, 0x0f, 0x41, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, - 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x22, 0x57, 0x0a, 0x0d, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, 0x0a, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, - 0x66, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1c, - 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x50, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xab, 0x05, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, - 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, - 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x48, - 0x00, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x12, 0x3a, - 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x4c, 0x0a, 0x12, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, - 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x72, 0x61, 0x63, 0x65, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0e, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x75, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, - 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x26, - 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x18, 0xc0, 0xb8, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x48, 0x00, - 0x52, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xc1, 0xb8, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x52, 0x61, 0x74, - 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x61, - 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x10, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xea, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, - 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, - 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2a, 0xb1, 0x01, 0x0a, 0x09, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x4e, 0x44, 0x45, - 0x46, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x45, - 0x58, 0x45, 0x43, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, - 0x5f, 0x45, 0x58, 0x49, 0x54, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, - 0x53, 0x53, 0x5f, 0x4b, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x50, - 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x50, 0x4f, 0x49, 0x4e, - 0x54, 0x10, 0x0a, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x4c, - 0x4f, 0x41, 0x44, 0x45, 0x52, 0x10, 0x0b, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, - 0x53, 0x53, 0x5f, 0x55, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x0c, 0x12, 0x0a, 0x0a, 0x04, 0x54, - 0x45, 0x53, 0x54, 0x10, 0xc0, 0xb8, 0x02, 0x12, 0x15, 0x0a, 0x0f, 0x52, 0x41, 0x54, 0x45, 0x5f, - 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0xc1, 0xb8, 0x02, 0x2a, 0x2d, - 0x0a, 0x11, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x00, - 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x58, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x01, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x74, 0x53, 0x65, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, + 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, + 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x53, 0x65, 0x74, 0x22, 0xfd, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, + 0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x09, 0x64, 0x65, + 0x6e, 0x79, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, + 0x08, 0x64, 0x65, 0x6e, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x13, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, + 0x6e, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x73, 0x22, 0x80, 0x01, 0x0a, 0x12, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x77, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x27, 0x0a, 0x0f, 0x41, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x22, 0x57, 0x0a, 0x0d, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x46, 0x0a, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x64, + 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1c, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x4f, 0x66, 0x44, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xab, 0x05, 0x0a, 0x11, 0x47, 0x65, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x48, 0x00, 0x52, 0x0b, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x65, 0x63, 0x12, 0x3a, 0x0a, 0x0c, 0x70, + 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x45, 0x78, 0x69, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x5f, 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x4c, 0x0a, 0x12, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0e, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x5f, 0x75, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x18, 0xc0, 0xb8, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x65, + 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xc1, 0xb8, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x61, 0x74, 0x65, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0xe9, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x10, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0xea, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x74, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x07, + 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2a, 0xb1, 0x01, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x10, 0x00, + 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x45, 0x43, + 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x45, 0x58, + 0x49, 0x54, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, + 0x4b, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x43, + 0x45, 0x53, 0x53, 0x5f, 0x54, 0x52, 0x41, 0x43, 0x45, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x10, 0x0a, + 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x4c, 0x4f, 0x41, 0x44, + 0x45, 0x52, 0x10, 0x0b, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x5f, + 0x55, 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x0c, 0x12, 0x0a, 0x0a, 0x04, 0x54, 0x45, 0x53, 0x54, + 0x10, 0xc0, 0xb8, 0x02, 0x12, 0x15, 0x0a, 0x0f, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x4c, 0x49, 0x4d, + 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0xc1, 0xb8, 0x02, 0x2a, 0x2d, 0x0a, 0x11, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x45, 0x58, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -958,56 +1143,67 @@ func file_tetragon_events_proto_rawDescGZIP() []byte { } var file_tetragon_events_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_tetragon_events_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_tetragon_events_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_tetragon_events_proto_goTypes = []interface{}{ (EventType)(0), // 0: tetragon.EventType (FieldFilterAction)(0), // 1: tetragon.FieldFilterAction (*Filter)(nil), // 2: tetragon.Filter - (*FieldFilter)(nil), // 3: tetragon.FieldFilter - (*GetEventsRequest)(nil), // 4: tetragon.GetEventsRequest - (*AggregationOptions)(nil), // 5: tetragon.AggregationOptions - (*AggregationInfo)(nil), // 6: tetragon.AggregationInfo - (*RateLimitInfo)(nil), // 7: tetragon.RateLimitInfo - (*GetEventsResponse)(nil), // 8: tetragon.GetEventsResponse - (*wrapperspb.BoolValue)(nil), // 9: google.protobuf.BoolValue - (*fieldmaskpb.FieldMask)(nil), // 10: google.protobuf.FieldMask - (*durationpb.Duration)(nil), // 11: google.protobuf.Duration - (*ProcessExec)(nil), // 12: tetragon.ProcessExec - (*ProcessExit)(nil), // 13: tetragon.ProcessExit - (*ProcessKprobe)(nil), // 14: tetragon.ProcessKprobe - (*ProcessTracepoint)(nil), // 15: tetragon.ProcessTracepoint - (*ProcessLoader)(nil), // 16: tetragon.ProcessLoader - (*ProcessUprobe)(nil), // 17: tetragon.ProcessUprobe - (*Test)(nil), // 18: tetragon.Test - (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp + (*CapFilter)(nil), // 3: tetragon.CapFilter + (*CapFilterSet)(nil), // 4: tetragon.CapFilterSet + (*FieldFilter)(nil), // 5: tetragon.FieldFilter + (*GetEventsRequest)(nil), // 6: tetragon.GetEventsRequest + (*AggregationOptions)(nil), // 7: tetragon.AggregationOptions + (*AggregationInfo)(nil), // 8: tetragon.AggregationInfo + (*RateLimitInfo)(nil), // 9: tetragon.RateLimitInfo + (*GetEventsResponse)(nil), // 10: tetragon.GetEventsResponse + (*wrapperspb.BoolValue)(nil), // 11: google.protobuf.BoolValue + (CapabilitiesType)(0), // 12: tetragon.CapabilitiesType + (*fieldmaskpb.FieldMask)(nil), // 13: google.protobuf.FieldMask + (*durationpb.Duration)(nil), // 14: google.protobuf.Duration + (*ProcessExec)(nil), // 15: tetragon.ProcessExec + (*ProcessExit)(nil), // 16: tetragon.ProcessExit + (*ProcessKprobe)(nil), // 17: tetragon.ProcessKprobe + (*ProcessTracepoint)(nil), // 18: tetragon.ProcessTracepoint + (*ProcessLoader)(nil), // 19: tetragon.ProcessLoader + (*ProcessUprobe)(nil), // 20: tetragon.ProcessUprobe + (*Test)(nil), // 21: tetragon.Test + (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp } var file_tetragon_events_proto_depIdxs = []int32{ - 9, // 0: tetragon.Filter.health_check:type_name -> google.protobuf.BoolValue + 11, // 0: tetragon.Filter.health_check:type_name -> google.protobuf.BoolValue 0, // 1: tetragon.Filter.event_set:type_name -> tetragon.EventType - 0, // 2: tetragon.FieldFilter.event_set:type_name -> tetragon.EventType - 10, // 3: tetragon.FieldFilter.fields:type_name -> google.protobuf.FieldMask - 1, // 4: tetragon.FieldFilter.action:type_name -> tetragon.FieldFilterAction - 9, // 5: tetragon.FieldFilter.invert_event_set:type_name -> google.protobuf.BoolValue - 2, // 6: tetragon.GetEventsRequest.allow_list:type_name -> tetragon.Filter - 2, // 7: tetragon.GetEventsRequest.deny_list:type_name -> tetragon.Filter - 5, // 8: tetragon.GetEventsRequest.aggregation_options:type_name -> tetragon.AggregationOptions - 3, // 9: tetragon.GetEventsRequest.field_filters:type_name -> tetragon.FieldFilter - 11, // 10: tetragon.AggregationOptions.window_size:type_name -> google.protobuf.Duration - 12, // 11: tetragon.GetEventsResponse.process_exec:type_name -> tetragon.ProcessExec - 13, // 12: tetragon.GetEventsResponse.process_exit:type_name -> tetragon.ProcessExit - 14, // 13: tetragon.GetEventsResponse.process_kprobe:type_name -> tetragon.ProcessKprobe - 15, // 14: tetragon.GetEventsResponse.process_tracepoint:type_name -> tetragon.ProcessTracepoint - 16, // 15: tetragon.GetEventsResponse.process_loader:type_name -> tetragon.ProcessLoader - 17, // 16: tetragon.GetEventsResponse.process_uprobe:type_name -> tetragon.ProcessUprobe - 18, // 17: tetragon.GetEventsResponse.test:type_name -> tetragon.Test - 7, // 18: tetragon.GetEventsResponse.rate_limit_info:type_name -> tetragon.RateLimitInfo - 19, // 19: tetragon.GetEventsResponse.time:type_name -> google.protobuf.Timestamp - 6, // 20: tetragon.GetEventsResponse.aggregation_info:type_name -> tetragon.AggregationInfo - 21, // [21:21] is the sub-list for method output_type - 21, // [21:21] is the sub-list for method input_type - 21, // [21:21] is the sub-list for extension type_name - 21, // [21:21] is the sub-list for extension extendee - 0, // [0:21] is the sub-list for field type_name + 3, // 2: tetragon.Filter.capabilities:type_name -> tetragon.CapFilter + 4, // 3: tetragon.CapFilter.permitted:type_name -> tetragon.CapFilterSet + 4, // 4: tetragon.CapFilter.effective:type_name -> tetragon.CapFilterSet + 4, // 5: tetragon.CapFilter.inheritable:type_name -> tetragon.CapFilterSet + 12, // 6: tetragon.CapFilterSet.any:type_name -> tetragon.CapabilitiesType + 12, // 7: tetragon.CapFilterSet.all:type_name -> tetragon.CapabilitiesType + 12, // 8: tetragon.CapFilterSet.exactly:type_name -> tetragon.CapabilitiesType + 12, // 9: tetragon.CapFilterSet.none:type_name -> tetragon.CapabilitiesType + 0, // 10: tetragon.FieldFilter.event_set:type_name -> tetragon.EventType + 13, // 11: tetragon.FieldFilter.fields:type_name -> google.protobuf.FieldMask + 1, // 12: tetragon.FieldFilter.action:type_name -> tetragon.FieldFilterAction + 11, // 13: tetragon.FieldFilter.invert_event_set:type_name -> google.protobuf.BoolValue + 2, // 14: tetragon.GetEventsRequest.allow_list:type_name -> tetragon.Filter + 2, // 15: tetragon.GetEventsRequest.deny_list:type_name -> tetragon.Filter + 7, // 16: tetragon.GetEventsRequest.aggregation_options:type_name -> tetragon.AggregationOptions + 5, // 17: tetragon.GetEventsRequest.field_filters:type_name -> tetragon.FieldFilter + 14, // 18: tetragon.AggregationOptions.window_size:type_name -> google.protobuf.Duration + 15, // 19: tetragon.GetEventsResponse.process_exec:type_name -> tetragon.ProcessExec + 16, // 20: tetragon.GetEventsResponse.process_exit:type_name -> tetragon.ProcessExit + 17, // 21: tetragon.GetEventsResponse.process_kprobe:type_name -> tetragon.ProcessKprobe + 18, // 22: tetragon.GetEventsResponse.process_tracepoint:type_name -> tetragon.ProcessTracepoint + 19, // 23: tetragon.GetEventsResponse.process_loader:type_name -> tetragon.ProcessLoader + 20, // 24: tetragon.GetEventsResponse.process_uprobe:type_name -> tetragon.ProcessUprobe + 21, // 25: tetragon.GetEventsResponse.test:type_name -> tetragon.Test + 9, // 26: tetragon.GetEventsResponse.rate_limit_info:type_name -> tetragon.RateLimitInfo + 22, // 27: tetragon.GetEventsResponse.time:type_name -> google.protobuf.Timestamp + 8, // 28: tetragon.GetEventsResponse.aggregation_info:type_name -> tetragon.AggregationInfo + 29, // [29:29] is the sub-list for method output_type + 29, // [29:29] is the sub-list for method input_type + 29, // [29:29] is the sub-list for extension type_name + 29, // [29:29] is the sub-list for extension extendee + 0, // [0:29] is the sub-list for field type_name } func init() { file_tetragon_events_proto_init() } @@ -1016,6 +1212,7 @@ func file_tetragon_events_proto_init() { return } file_tetragon_tetragon_proto_init() + file_tetragon_capabilities_proto_init() if !protoimpl.UnsafeEnabled { file_tetragon_events_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Filter); i { @@ -1030,7 +1227,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FieldFilter); i { + switch v := v.(*CapFilter); i { case 0: return &v.state case 1: @@ -1042,7 +1239,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetEventsRequest); i { + switch v := v.(*CapFilterSet); i { case 0: return &v.state case 1: @@ -1054,7 +1251,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AggregationOptions); i { + switch v := v.(*FieldFilter); i { case 0: return &v.state case 1: @@ -1066,7 +1263,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AggregationInfo); i { + switch v := v.(*GetEventsRequest); i { case 0: return &v.state case 1: @@ -1078,7 +1275,7 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RateLimitInfo); i { + switch v := v.(*AggregationOptions); i { case 0: return &v.state case 1: @@ -1090,6 +1287,30 @@ func file_tetragon_events_proto_init() { } } file_tetragon_events_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AggregationInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tetragon_events_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RateLimitInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tetragon_events_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetEventsResponse); i { case 0: return &v.state @@ -1102,7 +1323,7 @@ func file_tetragon_events_proto_init() { } } } - file_tetragon_events_proto_msgTypes[6].OneofWrappers = []interface{}{ + file_tetragon_events_proto_msgTypes[8].OneofWrappers = []interface{}{ (*GetEventsResponse_ProcessExec)(nil), (*GetEventsResponse_ProcessExit)(nil), (*GetEventsResponse_ProcessKprobe)(nil), @@ -1118,7 +1339,7 @@ func file_tetragon_events_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tetragon_events_proto_rawDesc, NumEnums: 2, - NumMessages: 7, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.json.go b/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.json.go index 19b46bed27b..161fd1783e8 100644 --- a/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.json.go +++ b/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.json.go @@ -23,6 +23,38 @@ func (msg *Filter) UnmarshalJSON(b []byte) error { }.Unmarshal(b, msg) } +// MarshalJSON implements json.Marshaler +func (msg *CapFilter) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: true, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *CapFilter) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} + +// MarshalJSON implements json.Marshaler +func (msg *CapFilterSet) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: true, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *CapFilterSet) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} + // MarshalJSON implements json.Marshaler func (msg *FieldFilter) MarshalJSON() ([]byte, error) { return protojson.MarshalOptions{ diff --git a/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.proto b/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.proto index 54cbcfc6eab..81ccd53f795 100644 --- a/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.proto +++ b/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.proto @@ -6,6 +6,7 @@ syntax = "proto3"; package tetragon; import "tetragon/tetragon.proto"; +import "tetragon/capabilities.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; import "google/protobuf/timestamp.proto"; @@ -18,132 +19,169 @@ import "google/protobuf/field_mask.proto"; enum EventType { UNDEF = 0; - PROCESS_EXEC = 1; - PROCESS_EXIT = 5; - PROCESS_KPROBE = 9; - PROCESS_TRACEPOINT = 10; - PROCESS_LOADER = 11; - PROCESS_UPROBE = 12; + PROCESS_EXEC = 1; + PROCESS_EXIT = 5; + PROCESS_KPROBE = 9; + PROCESS_TRACEPOINT = 10; + PROCESS_LOADER = 11; + PROCESS_UPROBE = 12; - TEST = 40000; - RATE_LIMIT_INFO = 40001; + TEST = 40000; + RATE_LIMIT_INFO = 40001; } -message Filter { - repeated string binary_regex = 1; - repeated string namespace = 2; - google.protobuf.BoolValue health_check = 3; - repeated uint32 pid = 4; - // Filter by the PID of a process and any of its descendants. Note that this filter is - // intended for testing and development purposes only and should not be used in - // production. In particular, PID cycling in the OS over longer periods of time may - // cause unexpected events to pass this filter. - repeated uint32 pid_set = 5; - repeated EventType event_set = 6; - // Filter by process.pod.name field using RE2 regular expression syntax: - // https://github.com/google/re2/wiki/Syntax - repeated string pod_regex = 7; - // Filter by process.arguments field using RE2 regular expression syntax: - // https://github.com/google/re2/wiki/Syntax - repeated string arguments_regex = 8; - // Filter events by pod labels using Kubernetes label selector syntax: - // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors - // Note that this filter never matches events without the pod field (i.e. - // host process events). - repeated string labels = 9; - // Filter events by tracing policy names - repeated string policy_names = 10; +message Filter +{ + repeated string binary_regex = 1; + repeated string namespace = 2; + google.protobuf.BoolValue health_check = 3; + repeated uint32 pid = 4; + // Filter by the PID of a process and any of its descendants. Note that this filter is + // intended for testing and development purposes only and should not be used in + // production. In particular, PID cycling in the OS over longer periods of time may + // cause unexpected events to pass this filter. + repeated uint32 pid_set = 5; + repeated EventType event_set = 6; + // Filter by process.pod.name field using RE2 regular expression syntax: + // https://github.com/google/re2/wiki/Syntax + repeated string pod_regex = 7; + // Filter by process.arguments field using RE2 regular expression syntax: + // https://github.com/google/re2/wiki/Syntax + repeated string arguments_regex = 8; + // Filter events by pod labels using Kubernetes label selector syntax: + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors + // Note that this filter never matches events without the pod field (i.e. + // host process events). + repeated string labels = 9; + // Filter events by tracing policy names + repeated string policy_names = 10; + // Filter events by Linux process capability + CapFilter capabilities = 11; +} + +// Filter over a set of Linux process capabilities. See `message Capabilities` +// for more info. WARNING: Multiple sets are ANDed. For example, if the +// permitted filter matches, but the effective filter does not, the filter will +// NOT match. +message CapFilter +{ + // Filter over the set of permitted capabilities. + CapFilterSet permitted = 1; + // Filter over the set of effective capabilities. + CapFilterSet effective = 2; + // Filter over the set of inheritable capabilities. + CapFilterSet inheritable = 3; +} + +// Capability set to filter over. NOTE: you may specify only ONE set here. +message CapFilterSet +{ + // Match if the capability set contains any of the capabilities defined in this filter. + repeated CapabilitiesType any = 1; + // Match if the capability set contains all of the capabilities defined in this filter. + repeated CapabilitiesType all = 2; + // Match if the capability set exactly matches all of the capabilities defined in this filter. + repeated CapabilitiesType exactly = 3; + // Match if the capability set contains none of the capabilities defined in this filter. + repeated CapabilitiesType none = 4; } // Determines the behavior of a field filter enum FieldFilterAction { - INCLUDE = 0; - EXCLUDE = 1; + INCLUDE = 0; + EXCLUDE = 1; } -message FieldFilter { - // Event types to filter or undefined to filter over all event types. - repeated EventType event_set = 1; - // Fields to include or exclude. - google.protobuf.FieldMask fields = 2; - // Whether to include or exclude fields. - FieldFilterAction action = 3; - // Whether or not the event set filter should be inverted. - google.protobuf.BoolValue invert_event_set = 4; +message FieldFilter +{ + // Event types to filter or undefined to filter over all event types. + repeated EventType event_set = 1; + // Fields to include or exclude. + google.protobuf.FieldMask fields = 2; + // Whether to include or exclude fields. + FieldFilterAction action = 3; + // Whether or not the event set filter should be inverted. + google.protobuf.BoolValue invert_event_set = 4; } -message GetEventsRequest { - // allow_list specifies a list of filters to apply to only return certain - // events. If multiple filters are specified, at least one of them has to - // match for an event to be included in the results. - repeated Filter allow_list = 1; - // deny_list specifies a list of filters to apply to exclude certain events - // from the results. If multiple filters are specified, at least one of - // them has to match for an event to be excluded. - // If both allow_list and deny_list are specified, the results contain the - // set difference allow_list - deny_list. - repeated Filter deny_list = 2; - // aggregation_options configures aggregation options for this request. - // If this field is not set, responses will not be aggregated. - // Note that currently only process_accept and process_connect events are - // aggregated. Other events remain unaggregated. - AggregationOptions aggregation_options = 3; - // Fields to include or exclude for events in the GetEventsResponse. Omitting this - // field implies that all fields will be included. Exclusion always takes precedence - // over inclusion in the case of conflicts. - repeated FieldFilter field_filters = 4; +message GetEventsRequest +{ + // allow_list specifies a list of filters to apply to only return certain + // events. If multiple filters are specified, at least one of them has to + // match for an event to be included in the results. + repeated Filter allow_list = 1; + // deny_list specifies a list of filters to apply to exclude certain events + // from the results. If multiple filters are specified, at least one of + // them has to match for an event to be excluded. + // If both allow_list and deny_list are specified, the results contain the + // set difference allow_list - deny_list. + repeated Filter deny_list = 2; + // aggregation_options configures aggregation options for this request. + // If this field is not set, responses will not be aggregated. + // Note that currently only process_accept and process_connect events are + // aggregated. Other events remain unaggregated. + AggregationOptions aggregation_options = 3; + // Fields to include or exclude for events in the GetEventsResponse. Omitting this + // field implies that all fields will be included. Exclusion always takes precedence + // over inclusion in the case of conflicts. + repeated FieldFilter field_filters = 4; } // AggregationOptions defines configuration options for aggregating events. -message AggregationOptions { - // Aggregation window size. Defaults to 15 seconds if this field is not set. - google.protobuf.Duration window_size = 1; - // Size of the buffer for the aggregator to receive incoming events. If the - // buffer becomes full, the aggregator will log a warning and start dropping - // incoming events. - uint64 channel_buffer_size = 2; +message AggregationOptions +{ + // Aggregation window size. Defaults to 15 seconds if this field is not set. + google.protobuf.Duration window_size = 1; + // Size of the buffer for the aggregator to receive incoming events. If the + // buffer becomes full, the aggregator will log a warning and start dropping + // incoming events. + uint64 channel_buffer_size = 2; } // AggregationInfo contains information about aggregation results. -message AggregationInfo { - // Total count of events in this aggregation time window. - uint64 count = 1; +message AggregationInfo +{ + // Total count of events in this aggregation time window. + uint64 count = 1; } -message RateLimitInfo { - uint64 number_of_dropped_process_events = 1; +message RateLimitInfo +{ + uint64 number_of_dropped_process_events = 1; } -message GetEventsResponse { - // The type-specific fields of an event. - // - // NOTE: Numbers must stay in sync with enum EventType. - oneof event { - // ProcessExec event includes information about the execution of - // binaries and other related process metadata. - ProcessExec process_exec = 1; - // ProcessExit event indicates how and when a process terminates. - ProcessExit process_exit = 5; - // ProcessKprobe event contains information about the pre-defined - // functions and the process that invoked them. - ProcessKprobe process_kprobe = 9; - // ProcessTracepoint contains information about the pre-defined - // tracepoint and the process that invoked them. - ProcessTracepoint process_tracepoint = 10; - ProcessLoader process_loader = 11; - ProcessUprobe process_uprobe = 12; - - Test test = 40000; - RateLimitInfo rate_limit_info = 40001; - } - // Name of the node where this event was observed. - string node_name = 1000; - // Timestamp at which this event was observed. - // For an aggregated response, this field to set to the timestamp at which - // the event was observed for the first time in a given aggregation time window. - google.protobuf.Timestamp time = 1001; - - // aggregation_info contains information about aggregation results. This field - // is set only for aggregated responses. - AggregationInfo aggregation_info = 1002; +message GetEventsResponse +{ + // The type-specific fields of an event. + // + // NOTE: Numbers must stay in sync with enum EventType. + oneof event + { + // ProcessExec event includes information about the execution of + // binaries and other related process metadata. + ProcessExec process_exec = 1; + // ProcessExit event indicates how and when a process terminates. + ProcessExit process_exit = 5; + // ProcessKprobe event contains information about the pre-defined + // functions and the process that invoked them. + ProcessKprobe process_kprobe = 9; + // ProcessTracepoint contains information about the pre-defined + // tracepoint and the process that invoked them. + ProcessTracepoint process_tracepoint = 10; + ProcessLoader process_loader = 11; + ProcessUprobe process_uprobe = 12; + + Test test = 40000; + RateLimitInfo rate_limit_info = 40001; + } + // Name of the node where this event was observed. + string node_name = 1000; + // Timestamp at which this event was observed. + // For an aggregated response, this field to set to the timestamp at which + // the event was observed for the first time in a given aggregation time window. + google.protobuf.Timestamp time = 1001; + + // aggregation_info contains information about aggregation results. This field + // is set only for aggregated responses. + AggregationInfo aggregation_info = 1002; } diff --git a/vendor/github.com/deckarep/golang-set/v2/.gitignore b/vendor/github.com/deckarep/golang-set/v2/.gitignore new file mode 100644 index 00000000000..4eb156d14a7 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +.idea \ No newline at end of file diff --git a/vendor/github.com/deckarep/golang-set/v2/LICENSE b/vendor/github.com/deckarep/golang-set/v2/LICENSE new file mode 100644 index 00000000000..efd4827e212 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/LICENSE @@ -0,0 +1,22 @@ +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/deckarep/golang-set/v2/README.md b/vendor/github.com/deckarep/golang-set/v2/README.md new file mode 100644 index 00000000000..921f0cec066 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/README.md @@ -0,0 +1,181 @@ +![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2) +[![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2) + +# golang-set + +The missing `generic` set collection for the Go language. Until Go has sets built-in...use this. + +## Update 3/5/2023 +* Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`. +* supports `new generic` syntax +* Go `1.18.0` or higher +* Workflow tested on Go `1.20` + +![With Generics](new_improved.jpeg) + +Coming from Python one of the things I miss is the superbly wonderful set collection. This is my attempt to mimic the primary features of the set collection from Python. +You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library. To those I say simply ignore this repository and carry-on and to the rest that find this useful please contribute in helping me make it better by contributing with suggestions or PRs. + +## Install + +Use `go get` to install this package. + +```shell +go get github.com/deckarep/golang-set/v2 +``` + +## Features + +* *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher) +* One common *interface* to both implementations + * a **non threadsafe** implementation favoring *performance* + * a **threadsafe** implementation favoring *concurrent* use +* Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set). +* Exhaustive unit-test and benchmark suite + +## Trusted by + +This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package. + +* Notable projects/companies using this package + * Ethereum + * Docker + * 1Password + * Hashicorp + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date) + + +## Usage + +The code below demonstrates how a Set collection can better manage data and actually minimize boilerplate and needless loops in code. This package now fully supports *generic* syntax so you are now able to instantiate a collection for any [comparable](https://flaviocopes.com/golang-comparing-values/) type object. + +What is considered comparable in Go? +* `Booleans`, `integers`, `strings`, `floats` or basically primitive types. +* `Pointers` +* `Arrays` +* `Structs` if *all of their fields* are also comparable independently + +Using this library is as simple as creating either a threadsafe or non-threadsafe set and providing a `comparable` type for instantiation of the collection. + +```go +// Syntax example, doesn't compile. +mySet := mapset.NewSet[T]() // where T is some concrete comparable type. + +// Therefore this code creates an int set +mySet := mapset.NewSet[int]() + +// Or perhaps you want a string set +mySet := mapset.NewSet[string]() + +type myStruct struct { + name string + age uint8 +} + +// Alternatively a set of structs +mySet := mapset.NewSet[myStruct]() + +// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety. +mySet := mapset.NewSet[any]() +``` + +## Comprehensive Example + +```go +package main + +import ( + "fmt" + mapset "github.com/deckarep/golang-set/v2" +) + +func main() { + // Create a string-based set of required classes. + required := mapset.NewSet[string]() + required.Add("cooking") + required.Add("english") + required.Add("math") + required.Add("biology") + + // Create a string-based set of science classes. + sciences := mapset.NewSet[string]() + sciences.Add("biology") + sciences.Add("chemistry") + + // Create a string-based set of electives. + electives := mapset.NewSet[string]() + electives.Add("welding") + electives.Add("music") + electives.Add("automotive") + + // Create a string-based set of bonus programming classes. + bonus := mapset.NewSet[string]() + bonus.Add("beginner go") + bonus.Add("python for dummies") +} +``` + +Create a set of all unique classes. +Sets will *automatically* deduplicate the same data. + +```go + all := required + .Union(sciences) + .Union(electives) + .Union(bonus) + + fmt.Println(all) +``` + +Output: +```sh +Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies} +``` + +Is cooking considered a science class? +```go +result := sciences.Contains("cooking") +fmt.Println(result) +``` + +Output: +```false +false +``` + +Show me all classes that are not science classes, since I don't enjoy science. +```go +notScience := all.Difference(sciences) +fmt.Println(notScience) +``` + +```sh +Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding } +``` + +Which science classes are also required classes? +```go +reqScience := sciences.Intersect(required) +``` + +Output: +```sh +Set{biology} +``` + +How many bonus classes do you offer? +```go +fmt.Println(bonus.Cardinality()) +``` +Output: +```sh +2 +``` + +Thanks for visiting! + +-deckarep diff --git a/vendor/github.com/deckarep/golang-set/v2/iterator.go b/vendor/github.com/deckarep/golang-set/v2/iterator.go new file mode 100644 index 00000000000..fc14e705643 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/iterator.go @@ -0,0 +1,58 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's +// elements. +type Iterator[T comparable] struct { + C <-chan T + stop chan struct{} +} + +// Stop stops the Iterator, no further elements will be received on C, C will be closed. +func (i *Iterator[T]) Stop() { + // Allows for Stop() to be called multiple times + // (close() panics when called on already closed channel) + defer func() { + recover() + }() + + close(i.stop) + + // Exhaust any remaining elements. + for range i.C { + } +} + +// newIterator returns a new Iterator instance together with its item and stop channels. +func newIterator[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) { + itemChan := make(chan T) + stopChan := make(chan struct{}) + return &Iterator[T]{ + C: itemChan, + stop: stopChan, + }, itemChan, stopChan +} diff --git a/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..429752a07a94b837434515275aa7645714879341 GIT binary patch literal 120935 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/deckarep/golang-set/v2/set.go b/vendor/github.com/deckarep/golang-set/v2/set.go new file mode 100644 index 00000000000..292089dcf32 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/set.go @@ -0,0 +1,255 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Package mapset implements a simple and set collection. +// Items stored within it are unordered and unique. It supports +// typical set operations: membership testing, intersection, union, +// difference, symmetric difference and cloning. +// +// Package mapset provides two implementations of the Set +// interface. The default implementation is safe for concurrent +// access, but a non-thread-safe implementation is also provided for +// programs that can benefit from the slight speed improvement and +// that can enforce mutual exclusion through other means. +package mapset + +// Set is the primary interface provided by the mapset package. It +// represents an unordered set of data and a large number of +// operations that can be applied to that set. +type Set[T comparable] interface { + // Add adds an element to the set. Returns whether + // the item was added. + Add(val T) bool + + // Append multiple elements to the set. Returns + // the number of elements added. + Append(val ...T) int + + // Cardinality returns the number of elements in the set. + Cardinality() int + + // Clear removes all elements from the set, leaving + // the empty set. + Clear() + + // Clone returns a clone of the set using the same + // implementation, duplicating all keys. + Clone() Set[T] + + // Contains returns whether the given items + // are all in the set. + Contains(val ...T) bool + + // ContainsOne returns whether the given item + // is in the set. + // + // Contains may cause the argument to escape to the heap. + // See: https://github.com/deckarep/golang-set/issues/118 + ContainsOne(val T) bool + + // ContainsAny returns whether at least one of the + // given items are in the set. + ContainsAny(val ...T) bool + + // Difference returns the difference between this set + // and other. The returned set will contain + // all elements of this set that are not also + // elements of other. + // + // Note that the argument to Difference + // must be of the same type as the receiver + // of the method. Otherwise, Difference will + // panic. + Difference(other Set[T]) Set[T] + + // Equal determines if two sets are equal to each + // other. If they have the same cardinality + // and contain the same elements, they are + // considered equal. The order in which + // the elements were added is irrelevant. + // + // Note that the argument to Equal must be + // of the same type as the receiver of the + // method. Otherwise, Equal will panic. + Equal(other Set[T]) bool + + // Intersect returns a new set containing only the elements + // that exist only in both sets. + // + // Note that the argument to Intersect + // must be of the same type as the receiver + // of the method. Otherwise, Intersect will + // panic. + Intersect(other Set[T]) Set[T] + + // IsEmpty determines if there are elements in the set. + IsEmpty() bool + + // IsProperSubset determines if every element in this set is in + // the other set but the two sets are not equal. + // + // Note that the argument to IsProperSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsProperSubset + // will panic. + IsProperSubset(other Set[T]) bool + + // IsProperSuperset determines if every element in the other set + // is in this set but the two sets are not + // equal. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsProperSuperset(other Set[T]) bool + + // IsSubset determines if every element in this set is in + // the other set. + // + // Note that the argument to IsSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsSubset will + // panic. + IsSubset(other Set[T]) bool + + // IsSuperset determines if every element in the other set + // is in this set. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsSuperset(other Set[T]) bool + + // Each iterates over elements and executes the passed func against each element. + // If passed func returns true, stop iteration at the time. + Each(func(T) bool) + + // Iter returns a channel of elements that you can + // range over. + Iter() <-chan T + + // Iterator returns an Iterator object that you can + // use to range over the set. + Iterator() *Iterator[T] + + // Remove removes a single element from the set. + Remove(i T) + + // RemoveAll removes multiple elements from the set. + RemoveAll(i ...T) + + // String provides a convenient string representation + // of the current state of the set. + String() string + + // SymmetricDifference returns a new set with all elements which are + // in either this set or the other set but not in both. + // + // Note that the argument to SymmetricDifference + // must be of the same type as the receiver + // of the method. Otherwise, SymmetricDifference + // will panic. + SymmetricDifference(other Set[T]) Set[T] + + // Union returns a new set with all elements in both sets. + // + // Note that the argument to Union must be of the + // same type as the receiver of the method. + // Otherwise, Union will panic. + Union(other Set[T]) Set[T] + + // Pop removes and returns an arbitrary item from the set. + Pop() (T, bool) + + // ToSlice returns the members of the set as a slice. + ToSlice() []T + + // MarshalJSON will marshal the set into a JSON-based representation. + MarshalJSON() ([]byte, error) + + // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure. + // For this to work, set subtypes must implemented the Marshal/Unmarshal interface. + UnmarshalJSON(b []byte) error +} + +// NewSet creates and returns a new set with the given elements. +// Operations on the resulting set are thread-safe. +func NewSet[T comparable](vals ...T) Set[T] { + s := newThreadSafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewSetWithSize creates and returns a reference to an empty set with a specified +// capacity. Operations on the resulting set are thread-safe. +func NewSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadSafeSetWithSize[T](cardinality) + return s +} + +// NewThreadUnsafeSet creates and returns a new set with the given elements. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] { + s := newThreadUnsafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with +// a specified capacity. Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadUnsafeSetWithSize[T](cardinality) + return s +} + +// NewSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are thread-safe. +func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} + +// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewThreadUnsafeSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/sorted.go b/vendor/github.com/deckarep/golang-set/v2/sorted.go new file mode 100644 index 00000000000..8ee2e70763d --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/sorted.go @@ -0,0 +1,42 @@ +//go:build go1.21 +// +build go1.21 + +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2023 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import ( + "cmp" + "slices" +) + +// Sorted returns a sorted slice of a set of any ordered type in ascending order. +// When sorting floating-point numbers, NaNs are ordered before other values. +func Sorted[E cmp.Ordered](set Set[E]) []E { + s := set.ToSlice() + slices.Sort(s) + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go new file mode 100644 index 00000000000..ad7a834b59c --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go @@ -0,0 +1,299 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import "sync" + +type threadSafeSet[T comparable] struct { + sync.RWMutex + uss threadUnsafeSet[T] +} + +func newThreadSafeSet[T comparable]() *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSet[T](), + } +} + +func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSetWithSize[T](cardinality), + } +} + +func (t *threadSafeSet[T]) Add(v T) bool { + t.Lock() + ret := t.uss.Add(v) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Append(v ...T) int { + t.Lock() + ret := t.uss.Append(v...) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Contains(v ...T) bool { + t.RLock() + ret := t.uss.Contains(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsOne(v T) bool { + t.RLock() + ret := t.uss.ContainsOne(v) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsAny(v ...T) bool { + t.RLock() + ret := t.uss.ContainsAny(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) IsEmpty() bool { + return t.Cardinality() == 0 +} + +func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.IsSubset(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + defer t.RUnlock() + o.RLock() + defer o.RUnlock() + + return t.uss.IsProperSubset(o.uss) +} + +func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(t) +} + +func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool { + return other.IsProperSubset(t) +} + +func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeUnion} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeIntersection} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clear() { + t.Lock() + t.uss.Clear() + t.Unlock() +} + +func (t *threadSafeSet[T]) Remove(v T) { + t.Lock() + delete(t.uss, v) + t.Unlock() +} + +func (t *threadSafeSet[T]) RemoveAll(i ...T) { + t.Lock() + t.uss.RemoveAll(i...) + t.Unlock() +} + +func (t *threadSafeSet[T]) Cardinality() int { + t.RLock() + defer t.RUnlock() + return len(t.uss) +} + +func (t *threadSafeSet[T]) Each(cb func(T) bool) { + t.RLock() + for elem := range t.uss { + if cb(elem) { + break + } + } + t.RUnlock() +} + +func (t *threadSafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + t.RLock() + + for elem := range t.uss { + ch <- elem + } + close(ch) + t.RUnlock() + }() + + return ch +} + +func (t *threadSafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + t.RLock() + L: + for elem := range t.uss { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + t.RUnlock() + }() + + return iterator +} + +func (t *threadSafeSet[T]) Equal(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.Equal(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clone() Set[T] { + t.RLock() + + unsafeClone := t.uss.Clone().(threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeClone} + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) String() string { + t.RLock() + ret := t.uss.String() + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Pop() (T, bool) { + t.Lock() + defer t.Unlock() + return t.uss.Pop() +} + +func (t *threadSafeSet[T]) ToSlice() []T { + keys := make([]T, 0, t.Cardinality()) + t.RLock() + for elem := range t.uss { + keys = append(keys, elem) + } + t.RUnlock() + return keys +} + +func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) { + t.RLock() + b, err := t.uss.MarshalJSON() + t.RUnlock() + + return b, err +} + +func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error { + t.RLock() + err := t.uss.UnmarshalJSON(p) + t.RUnlock() + + return err +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go new file mode 100644 index 00000000000..8b17b017609 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go @@ -0,0 +1,330 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import ( + "encoding/json" + "fmt" + "strings" +) + +type threadUnsafeSet[T comparable] map[T]struct{} + +// Assert concrete type:threadUnsafeSet adheres to Set interface. +var _ Set[string] = (threadUnsafeSet[string])(nil) + +func newThreadUnsafeSet[T comparable]() threadUnsafeSet[T] { + return make(threadUnsafeSet[T]) +} + +func newThreadUnsafeSetWithSize[T comparable](cardinality int) threadUnsafeSet[T] { + return make(threadUnsafeSet[T], cardinality) +} + +func (s threadUnsafeSet[T]) Add(v T) bool { + prevLen := len(s) + s[v] = struct{}{} + return prevLen != len(s) +} + +func (s threadUnsafeSet[T]) Append(v ...T) int { + prevLen := len(s) + for _, val := range v { + (s)[val] = struct{}{} + } + return len(s) - prevLen +} + +// private version of Add which doesn't return a value +func (s threadUnsafeSet[T]) add(v T) { + s[v] = struct{}{} +} + +func (s threadUnsafeSet[T]) Cardinality() int { + return len(s) +} + +func (s threadUnsafeSet[T]) Clear() { + // Constructions like this are optimised by compiler, and replaced by + // mapclear() function, defined in + // https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993) + for key := range s { + delete(s, key) + } +} + +func (s threadUnsafeSet[T]) Clone() Set[T] { + clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality()) + for elem := range s { + clonedSet.add(elem) + } + return clonedSet +} + +func (s threadUnsafeSet[T]) Contains(v ...T) bool { + for _, val := range v { + if _, ok := s[val]; !ok { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) ContainsOne(v T) bool { + _, ok := s[v] + return ok +} + +func (s threadUnsafeSet[T]) ContainsAny(v ...T) bool { + for _, val := range v { + if _, ok := s[val]; ok { + return true + } + } + return false +} + +// private version of Contains for a single element v +func (s threadUnsafeSet[T]) contains(v T) (ok bool) { + _, ok = s[v] + return ok +} + +func (s threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + diff := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + diff.add(elem) + } + } + return diff +} + +func (s threadUnsafeSet[T]) Each(cb func(T) bool) { + for elem := range s { + if cb(elem) { + break + } + } +} + +func (s threadUnsafeSet[T]) Equal(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + + if s.Cardinality() != other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + intersection := newThreadUnsafeSet[T]() + // loop over smaller set + if s.Cardinality() < other.Cardinality() { + for elem := range s { + if o.contains(elem) { + intersection.add(elem) + } + } + } else { + for elem := range o { + if s.contains(elem) { + intersection.add(elem) + } + } + } + return intersection +} + +func (s threadUnsafeSet[T]) IsEmpty() bool { + return s.Cardinality() == 0 +} + +func (s threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool { + return s.Cardinality() < other.Cardinality() && s.IsSubset(other) +} + +func (s threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool { + return s.Cardinality() > other.Cardinality() && s.IsSuperset(other) +} + +func (s threadUnsafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(threadUnsafeSet[T]) + if s.Cardinality() > other.Cardinality() { + return false + } + for elem := range s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(s) +} + +func (s threadUnsafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + for elem := range s { + ch <- elem + } + close(ch) + }() + + return ch +} + +func (s threadUnsafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + L: + for elem := range s { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + }() + + return iterator +} + +// Pop returns a popped item in case set is not empty, or nil-value of T +// if set is already empty +func (s threadUnsafeSet[T]) Pop() (v T, ok bool) { + for item := range s { + delete(s, item) + return item, true + } + return v, false +} + +func (s threadUnsafeSet[T]) Remove(v T) { + delete(s, v) +} + +func (s threadUnsafeSet[T]) RemoveAll(i ...T) { + for _, elem := range i { + delete(s, elem) + } +} + +func (s threadUnsafeSet[T]) String() string { + items := make([]string, 0, len(s)) + + for elem := range s { + items = append(items, fmt.Sprintf("%v", elem)) + } + return fmt.Sprintf("Set{%s}", strings.Join(items, ", ")) +} + +func (s threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + sd := newThreadUnsafeSet[T]() + for elem := range s { + if !o.contains(elem) { + sd.add(elem) + } + } + for elem := range o { + if !s.contains(elem) { + sd.add(elem) + } + } + return sd +} + +func (s threadUnsafeSet[T]) ToSlice() []T { + keys := make([]T, 0, s.Cardinality()) + for elem := range s { + keys = append(keys, elem) + } + + return keys +} + +func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(threadUnsafeSet[T]) + + n := s.Cardinality() + if o.Cardinality() > n { + n = o.Cardinality() + } + unionedSet := make(threadUnsafeSet[T], n) + + for elem := range s { + unionedSet.add(elem) + } + for elem := range o { + unionedSet.add(elem) + } + return unionedSet +} + +// MarshalJSON creates a JSON array from the set, it marshals all elements +func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) { + items := make([]string, 0, s.Cardinality()) + + for elem := range s { + b, err := json.Marshal(elem) + if err != nil { + return nil, err + } + + items = append(items, string(b)) + } + + return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil +} + +// UnmarshalJSON recreates a set from a JSON array, it only decodes +// primitive types. Numbers are decoded as json.Number. +func (s threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { + var i []T + err := json.Unmarshal(b, &i) + if err != nil { + return err + } + s.Append(i...) + + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 40c433489fe..6f1b78a0592 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -316,6 +316,9 @@ github.com/cpuguy83/go-md2man/v2/md2man # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew +# github.com/deckarep/golang-set/v2 v2.6.0 +## explicit; go 1.18 +github.com/deckarep/golang-set/v2 # github.com/distribution/reference v0.5.0 ## explicit; go 1.20 github.com/distribution/reference