From afd7fbe8b6c1bc7e965867322a4b3e6d81442b07 Mon Sep 17 00:00:00 2001 From: Giles Hutton Date: Tue, 17 Feb 2026 17:33:49 +0000 Subject: [PATCH 1/3] move to using bmatcuk/doublestar, for correctness --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 63b1323d9c85a..a6209cd4bf0c0 100644 --- a/go.mod +++ b/go.mod @@ -252,6 +252,7 @@ require ( github.com/bits-and-blooms/bitset v1.12.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect + github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect From 991bbce2a240bc3288cc3d9a11cd90fcf9bb243b Mon Sep 17 00:00:00 2001 From: Giles Hutton Date: Wed, 18 Feb 2026 16:34:00 +0000 Subject: [PATCH 2/3] ROX-33022: configure fact on policy sync These changes allow for automatic configuration of Fact based on policies defined by the user. It constructs a set of path prefixes and writes them to a Fact ConfigMap, which is hot-reloaded by the Fact agent. This also includes a fairly significant refactor of the admission controller settings / config map persistence, so that there is a generic persistence component that can be initialized for specific settings (admission controller, and now Fact) --- generated/internalapi/sensor/fact.pb.go | 299 ++++ .../internalapi/sensor/fact_vtproto.pb.go | 1414 +++++++++++++++++ proto/internalapi/sensor/fact.proto | 27 + .../mocks/settings_manager.go | 15 + .../admissioncontroller/settings_manager.go | 2 + .../settings_manager_impl.go | 87 +- sensor/common/configmap/persister.go | 127 ++ sensor/common/detector/detector.go | 9 +- sensor/common/filesystem/settings_manager.go | 13 + .../filesystem/settings_manager_impl.go | 108 ++ sensor/kubernetes/sensor/sensor.go | 26 +- 11 files changed, 2120 insertions(+), 7 deletions(-) create mode 100644 generated/internalapi/sensor/fact.pb.go create mode 100644 generated/internalapi/sensor/fact_vtproto.pb.go create mode 100644 proto/internalapi/sensor/fact.proto create mode 100644 sensor/common/configmap/persister.go create mode 100644 sensor/common/filesystem/settings_manager.go create mode 100644 sensor/common/filesystem/settings_manager_impl.go diff --git a/generated/internalapi/sensor/fact.pb.go b/generated/internalapi/sensor/fact.pb.go new file mode 100644 index 0000000000000..1f9e4530373e8 --- /dev/null +++ b/generated/internalapi/sensor/fact.pb.go @@ -0,0 +1,299 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v6.32.1 +// source: internalapi/sensor/fact.proto + +package sensor + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FactGRPCConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + Certs string `protobuf:"bytes,2,opt,name=certs,proto3" json:"certs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FactGRPCConfig) Reset() { + *x = FactGRPCConfig{} + mi := &file_internalapi_sensor_fact_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FactGRPCConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FactGRPCConfig) ProtoMessage() {} + +func (x *FactGRPCConfig) ProtoReflect() protoreflect.Message { + mi := &file_internalapi_sensor_fact_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FactGRPCConfig.ProtoReflect.Descriptor instead. +func (*FactGRPCConfig) Descriptor() ([]byte, []int) { + return file_internalapi_sensor_fact_proto_rawDescGZIP(), []int{0} +} + +func (x *FactGRPCConfig) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *FactGRPCConfig) GetCerts() string { + if x != nil { + return x.Certs + } + return "" +} + +type FactEndpointConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + ExposeMetrics bool `protobuf:"varint,2,opt,name=expose_metrics,json=exposeMetrics,proto3" json:"expose_metrics,omitempty" yaml:"expose_metrics"` // @gotags: yaml:"expose_metrics" + HealthCheck bool `protobuf:"varint,3,opt,name=health_check,json=healthCheck,proto3" json:"health_check,omitempty" yaml:"health_check"` // @gotags: yaml:"health_check" + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FactEndpointConfig) Reset() { + *x = FactEndpointConfig{} + mi := &file_internalapi_sensor_fact_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FactEndpointConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FactEndpointConfig) ProtoMessage() {} + +func (x *FactEndpointConfig) ProtoReflect() protoreflect.Message { + mi := &file_internalapi_sensor_fact_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FactEndpointConfig.ProtoReflect.Descriptor instead. +func (*FactEndpointConfig) Descriptor() ([]byte, []int) { + return file_internalapi_sensor_fact_proto_rawDescGZIP(), []int{1} +} + +func (x *FactEndpointConfig) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *FactEndpointConfig) GetExposeMetrics() bool { + if x != nil { + return x.ExposeMetrics + } + return false +} + +func (x *FactEndpointConfig) GetHealthCheck() bool { + if x != nil { + return x.HealthCheck + } + return false +} + +type FactSettings struct { + state protoimpl.MessageState `protogen:"open.v1"` + Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` + Grpc *FactGRPCConfig `protobuf:"bytes,2,opt,name=grpc,proto3" json:"grpc,omitempty" yaml:",omitempty"` // @gotags: yaml:",omitempty" + Endpoint *FactEndpointConfig `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty" yaml:",omitempty"` // @gotags: yaml:",omitempty" + SkipPreFlight bool `protobuf:"varint,4,opt,name=skip_pre_flight,json=skipPreFlight,proto3" json:"skip_pre_flight,omitempty" yaml:"skip_pre_flight,omitempty"` // @gotags: yaml:"skip_pre_flight,omitempty" + Json bool `protobuf:"varint,5,opt,name=json,proto3" json:"json,omitempty" yaml:",omitempty"` // @gotags: yaml:",omitempty" + RingbufSize uint32 `protobuf:"varint,6,opt,name=ringbuf_size,json=ringbufSize,proto3" json:"ringbuf_size,omitempty" yaml:"ringbuf_size,omitempty"` // @gotags: yaml:"ringbuf_size,omitempty" + Hotreload bool `protobuf:"varint,7,opt,name=hotreload,proto3" json:"hotreload,omitempty" yaml:",omitempty"` // @gotags: yaml:",omitempty" + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FactSettings) Reset() { + *x = FactSettings{} + mi := &file_internalapi_sensor_fact_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FactSettings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FactSettings) ProtoMessage() {} + +func (x *FactSettings) ProtoReflect() protoreflect.Message { + mi := &file_internalapi_sensor_fact_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FactSettings.ProtoReflect.Descriptor instead. +func (*FactSettings) Descriptor() ([]byte, []int) { + return file_internalapi_sensor_fact_proto_rawDescGZIP(), []int{2} +} + +func (x *FactSettings) GetPaths() []string { + if x != nil { + return x.Paths + } + return nil +} + +func (x *FactSettings) GetGrpc() *FactGRPCConfig { + if x != nil { + return x.Grpc + } + return nil +} + +func (x *FactSettings) GetEndpoint() *FactEndpointConfig { + if x != nil { + return x.Endpoint + } + return nil +} + +func (x *FactSettings) GetSkipPreFlight() bool { + if x != nil { + return x.SkipPreFlight + } + return false +} + +func (x *FactSettings) GetJson() bool { + if x != nil { + return x.Json + } + return false +} + +func (x *FactSettings) GetRingbufSize() uint32 { + if x != nil { + return x.RingbufSize + } + return 0 +} + +func (x *FactSettings) GetHotreload() bool { + if x != nil { + return x.Hotreload + } + return false +} + +var File_internalapi_sensor_fact_proto protoreflect.FileDescriptor + +const file_internalapi_sensor_fact_proto_rawDesc = "" + + "\n" + + "\x1dinternalapi/sensor/fact.proto\x12\x06sensor\"8\n" + + "\x0eFactGRPCConfig\x12\x10\n" + + "\x03url\x18\x01 \x01(\tR\x03url\x12\x14\n" + + "\x05certs\x18\x02 \x01(\tR\x05certs\"x\n" + + "\x12FactEndpointConfig\x12\x18\n" + + "\aaddress\x18\x01 \x01(\tR\aaddress\x12%\n" + + "\x0eexpose_metrics\x18\x02 \x01(\bR\rexposeMetrics\x12!\n" + + "\fhealth_check\x18\x03 \x01(\bR\vhealthCheck\"\x85\x02\n" + + "\fFactSettings\x12\x14\n" + + "\x05paths\x18\x01 \x03(\tR\x05paths\x12*\n" + + "\x04grpc\x18\x02 \x01(\v2\x16.sensor.FactGRPCConfigR\x04grpc\x126\n" + + "\bendpoint\x18\x03 \x01(\v2\x1a.sensor.FactEndpointConfigR\bendpoint\x12&\n" + + "\x0fskip_pre_flight\x18\x04 \x01(\bR\rskipPreFlight\x12\x12\n" + + "\x04json\x18\x05 \x01(\bR\x04json\x12!\n" + + "\fringbuf_size\x18\x06 \x01(\rR\vringbufSize\x12\x1c\n" + + "\thotreload\x18\a \x01(\bR\thotreloadB Z\x1b./internalapi/sensor;sensor\xf8\x01\x01b\x06proto3" + +var ( + file_internalapi_sensor_fact_proto_rawDescOnce sync.Once + file_internalapi_sensor_fact_proto_rawDescData []byte +) + +func file_internalapi_sensor_fact_proto_rawDescGZIP() []byte { + file_internalapi_sensor_fact_proto_rawDescOnce.Do(func() { + file_internalapi_sensor_fact_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_internalapi_sensor_fact_proto_rawDesc), len(file_internalapi_sensor_fact_proto_rawDesc))) + }) + return file_internalapi_sensor_fact_proto_rawDescData +} + +var file_internalapi_sensor_fact_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_internalapi_sensor_fact_proto_goTypes = []any{ + (*FactGRPCConfig)(nil), // 0: sensor.FactGRPCConfig + (*FactEndpointConfig)(nil), // 1: sensor.FactEndpointConfig + (*FactSettings)(nil), // 2: sensor.FactSettings +} +var file_internalapi_sensor_fact_proto_depIdxs = []int32{ + 0, // 0: sensor.FactSettings.grpc:type_name -> sensor.FactGRPCConfig + 1, // 1: sensor.FactSettings.endpoint:type_name -> sensor.FactEndpointConfig + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_internalapi_sensor_fact_proto_init() } +func file_internalapi_sensor_fact_proto_init() { + if File_internalapi_sensor_fact_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_internalapi_sensor_fact_proto_rawDesc), len(file_internalapi_sensor_fact_proto_rawDesc)), + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_internalapi_sensor_fact_proto_goTypes, + DependencyIndexes: file_internalapi_sensor_fact_proto_depIdxs, + MessageInfos: file_internalapi_sensor_fact_proto_msgTypes, + }.Build() + File_internalapi_sensor_fact_proto = out.File + file_internalapi_sensor_fact_proto_goTypes = nil + file_internalapi_sensor_fact_proto_depIdxs = nil +} diff --git a/generated/internalapi/sensor/fact_vtproto.pb.go b/generated/internalapi/sensor/fact_vtproto.pb.go new file mode 100644 index 0000000000000..bed32a930ec23 --- /dev/null +++ b/generated/internalapi/sensor/fact_vtproto.pb.go @@ -0,0 +1,1414 @@ +// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. +// protoc-gen-go-vtproto version: v0.6.1-0.20240409071808-615f978279ca +// source: internalapi/sensor/fact.proto + +package sensor + +import ( + fmt "fmt" + protohelpers "github.com/planetscale/vtprotobuf/protohelpers" + proto "google.golang.org/protobuf/proto" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +func (m *FactGRPCConfig) CloneVT() *FactGRPCConfig { + if m == nil { + return (*FactGRPCConfig)(nil) + } + r := new(FactGRPCConfig) + r.Url = m.Url + r.Certs = m.Certs + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *FactGRPCConfig) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *FactEndpointConfig) CloneVT() *FactEndpointConfig { + if m == nil { + return (*FactEndpointConfig)(nil) + } + r := new(FactEndpointConfig) + r.Address = m.Address + r.ExposeMetrics = m.ExposeMetrics + r.HealthCheck = m.HealthCheck + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *FactEndpointConfig) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *FactSettings) CloneVT() *FactSettings { + if m == nil { + return (*FactSettings)(nil) + } + r := new(FactSettings) + r.Grpc = m.Grpc.CloneVT() + r.Endpoint = m.Endpoint.CloneVT() + r.SkipPreFlight = m.SkipPreFlight + r.Json = m.Json + r.RingbufSize = m.RingbufSize + r.Hotreload = m.Hotreload + if rhs := m.Paths; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.Paths = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *FactSettings) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (this *FactGRPCConfig) EqualVT(that *FactGRPCConfig) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Url != that.Url { + return false + } + if this.Certs != that.Certs { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FactGRPCConfig) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*FactGRPCConfig) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *FactEndpointConfig) EqualVT(that *FactEndpointConfig) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Address != that.Address { + return false + } + if this.ExposeMetrics != that.ExposeMetrics { + return false + } + if this.HealthCheck != that.HealthCheck { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FactEndpointConfig) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*FactEndpointConfig) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *FactSettings) EqualVT(that *FactSettings) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Paths) != len(that.Paths) { + return false + } + for i, vx := range this.Paths { + vy := that.Paths[i] + if vx != vy { + return false + } + } + if !this.Grpc.EqualVT(that.Grpc) { + return false + } + if !this.Endpoint.EqualVT(that.Endpoint) { + return false + } + if this.SkipPreFlight != that.SkipPreFlight { + return false + } + if this.Json != that.Json { + return false + } + if this.RingbufSize != that.RingbufSize { + return false + } + if this.Hotreload != that.Hotreload { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *FactSettings) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*FactSettings) + if !ok { + return false + } + return this.EqualVT(that) +} +func (m *FactGRPCConfig) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FactGRPCConfig) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FactGRPCConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Certs) > 0 { + i -= len(m.Certs) + copy(dAtA[i:], m.Certs) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Certs))) + i-- + dAtA[i] = 0x12 + } + if len(m.Url) > 0 { + i -= len(m.Url) + copy(dAtA[i:], m.Url) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Url))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FactEndpointConfig) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FactEndpointConfig) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FactEndpointConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.HealthCheck { + i-- + if m.HealthCheck { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.ExposeMetrics { + i-- + if m.ExposeMetrics { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FactSettings) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FactSettings) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *FactSettings) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Hotreload { + i-- + if m.Hotreload { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } + if m.RingbufSize != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RingbufSize)) + i-- + dAtA[i] = 0x30 + } + if m.Json { + i-- + if m.Json { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.SkipPreFlight { + i-- + if m.SkipPreFlight { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.Endpoint != nil { + size, err := m.Endpoint.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + if m.Grpc != nil { + size, err := m.Grpc.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if len(m.Paths) > 0 { + for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Paths[iNdEx]) + copy(dAtA[i:], m.Paths[iNdEx]) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Paths[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FactGRPCConfig) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Url) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Certs) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *FactEndpointConfig) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.ExposeMetrics { + n += 2 + } + if m.HealthCheck { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *FactSettings) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Paths) > 0 { + for _, s := range m.Paths { + l = len(s) + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + if m.Grpc != nil { + l = m.Grpc.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Endpoint != nil { + l = m.Endpoint.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.SkipPreFlight { + n += 2 + } + if m.Json { + n += 2 + } + if m.RingbufSize != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.RingbufSize)) + } + if m.Hotreload { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *FactGRPCConfig) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FactGRPCConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FactGRPCConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Url = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Certs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Certs = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FactEndpointConfig) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FactEndpointConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FactEndpointConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExposeMetrics", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ExposeMetrics = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HealthCheck", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.HealthCheck = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FactSettings) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FactSettings: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FactSettings: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Paths = append(m.Paths, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grpc", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Grpc == nil { + m.Grpc = &FactGRPCConfig{} + } + if err := m.Grpc.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Endpoint == nil { + m.Endpoint = &FactEndpointConfig{} + } + if err := m.Endpoint.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SkipPreFlight", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SkipPreFlight = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Json", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Json = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RingbufSize", wireType) + } + m.RingbufSize = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RingbufSize |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hotreload", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Hotreload = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FactGRPCConfig) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FactGRPCConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FactGRPCConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Url = stringValue + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Certs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Certs = stringValue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FactEndpointConfig) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FactEndpointConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FactEndpointConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Address = stringValue + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExposeMetrics", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ExposeMetrics = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HealthCheck", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.HealthCheck = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FactSettings) UnmarshalVTUnsafe(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FactSettings: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FactSettings: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var stringValue string + if intStringLen > 0 { + stringValue = unsafe.String(&dAtA[iNdEx], intStringLen) + } + m.Paths = append(m.Paths, stringValue) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grpc", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Grpc == nil { + m.Grpc = &FactGRPCConfig{} + } + if err := m.Grpc.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Endpoint == nil { + m.Endpoint = &FactEndpointConfig{} + } + if err := m.Endpoint.UnmarshalVTUnsafe(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SkipPreFlight", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SkipPreFlight = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Json", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Json = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RingbufSize", wireType) + } + m.RingbufSize = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RingbufSize |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hotreload", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Hotreload = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/proto/internalapi/sensor/fact.proto b/proto/internalapi/sensor/fact.proto new file mode 100644 index 0000000000000..91411541217cb --- /dev/null +++ b/proto/internalapi/sensor/fact.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package sensor; + +option cc_enable_arenas = true; +option go_package = "./internalapi/sensor;sensor"; + +message FactGRPCConfig { + string url = 1; + string certs = 2; +} + +message FactEndpointConfig { + string address = 1; + bool expose_metrics = 2; // @gotags: yaml:"expose_metrics" + bool health_check = 3; // @gotags: yaml:"health_check" +} + +message FactSettings { + repeated string paths = 1; + FactGRPCConfig grpc = 2; // @gotags: yaml:",omitempty" + FactEndpointConfig endpoint = 3; // @gotags: yaml:",omitempty" + bool skip_pre_flight = 4; // @gotags: yaml:"skip_pre_flight,omitempty" + bool json = 5; // @gotags: yaml:",omitempty" + uint32 ringbuf_size = 6; // @gotags: yaml:"ringbuf_size,omitempty" + bool hotreload = 7; // @gotags: yaml:",omitempty" +} diff --git a/sensor/common/admissioncontroller/mocks/settings_manager.go b/sensor/common/admissioncontroller/mocks/settings_manager.go index 1ffda89030bee..579ba06e2b4ea 100644 --- a/sensor/common/admissioncontroller/mocks/settings_manager.go +++ b/sensor/common/admissioncontroller/mocks/settings_manager.go @@ -17,6 +17,7 @@ import ( storage "github.com/stackrox/rox/generated/storage" concurrency "github.com/stackrox/rox/pkg/concurrency" gomock "go.uber.org/mock/gomock" + v1 "k8s.io/api/core/v1" ) // MockSettingsManager is a mock of SettingsManager interface. @@ -43,6 +44,20 @@ func (m *MockSettingsManager) EXPECT() *MockSettingsManagerMockRecorder { return m.recorder } +// ConfigMapStream mocks base method. +func (m *MockSettingsManager) ConfigMapStream() concurrency.ReadOnlyValueStream[*v1.ConfigMap] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConfigMapStream") + ret0, _ := ret[0].(concurrency.ReadOnlyValueStream[*v1.ConfigMap]) + return ret0 +} + +// ConfigMapStream indicates an expected call of ConfigMapStream. +func (mr *MockSettingsManagerMockRecorder) ConfigMapStream() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigMapStream", reflect.TypeOf((*MockSettingsManager)(nil).ConfigMapStream)) +} + // FlushCache mocks base method. func (m *MockSettingsManager) FlushCache() { m.ctrl.T.Helper() diff --git a/sensor/common/admissioncontroller/settings_manager.go b/sensor/common/admissioncontroller/settings_manager.go index ae8f629a19b3d..26c28716206cd 100644 --- a/sensor/common/admissioncontroller/settings_manager.go +++ b/sensor/common/admissioncontroller/settings_manager.go @@ -5,6 +5,7 @@ import ( "github.com/stackrox/rox/generated/internalapi/sensor" "github.com/stackrox/rox/generated/storage" "github.com/stackrox/rox/pkg/concurrency" + v1 "k8s.io/api/core/v1" ) // SettingsManager allows managing admission control settings. It allows updating policies and cluster configuration @@ -20,5 +21,6 @@ type SettingsManager interface { FlushCache() SettingsStream() concurrency.ReadOnlyValueStream[*sensor.AdmissionControlSettings] + ConfigMapStream() concurrency.ReadOnlyValueStream[*v1.ConfigMap] SensorEventsStream() concurrency.ReadOnlyValueStream[*sensor.AdmCtrlUpdateResourceRequest] } diff --git a/sensor/common/admissioncontroller/settings_manager_impl.go b/sensor/common/admissioncontroller/settings_manager_impl.go index 95c68389a379c..10ddf33e15497 100644 --- a/sensor/common/admissioncontroller/settings_manager_impl.go +++ b/sensor/common/admissioncontroller/settings_manager_impl.go @@ -1,6 +1,10 @@ package admissioncontroller import ( + "compress/gzip" + "time" + + "github.com/pkg/errors" "github.com/stackrox/rox/generated/internalapi/central" "github.com/stackrox/rox/generated/internalapi/sensor" "github.com/stackrox/rox/generated/storage" @@ -9,18 +13,23 @@ import ( "github.com/stackrox/rox/pkg/centralsensor" "github.com/stackrox/rox/pkg/concurrency" "github.com/stackrox/rox/pkg/env" + "github.com/stackrox/rox/pkg/gziputil" pkgPolicies "github.com/stackrox/rox/pkg/policies" "github.com/stackrox/rox/pkg/protocompat" "github.com/stackrox/rox/pkg/sync" "github.com/stackrox/rox/pkg/uuid" "github.com/stackrox/rox/sensor/common/centralcaps" + "github.com/stackrox/rox/sensor/common/configmap" "github.com/stackrox/rox/sensor/common/store" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type settingsManager struct { mutex sync.Mutex currSettings *sensor.AdmissionControlSettings settingsStream *concurrency.ValueStream[*sensor.AdmissionControlSettings] + configStream *concurrency.ValueStream[*v1.ConfigMap] sensorEventsStream *concurrency.ValueStream[*sensor.AdmCtrlUpdateResourceRequest] hasClusterConfig, hasPolicies bool centralEndpoint string @@ -38,6 +47,7 @@ type clusterIDWaiter interface { // NewSettingsManager creates a new settings manager for admission control settings. func NewSettingsManager(clusterID clusterIDWaiter, deployments store.DeploymentStore, pods store.PodStore) SettingsManager { return &settingsManager{ + configStream: concurrency.NewValueStream[*v1.ConfigMap](nil), settingsStream: concurrency.NewValueStream[*sensor.AdmissionControlSettings](nil), sensorEventsStream: concurrency.NewValueStream[*sensor.AdmCtrlUpdateResourceRequest](nil), centralEndpoint: env.CentralEndpoint.Setting(), @@ -85,7 +95,7 @@ func (p *settingsManager) UpdatePolicies(policies []*storage.Policy) { newSettings.RuntimePolicies = &storage.PolicyList{Policies: runtimePolicies} if p.hasClusterConfig && p.hasPolicies { - p.settingsStream.Push(newSettings) + p.pushSettings(newSettings) } p.currSettings = newSettings @@ -103,7 +113,7 @@ func (p *settingsManager) UpdateConfig(config *storage.DynamicClusterConfig) { newSettings.ClusterConfig = clonedConfig if p.hasClusterConfig && p.hasPolicies { - p.settingsStream.Push(newSettings) + p.pushSettings(newSettings) } p.currSettings = newSettings } @@ -116,15 +126,28 @@ func (p *settingsManager) FlushCache() { newSettings.CacheVersion = uuid.NewV4().String() if p.hasClusterConfig && p.hasPolicies { - p.settingsStream.Push(newSettings) + p.pushSettings(newSettings) } p.currSettings = newSettings } +func (p *settingsManager) pushSettings(newSettings *sensor.AdmissionControlSettings) { + p.settingsStream.Push(newSettings) + if config, err := p.settingsToConfigMap(newSettings); err != nil { + log.Errorf("failed to create config map: %v", err) + } else { + p.configStream.Push(config) + } +} + func (p *settingsManager) SettingsStream() concurrency.ReadOnlyValueStream[*sensor.AdmissionControlSettings] { return p.settingsStream } +func (p *settingsManager) ConfigMapStream() concurrency.ReadOnlyValueStream[*v1.ConfigMap] { + return p.configStream +} + func (p *settingsManager) SensorEventsStream() concurrency.ReadOnlyValueStream[*sensor.AdmCtrlUpdateResourceRequest] { return p.sensorEventsStream } @@ -173,3 +196,61 @@ func (p *settingsManager) convertAndPush(event *central.SensorEvent) { p.sensorEventsStream.Push(converted) } + +func (p *settingsManager) settingsToConfigMap(settings *sensor.AdmissionControlSettings) (*v1.ConfigMap, error) { + clusterConfig := settings.GetClusterConfig() + enforcedDeployTimePolicies := settings.GetEnforcedDeployTimePolicies() + runtimePolicies := settings.GetRuntimePolicies() + if settings == nil || clusterConfig == nil || enforcedDeployTimePolicies == nil || runtimePolicies == nil { + return nil, nil + } + + configBytes, err := clusterConfig.MarshalVT() + if err != nil { + return nil, errors.Wrap(err, "marshaling cluster config") + } + configBytesGZ, err := gziputil.Compress(configBytes, gzip.BestCompression) + if err != nil { + return nil, errors.Wrap(err, "compressing cluster config") + } + + deployTimePoliciesBytes, err := enforcedDeployTimePolicies.MarshalVT() + if err != nil { + return nil, errors.Wrap(err, "encoding deploy-time policies") + } + deployTimePoliciesBytesGZ, err := gziputil.Compress(deployTimePoliciesBytes, gzip.BestCompression) + if err != nil { + return nil, errors.Wrap(err, "compressing deploy-time policies") + } + + runTimePoliciesBytes, err := runtimePolicies.MarshalVT() + if err != nil { + return nil, errors.Wrap(err, "encoding run-time policies") + } + runTimePoliciesBytesGZ, err := gziputil.Compress(runTimePoliciesBytes, gzip.BestCompression) + if err != nil { + return nil, errors.Wrap(err, "compressing run-time policies") + } + + return &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: admissioncontrol.ConfigMapName, + Annotations: configmap.InfoAnnotations("admission-control"), + }, + Data: map[string]string{ + admissioncontrol.LastUpdateTimeDataKey: settings.GetTimestamp().AsTime().Format(time.RFC3339Nano), + admissioncontrol.CacheVersionDataKey: settings.GetCacheVersion(), + admissioncontrol.CentralEndpointDataKey: settings.GetCentralEndpoint(), + admissioncontrol.ClusterIDDataKey: settings.GetClusterId(), + }, + BinaryData: map[string][]byte{ + admissioncontrol.ConfigGZDataKey: configBytesGZ, + admissioncontrol.DeployTimePoliciesGZDataKey: deployTimePoliciesBytesGZ, + admissioncontrol.RunTimePoliciesGZDataKey: runTimePoliciesBytesGZ, + }, + }, nil +} diff --git a/sensor/common/configmap/persister.go b/sensor/common/configmap/persister.go new file mode 100644 index 0000000000000..5368bf2ef6b75 --- /dev/null +++ b/sensor/common/configmap/persister.go @@ -0,0 +1,127 @@ +package configmap + +import ( + "fmt" + + "context" + + "github.com/pkg/errors" + "github.com/stackrox/rox/pkg/centralsensor" + "github.com/stackrox/rox/pkg/concurrency" + "github.com/stackrox/rox/pkg/logging" + "github.com/stackrox/rox/sensor/common" + "github.com/stackrox/rox/sensor/common/message" + "github.com/stackrox/rox/sensor/common/unimplemented" + v1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + v1client "k8s.io/client-go/kubernetes/typed/core/v1" +) + +const ( + annotationInfoKey = `stackrox.io/info` + annotationInfoTextFmt = `ConfigMap for %s service. Automatically generated - do not modify. Your changes will be overwritten.` +) + +var ( + log = logging.LoggerForModule() +) + +func InfoAnnotations(serviceName string) map[string]string { + return map[string]string{ + annotationInfoKey: fmt.Sprintf(annotationInfoTextFmt, serviceName), + } +} + +type configMapPersister struct { + unimplemented.Receiver + + name string + + stopSig concurrency.ErrorSignal + + client v1client.ConfigMapInterface + + settingsStreamIt concurrency.ValueStreamIter[*v1.ConfigMap] +} + +func NewConfigMapPersister(name, namespace string, k8s kubernetes.Interface, settings concurrency.ValueStreamIter[*v1.ConfigMap]) common.SensorComponent { + return &configMapPersister{ + name: name, + client: k8s.CoreV1().ConfigMaps(namespace), + settingsStreamIt: settings, + } +} + +func (p *configMapPersister) Start() error { + if !p.stopSig.Reset() { + return errors.New("config persister was already started") + } + + go p.run() + return nil +} + +func (p *configMapPersister) Name() string { + return fmt.Sprintf("%s.configMapPersister", p.name) +} + +func (p *configMapPersister) Stop() { + p.stopSig.Signal() +} + +func (p *configMapPersister) Notify(common.SensorComponentEvent) {} + +func (p *configMapPersister) Capabilities() []centralsensor.SensorCapability { + return nil +} + +func (p *configMapPersister) ResponsesC() <-chan *message.ExpiringMessage { + return nil +} + +func (p *configMapPersister) ctx() context.Context { + return concurrency.AsContext(&p.stopSig) +} + +func (p *configMapPersister) run() { + // Attempt to apply the initial config, if any. + if err := p.applyCurrentConfigMap(p.ctx()); err != nil { + log.Errorf("Could not apply admission controller config map: %v", err) + } + + for !p.stopSig.IsDone() { + select { + case <-p.stopSig.Done(): + return + + case <-p.settingsStreamIt.Done(): + p.settingsStreamIt = p.settingsStreamIt.TryNext() + + if err := p.applyCurrentConfigMap(p.ctx()); err != nil { + log.Errorf("Could not apply admission controller config map: %v", err) + } + } + } +} + +func (p *configMapPersister) applyCurrentConfigMap(ctx context.Context) error { + configMap := p.settingsStreamIt.Value() + if configMap == nil { + return nil + } + + _, err := p.client.Create(ctx, configMap, metav1.CreateOptions{}) + if err != nil { + if !k8serrors.IsAlreadyExists(err) { + return errors.Wrap(err, "telling Kubernetes to create config map") + } + + if _, err := p.client.Update(ctx, configMap, metav1.UpdateOptions{}); err != nil { + return errors.Wrap(err, "telling Kubernetes to update existing config map") + } + } + + return nil +} diff --git a/sensor/common/detector/detector.go b/sensor/common/detector/detector.go index 2c140ca7cdcf4..c0577d00d911e 100644 --- a/sensor/common/detector/detector.go +++ b/sensor/common/detector/detector.go @@ -34,6 +34,7 @@ import ( "github.com/stackrox/rox/sensor/common/detector/unified" "github.com/stackrox/rox/sensor/common/enforcer" "github.com/stackrox/rox/sensor/common/externalsrcs" + "github.com/stackrox/rox/sensor/common/filesystem" fsUtils "github.com/stackrox/rox/sensor/common/filesystem/utils" "github.com/stackrox/rox/sensor/common/image/cache" "github.com/stackrox/rox/sensor/common/message" @@ -73,7 +74,7 @@ type Detector interface { // New returns a new detector func New(clusterID clusterIDPeekWaiter, enforcer enforcer.Enforcer, admCtrlSettingsMgr admissioncontroller.SettingsManager, deploymentStore store.DeploymentStore, serviceAccountStore store.ServiceAccountStore, cache cache.Image, auditLogEvents chan *sensor.AuditEvents, - auditLogUpdater updater.Component, networkPolicyStore store.NetworkPolicyStore, registryStore *registry.Store, localScan *scan.LocalScan, nodeStore store.NodeStore) Detector { + auditLogUpdater updater.Component, networkPolicyStore store.NetworkPolicyStore, registryStore *registry.Store, localScan *scan.LocalScan, nodeStore store.NodeStore, factSettingsMgr filesystem.SettingsManager) Detector { detectorStopper := concurrency.NewStopper() netFlowQueueSize := queueScaler.ScaleSizeOnNonDefault(env.DetectorNetworkFlowBufferSize) piQueueSize := queueScaler.ScaleSizeOnNonDefault(env.DetectorProcessIndicatorBufferSize) @@ -132,6 +133,7 @@ func New(clusterID clusterIDPeekWaiter, enforcer enforcer.Enforcer, admCtrlSetti admCtrlSettingsMgr: admCtrlSettingsMgr, auditLogUpdater: auditLogUpdater, + factSettingsMgr: factSettingsMgr, detectorStopper: detectorStopper, auditStopper: concurrency.NewStopper(), @@ -173,6 +175,7 @@ type detectorImpl struct { admCtrlSettingsMgr admissioncontroller.SettingsManager auditLogUpdater updater.Component + factSettingsMgr filesystem.SettingsManager detectorStopper concurrency.Stopper auditStopper concurrency.Stopper @@ -320,6 +323,10 @@ func (d *detectorImpl) ProcessPolicySync(ctx context.Context, sync *central.Poli if d.admCtrlSettingsMgr != nil { d.admCtrlSettingsMgr.UpdatePolicies(sync.GetPolicies()) } + + if d.factSettingsMgr != nil { + d.factSettingsMgr.UpdateFactSettings(sync.GetPolicies()) + } return nil } diff --git a/sensor/common/filesystem/settings_manager.go b/sensor/common/filesystem/settings_manager.go new file mode 100644 index 0000000000000..44673df148e80 --- /dev/null +++ b/sensor/common/filesystem/settings_manager.go @@ -0,0 +1,13 @@ +package filesystem + +import ( + "github.com/stackrox/rox/generated/storage" + "github.com/stackrox/rox/pkg/concurrency" + v1 "k8s.io/api/core/v1" +) + +type SettingsManager interface { + UpdateFactSettings(policies []*storage.Policy) + + ConfigMapStream() concurrency.ReadOnlyValueStream[*v1.ConfigMap] +} diff --git a/sensor/common/filesystem/settings_manager_impl.go b/sensor/common/filesystem/settings_manager_impl.go new file mode 100644 index 0000000000000..282bf172b9b9f --- /dev/null +++ b/sensor/common/filesystem/settings_manager_impl.go @@ -0,0 +1,108 @@ +package filesystem + +import ( + "strings" + + "github.com/stackrox/rox/generated/internalapi/sensor" + "github.com/stackrox/rox/generated/storage" + "github.com/stackrox/rox/pkg/booleanpolicy" + "github.com/stackrox/rox/pkg/booleanpolicy/fieldnames" + "github.com/stackrox/rox/pkg/concurrency" + "github.com/stackrox/rox/pkg/logging" + pkgPolicies "github.com/stackrox/rox/pkg/policies" + "github.com/stackrox/rox/pkg/set" + "github.com/stackrox/rox/sensor/common/configmap" + "gopkg.in/yaml.v3" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + configMapName = "fact" + configMapPathsKey = "paths" + + factConfigFile = "fact.yml" +) + +var ( + log = logging.LoggerForModule() +) + +type factSettingsManager struct { + settingsUpdate *concurrency.ValueStream[*v1.ConfigMap] +} + +func NewFactSettingsManager() SettingsManager { + f := &factSettingsManager{ + settingsUpdate: concurrency.NewValueStream[*v1.ConfigMap](nil), + } + + return f +} + +func (f *factSettingsManager) ConfigMapStream() concurrency.ReadOnlyValueStream[*v1.ConfigMap] { + return f.settingsUpdate +} + +func (f *factSettingsManager) UpdateFactSettings(policies []*storage.Policy) { + paths := f.extractFileActivityPaths(policies) + if len(paths) == 0 { + return + } + + newSettings := &sensor.FactSettings{ + Paths: paths, + } + + if settings := f.settingsToConfigMap(newSettings); settings != nil { + f.settingsUpdate.Push(settings) + } +} + +func (f *factSettingsManager) extractFileActivityPaths(policies []*storage.Policy) []string { + paths := set.NewStringSet() + for _, policy := range policies { + if !pkgPolicies.AppliesAtRunTime(policy) || + !booleanpolicy.ContainsOneOf(policy, booleanpolicy.FileAccess) { + // doesn't contain file activity fields, so no paths to extract + continue + } + + booleanpolicy.ForEachValueWithFieldName(policy, fieldnames.FilePath, func(value string) bool { + idx := strings.IndexFunc(value, func(r rune) bool { + return strings.ContainsRune("*?[]{}", r) + }) + + if idx < 0 { + paths.Add(value) + } else { + paths.Add(value[:idx]) + } + return true + }) + } + return paths.AsSlice() +} + +func (f *factSettingsManager) settingsToConfigMap(settings *sensor.FactSettings) *v1.ConfigMap { + factConfigYaml, err := yaml.Marshal(settings) + if err != nil { + log.Errorf("failed to unmarshal fact settings: %v", err) + return nil + } + + return &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Annotations: configmap.InfoAnnotations("fact"), + }, + Data: map[string]string{ + factConfigFile: string(factConfigYaml), + }, + } +} diff --git a/sensor/kubernetes/sensor/sensor.go b/sensor/kubernetes/sensor/sensor.go index 0a67c4f7f2956..d17728627e1de 100644 --- a/sensor/kubernetes/sensor/sensor.go +++ b/sensor/kubernetes/sensor/sensor.go @@ -22,11 +22,13 @@ import ( "github.com/stackrox/rox/sensor/common/admissioncontroller" "github.com/stackrox/rox/sensor/common/compliance" "github.com/stackrox/rox/sensor/common/config" + "github.com/stackrox/rox/sensor/common/configmap" "github.com/stackrox/rox/sensor/common/delegatedregistry" "github.com/stackrox/rox/sensor/common/deployment" "github.com/stackrox/rox/sensor/common/deploymentenhancer" "github.com/stackrox/rox/sensor/common/detector" "github.com/stackrox/rox/sensor/common/externalsrcs" + "github.com/stackrox/rox/sensor/common/filesystem" filesystemPipeline "github.com/stackrox/rox/sensor/common/filesystem/pipeline" filesystemService "github.com/stackrox/rox/sensor/common/filesystem/service" "github.com/stackrox/rox/sensor/common/heritage" @@ -48,7 +50,6 @@ import ( "github.com/stackrox/rox/sensor/common/sensor" signalService "github.com/stackrox/rox/sensor/common/signal" vmIndex "github.com/stackrox/rox/sensor/common/virtualmachine/index" - k8sadmctrl "github.com/stackrox/rox/sensor/kubernetes/admissioncontroller" "github.com/stackrox/rox/sensor/kubernetes/certrefresh" "github.com/stackrox/rox/sensor/kubernetes/clusterhealth" "github.com/stackrox/rox/sensor/kubernetes/clustermetrics" @@ -90,6 +91,7 @@ func CreateSensor(cfg *CreateOptions) (*sensor.Sensor, error) { hm := heritage.NewHeritageManager(pods.GetPodNamespace(), cfg.k8sClient.Kubernetes().CoreV1(), time.Now()) storeProvider := resources.InitializeStore(hm) admCtrlSettingsMgr := admissioncontroller.NewSettingsManager(clusterID, storeProvider.Deployments(), storeProvider.Pods()) + factSettingsMgr := filesystem.NewFactSettingsManager() helmManagedConfig, err := helm.GetHelmManagedConfig(storage.ServiceType_SENSOR_SERVICE) if err != nil { @@ -140,7 +142,7 @@ func CreateSensor(cfg *CreateOptions) (*sensor.Sensor, error) { pubSub := internalmessage.NewMessageSubscriber() - policyDetector := detector.New(clusterID, enforcer, admCtrlSettingsMgr, storeProvider.Deployments(), storeProvider.ServiceAccounts(), imageCache, auditLogEventsInput, auditLogCollectionManager, storeProvider.NetworkPolicies(), storeProvider.Registries(), localScan, storeProvider.Nodes()) + policyDetector := detector.New(clusterID, enforcer, admCtrlSettingsMgr, storeProvider.Deployments(), storeProvider.ServiceAccounts(), imageCache, auditLogEventsInput, auditLogCollectionManager, storeProvider.NetworkPolicies(), storeProvider.Registries(), localScan, storeProvider.Nodes(), factSettingsMgr) reprocessorHandler := reprocessor.NewHandler(admCtrlSettingsMgr, policyDetector, imageCache) pipeline, err := eventpipeline.New(clusterID, cfg.k8sClient, configHandler, policyDetector, reprocessorHandler, k8sNodeName.Setting(), cfg.traceWriter, storeProvider, cfg.eventPipelineQueueSize, pubSub, internalMessageDispatcher) if err != nil { @@ -218,7 +220,25 @@ func CreateSensor(cfg *CreateOptions) (*sensor.Sensor, error) { sensorNamespace := pods.GetPodNamespace() if admCtrlSettingsMgr != nil { - components = append(components, k8sadmctrl.NewConfigMapSettingsPersister(cfg.k8sClient.Kubernetes(), admCtrlSettingsMgr, sensorNamespace)) + components = append(components, + configmap.NewConfigMapPersister( + "admissionController", + sensorNamespace, + cfg.k8sClient.Kubernetes(), + admCtrlSettingsMgr.ConfigMapStream().Iterator(false), + ), + ) + } + + if factSettingsMgr != nil { + components = append(components, + configmap.NewConfigMapPersister( + "fact", + sensorNamespace, + cfg.k8sClient.Kubernetes(), + factSettingsMgr.ConfigMapStream().Iterator(false), + ), + ) } if centralsensor.SecuredClusterIsNotManagedManually(helmManagedConfig) { From 5a8c65d997b6b67ce57d76f8a6515a04923544cf Mon Sep 17 00:00:00 2001 From: Giles Hutton Date: Wed, 18 Feb 2026 16:45:28 +0000 Subject: [PATCH 3/3] Add fact config to helm templates --- .../templates/collector.yaml.htpl | 11 +++++++++-- sensor/common/filesystem/settings_manager_impl.go | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/image/templates/helm/stackrox-secured-cluster/templates/collector.yaml.htpl b/image/templates/helm/stackrox-secured-cluster/templates/collector.yaml.htpl index af6e5d8c35d14..8230795f0626e 100644 --- a/image/templates/helm/stackrox-secured-cluster/templates/collector.yaml.htpl +++ b/image/templates/helm/stackrox-secured-cluster/templates/collector.yaml.htpl @@ -129,8 +129,8 @@ spec: value: "/var/run/secrets/stackrox.io/certs/" - name: FACT_HOST_MOUNT value: "/host" - - name: FACT_PATHS - value: "/etc/ssh/sshd_config:/etc/sudoers:/etc/passwd:/etc/shadow" + - name: FACT_HOTRELOAD + value: "true" {{- include "srox.envVars" (list . "daemonset" "collector" "fact") | nindent 8 }} resources: {{- ._rox.collector._famResources | nindent 10 }} @@ -156,6 +156,9 @@ spec: name: etc-ro readOnly: true mountPropagation: HostToContainer + - mountPath: /etc/stackrox + name: fact-config + readOnly: true {{- end }} [<- end >] @@ -333,6 +336,10 @@ spec: configMap: name: collector-config optional: true + - name: fact-config + configMap: + name: fact-config + optional: true {{- if ._rox.collector.exposeMonitoring }} --- diff --git a/sensor/common/filesystem/settings_manager_impl.go b/sensor/common/filesystem/settings_manager_impl.go index 282bf172b9b9f..a16c173820402 100644 --- a/sensor/common/filesystem/settings_manager_impl.go +++ b/sensor/common/filesystem/settings_manager_impl.go @@ -19,7 +19,7 @@ import ( ) const ( - configMapName = "fact" + configMapName = "fact-config" configMapPathsKey = "paths" factConfigFile = "fact.yml" @@ -69,6 +69,12 @@ func (f *factSettingsManager) extractFileActivityPaths(policies []*storage.Polic continue } + // we need to remove any wildcard information in the path values + // and construct a deduplicated list of prefixes which Fact + // can use to capture events. + // + // This is a fairly dumb algorithm, just split the string on the first + // occurrence of a wildcard character (*?[]{}), and send the prefix booleanpolicy.ForEachValueWithFieldName(policy, fieldnames.FilePath, func(value string) bool { idx := strings.IndexFunc(value, func(r rune) bool { return strings.ContainsRune("*?[]{}", r)