diff --git a/pkg/ebpf/c/common/context.h b/pkg/ebpf/c/common/context.h index 85ef714f5c4b..ebd2acbd6e0c 100644 --- a/pkg/ebpf/c/common/context.h +++ b/pkg/ebpf/c/common/context.h @@ -198,6 +198,7 @@ statfunc int init_program_data(program_data_t *p, void *ctx, u32 event_id) if (event_config != NULL) { p->event->config.field_types = event_config->field_types; p->event->config.submit_for_policies = event_config->submit_for_policies; + p->event->config.data_filter = event_config->data_filter; } } diff --git a/pkg/ebpf/c/common/filtering.h b/pkg/ebpf/c/common/filtering.h index 5c211e3ff8f6..0b871c01e90a 100644 --- a/pkg/ebpf/c/common/filtering.h +++ b/pkg/ebpf/c/common/filtering.h @@ -346,9 +346,11 @@ statfunc u64 match_scope_filters(program_data_t *p) statfunc u64 match_data_filters(program_data_t *p, u8 index) { policies_config_t *policies_cfg = &p->event->policies_config; + // Retrieve the string filter for the current event + // TODO: Dynamically determine the filter and type based on policy configuration + string_filter_config_t *str_filter = &p->event->config.data_filter.string; - if (!(policies_cfg->data_filter_exact_enabled || policies_cfg->data_filter_prefix_enabled || - policies_cfg->data_filter_suffix_enabled)) + if (!(str_filter->exact_enabled || str_filter->prefix_enabled || str_filter->suffix_enabled)) return policies_cfg->enabled_policies; u64 res = 0; @@ -362,7 +364,7 @@ statfunc u64 match_data_filters(program_data_t *p, u8 index) u16 version = p->event->context.policies_version; // Exact match - if (policies_cfg->data_filter_exact_enabled) { + if (str_filter->exact_enabled) { data_filter_key_t *key = get_string_data_filter_buf(DATA_FILTER_BUF1_IDX); if (key == NULL) return 0; @@ -373,7 +375,7 @@ statfunc u64 match_data_filters(program_data_t *p, u8 index) if (!len) return 0; - u64 match_if_key_missing = policies_cfg->data_filter_exact_match_if_key_missing; + u64 match_if_key_missing = str_filter->exact_match_if_key_missing; filter_map = get_event_filter_map(&data_filter_exact_version, version, eventid); res = equality_filter_matches(match_if_key_missing, filter_map, key); explicit_enable_policies |= (res & ~match_if_key_missing); @@ -382,7 +384,7 @@ statfunc u64 match_data_filters(program_data_t *p, u8 index) } // Prefix match - if (policies_cfg->data_filter_prefix_enabled) { + if (str_filter->prefix_enabled) { data_filter_lpm_key_t *key = get_string_data_filter_lpm_buf(DATA_FILTER_BUF1_IDX); if (key == NULL) return 0; @@ -396,7 +398,7 @@ statfunc u64 match_data_filters(program_data_t *p, u8 index) // https://docs.kernel.org/bpf/map_lpm_trie.html key->prefix_len = len * 8; - u64 match_if_key_missing = policies_cfg->data_filter_prefix_match_if_key_missing; + u64 match_if_key_missing = str_filter->prefix_match_if_key_missing; filter_map = get_event_filter_map(&data_filter_prefix_version, version, eventid); res = equality_filter_matches(match_if_key_missing, filter_map, key); explicit_enable_policies |= (res & ~match_if_key_missing); @@ -405,7 +407,7 @@ statfunc u64 match_data_filters(program_data_t *p, u8 index) } // Suffix match - if (policies_cfg->data_filter_suffix_enabled) { + if (str_filter->suffix_enabled) { data_filter_lpm_key_t *key = get_string_data_filter_lpm_buf(DATA_FILTER_BUF1_IDX); if (key == NULL) @@ -417,7 +419,7 @@ statfunc u64 match_data_filters(program_data_t *p, u8 index) key->prefix_len = len * 8; - u64 match_if_key_missing = policies_cfg->data_filter_suffix_match_if_key_missing; + u64 match_if_key_missing = str_filter->suffix_match_if_key_missing; filter_map = get_event_filter_map(&data_filter_suffix_version, version, eventid); res = equality_filter_matches(match_if_key_missing, filter_map, key); explicit_enable_policies |= (res & ~match_if_key_missing); diff --git a/pkg/ebpf/c/types.h b/pkg/ebpf/c/types.h index 09f37ff11750..fa4c0260e0bb 100644 --- a/pkg/ebpf/c/types.h +++ b/pkg/ebpf/c/types.h @@ -347,14 +347,6 @@ typedef struct policies_config { // bitmap with policies that have at least one filter enabled u64 enabled_policies; - // enabled data filters bitmask per filter - u64 data_filter_prefix_enabled; - u64 data_filter_suffix_enabled; - u64 data_filter_exact_enabled; - u64 data_filter_prefix_match_if_key_missing; - u64 data_filter_suffix_match_if_key_missing; - u64 data_filter_exact_match_if_key_missing; - // global min max u64 uid_max; u64 uid_min; @@ -371,9 +363,24 @@ typedef struct config_entry { policies_config_t policies_config; } config_entry_t; +typedef struct string_filter_config { + u64 prefix_enabled; + u64 suffix_enabled; + u64 exact_enabled; + u64 prefix_match_if_key_missing; + u64 suffix_match_if_key_missing; + u64 exact_match_if_key_missing; +} string_filter_config_t; + +typedef struct data_filter_config { + string_filter_config_t string; + // other types of filters +} data_filter_config_t; + typedef struct event_config { u64 submit_for_policies; u64 field_types; + data_filter_config_t data_filter; } event_config_t; enum capture_options_e diff --git a/pkg/policy/ebpf.go b/pkg/policy/ebpf.go index 98d29bdd0f7f..7a6767273323 100644 --- a/pkg/policy/ebpf.go +++ b/pkg/policy/ebpf.go @@ -278,11 +278,18 @@ func (ps *policies) createNewFilterMapsVersion(bpfModule *bpf.Module) error { return nil } +type eventConfig struct { + submitForPolicies uint64 + fieldTypes uint64 + dataFilter dataFilterConfig +} + // createNewEventsMapVersion creates a new version of the events map. func (ps *policies) createNewEventsMapVersion( bpfModule *bpf.Module, rules map[events.ID]*eventFlags, eventsFields map[events.ID][]bufferdecoder.ArgType, + eventsFilterCfg map[events.ID]stringFilterConfig, ) error { polsVersion := ps.version() innerMapName := "events_map" @@ -303,10 +310,10 @@ func (ps *policies) createNewEventsMapVersion( ps.bpfInnerMaps[innerMapName] = newInnerMap for id, ecfg := range rules { - eventConfigVal := make([]byte, 16) - - // bitmap of policies that require this event to be submitted - binary.LittleEndian.PutUint64(eventConfigVal[0:8], ecfg.policiesSubmit) + stringFilter, exist := eventsFilterCfg[id] + if !exist { + stringFilter = stringFilterConfig{} + } // encoded event's field types var fieldTypes uint64 @@ -314,9 +321,17 @@ func (ps *policies) createNewEventsMapVersion( for n, fieldType := range fields { fieldTypes = fieldTypes | (uint64(fieldType) << (8 * n)) } - binary.LittleEndian.PutUint64(eventConfigVal[8:16], fieldTypes) - err := newInnerMap.Update(unsafe.Pointer(&id), unsafe.Pointer(&eventConfigVal[0])) + eventConfig := eventConfig{ + // bitmap of policies that require this event to be submitted + submitForPolicies: ecfg.policiesSubmit, + fieldTypes: fieldTypes, + dataFilter: dataFilterConfig{ + string: stringFilter, + }, + } + + err := newInnerMap.Update(unsafe.Pointer(&id), unsafe.Pointer(&eventConfig)) if err != nil { return errfmt.WrapError(err) } @@ -662,13 +677,6 @@ func (ps *policies) updateBPF( createNewMaps bool, updateProcTree bool, ) (*PoliciesConfig, error) { - if createNewMaps { - // Create new events map version - if err := ps.createNewEventsMapVersion(bpfModule, rules, eventsFields); err != nil { - return nil, errfmt.WrapError(err) - } - } - fEqs := &filtersEqualities{ uidEqualities: make(map[uint64]equality), pidEqualities: make(map[uint64]equality), @@ -683,15 +691,22 @@ func (ps *policies) updateBPF( binaryEqualities: make(map[filters.NSBinary]equality), } + fEvtCfg := make(map[events.ID]stringFilterConfig) + if err := ps.computeFilterEqualities(fEqs, cts); err != nil { return nil, errfmt.WrapError(err) } - if err := ps.computeDataFilterEqualities(fEqs); err != nil { + if err := ps.computeDataFilterEqualities(fEqs, fEvtCfg); err != nil { return nil, errfmt.WrapError(err) } if createNewMaps { + // Create new events map version + if err := ps.createNewEventsMapVersion(bpfModule, rules, eventsFields, fEvtCfg); err != nil { + return nil, errfmt.WrapError(err) + } + // Create new filter maps version if err := ps.createNewFilterMapsVersion(bpfModule); err != nil { return nil, errfmt.WrapError(err) @@ -835,13 +850,6 @@ type PoliciesConfig struct { EnabledPolicies uint64 - DataFilterPrefixEnabled uint64 - DataFilterSuffixEnabled uint64 - DataFilterExactEnabled uint64 - DataFilterPrefixMatchIfKeyMissing uint64 - DataFilterSuffixMatchIfKeyMissing uint64 - DataFilterExactMatchIfKeyMissing uint64 - UidMax uint64 UidMin uint64 PidMax uint64 @@ -908,15 +916,6 @@ func (ps *policies) computePoliciesConfig() *PoliciesConfig { if p.Follow { cfg.FollowFilterEnabled |= 1 << offset } - if ps.kernellandPolicyMatchStates[offset].EnabledDataExactMatch() { - cfg.DataFilterExactEnabled |= 1 << offset - } - if ps.kernellandPolicyMatchStates[offset].EnabledDataPrefixMatch() { - cfg.DataFilterPrefixEnabled |= 1 << offset - } - if ps.kernellandPolicyMatchStates[offset].EnabledDataSuffixMatch() { - cfg.DataFilterSuffixEnabled |= 1 << offset - } // bitmap indicating whether to match a rule if the key is missing from its filter map if p.UIDFilter.MatchIfKeyMissing() { cfg.UIDFilterMatchIfKeyMissing |= 1 << offset @@ -954,15 +953,6 @@ func (ps *policies) computePoliciesConfig() *PoliciesConfig { if p.BinaryFilter.MatchIfKeyMissing() { cfg.BinPathFilterMatchIfKeyMissing |= 1 << offset } - if ps.kernellandPolicyMatchStates[offset].MatchIfKeyMissingDataExactMatch() { - cfg.DataFilterExactMatchIfKeyMissing |= 1 << offset - } - if ps.kernellandPolicyMatchStates[offset].MatchIfKeyMissingDataPrefixMatch() { - cfg.DataFilterPrefixMatchIfKeyMissing |= 1 << offset - } - if ps.kernellandPolicyMatchStates[offset].MatchIfKeyMissingDataSuffixMatch() { - cfg.DataFilterSuffixMatchIfKeyMissing |= 1 << offset - } cfg.EnabledPolicies |= 1 << offset } diff --git a/pkg/policy/equality_data.go b/pkg/policy/equality_data.go index 5aa010f7e483..079f9975ca69 100644 --- a/pkg/policy/equality_data.go +++ b/pkg/policy/equality_data.go @@ -6,19 +6,18 @@ import ( "github.com/aquasecurity/tracee/pkg/utils" ) -// Constants for match states (kernel) -const ( - exactMatchEnabled = 1 << iota - notExactMatchEnabled - prefixMatchEnabled - notPrefixMatchEnabled - suffixMatchEnabled - notSuffixMatchEnabled -) +type dataFilterConfig struct { + string stringFilterConfig + // other types of filters +} -// KernelMatchStates - stores the bitfield for match states -type KernelMatchStates struct { - matchState uint8 +type stringFilterConfig struct { + prefixEnabled uint64 + suffixEnabled uint64 + exactEnabled uint64 + prefixMatchIfKeyMissing uint64 + suffixMatchIfKeyMissing uint64 + exactMatchIfKeyMissing uint64 } type KernelDataFields struct { @@ -26,9 +25,57 @@ type KernelDataFields struct { String string } +func (d *stringFilterConfig) EnableExact(policyID int) { + d.exactEnabled |= 1 << policyID +} + +func (d *stringFilterConfig) EnablePrefix(policyID int) { + d.prefixEnabled |= 1 << policyID +} + +func (d *stringFilterConfig) EnableSuffix(policyID int) { + d.suffixEnabled |= 1 << policyID +} + +func (d *stringFilterConfig) EnablePrefixMatchIfKeyMissing(policyID int) { + d.prefixMatchIfKeyMissing |= 1 << policyID +} + +func (d *stringFilterConfig) EnableSuffixMatchIfKeyMissing(policyID int) { + d.suffixMatchIfKeyMissing |= 1 << policyID +} + +func (d *stringFilterConfig) EnableExactMatchIfKeyMissing(policyID int) { + d.exactMatchIfKeyMissing |= 1 << policyID +} + +func combineEventBitmap(eventsMap map[events.ID]stringFilterConfig, eventID events.ID, strCfgFilter *stringFilterConfig) { + existingFilter, exists := eventsMap[eventID] + if !exists { + eventsMap[eventID] = stringFilterConfig{ + prefixEnabled: strCfgFilter.prefixEnabled, + suffixEnabled: strCfgFilter.suffixEnabled, + exactEnabled: strCfgFilter.exactEnabled, + prefixMatchIfKeyMissing: strCfgFilter.prefixMatchIfKeyMissing, + suffixMatchIfKeyMissing: strCfgFilter.suffixMatchIfKeyMissing, + exactMatchIfKeyMissing: strCfgFilter.exactMatchIfKeyMissing, + } + return + } + + existingFilter.prefixEnabled |= strCfgFilter.prefixEnabled + existingFilter.suffixEnabled |= strCfgFilter.suffixEnabled + existingFilter.exactEnabled |= strCfgFilter.exactEnabled + existingFilter.prefixMatchIfKeyMissing |= strCfgFilter.prefixMatchIfKeyMissing + existingFilter.suffixMatchIfKeyMissing |= strCfgFilter.suffixMatchIfKeyMissing + existingFilter.exactMatchIfKeyMissing |= strCfgFilter.exactMatchIfKeyMissing + + eventsMap[eventID] = existingFilter +} + // computeDataFilterEqualities computes the equalities for the kernel data filter // in the policies updating the provided eqs map. -func (ps *policies) computeDataFilterEqualities(fEqs *filtersEqualities) error { +func (ps *policies) computeDataFilterEqualities(fEqs *filtersEqualities, eventsConfig map[events.ID]stringFilterConfig) error { for _, p := range ps.allFromMap() { // Reinitialize variables at the start of each iteration combinedEqualities := make(map[KernelDataFields]struct{}) @@ -40,13 +87,17 @@ func (ps *policies) computeDataFilterEqualities(fEqs *filtersEqualities) error { policyID := p.ID for eventID, rule := range p.Rules { + strCfgFilter := &stringFilterConfig{} equalities, err := rule.DataFilter.Equalities() if err != nil { continue } - ps.handleExactMatches(policyID, eventID, equalities, combinedEqualities, combinedNotEqualities) - ps.handlePrefixMatches(policyID, eventID, equalities, combinedPrefixEqualities, combinedNotPrefixEqualities) - ps.handleSuffixMatches(policyID, eventID, equalities, combinedSuffixEqualities, combinedNotSuffixEqualities) + ps.handleExactMatches(policyID, eventID, strCfgFilter, equalities, combinedEqualities, combinedNotEqualities) + ps.handlePrefixMatches(policyID, eventID, strCfgFilter, equalities, combinedPrefixEqualities, combinedNotPrefixEqualities) + ps.handleSuffixMatches(policyID, eventID, strCfgFilter, equalities, combinedSuffixEqualities, combinedNotSuffixEqualities) + + // Combine the event bitmap across all policies + combineEventBitmap(eventsConfig, eventID, strCfgFilter) } // Exact match equalities @@ -65,72 +116,46 @@ func (ps *policies) computeDataFilterEqualities(fEqs *filtersEqualities) error { return nil } -func (ps *policies) handleExactMatches(policyId int, eventID events.ID, equalities filters.StringFilterEqualities, combinedEqualities, combinedNotEqualities map[KernelDataFields]struct{}) { +func (ps *policies) handleExactMatches(policyId int, eventID events.ID, filter *stringFilterConfig, equalities filters.StringFilterEqualities, combinedEqualities, combinedNotEqualities map[KernelDataFields]struct{}) { for k := range equalities.ExactEqual { combinedEqualities[KernelDataFields{eventID, k}] = struct{}{} - ps.kernellandPolicyMatchStates[policyId].EnableState(exactMatchEnabled) + + filter.EnableExact(policyId) } for k := range equalities.ExactNotEqual { combinedNotEqualities[KernelDataFields{eventID, k}] = struct{}{} - ps.kernellandPolicyMatchStates[policyId].EnableState(exactMatchEnabled) - ps.kernellandPolicyMatchStates[policyId].EnableState(notExactMatchEnabled) + + filter.EnableExact(policyId) + filter.EnableExactMatchIfKeyMissing(policyId) } } -func (ps *policies) handlePrefixMatches(policyId int, eventID events.ID, equalities filters.StringFilterEqualities, combinedPrefixEqualities, combinedNotPrefixEqualities map[KernelDataFields]struct{}) { +func (ps *policies) handlePrefixMatches(policyId int, eventID events.ID, filter *stringFilterConfig, equalities filters.StringFilterEqualities, combinedPrefixEqualities, combinedNotPrefixEqualities map[KernelDataFields]struct{}) { for k := range equalities.PrefixEqual { combinedPrefixEqualities[KernelDataFields{eventID, k}] = struct{}{} - ps.kernellandPolicyMatchStates[policyId].EnableState(prefixMatchEnabled) + + filter.EnablePrefix(policyId) } for k := range equalities.PrefixNotEqual { combinedNotPrefixEqualities[KernelDataFields{eventID, k}] = struct{}{} - ps.kernellandPolicyMatchStates[policyId].EnableState(prefixMatchEnabled) - ps.kernellandPolicyMatchStates[policyId].EnableState(notPrefixMatchEnabled) + + filter.EnablePrefix(policyId) + filter.EnablePrefixMatchIfKeyMissing(policyId) } } -func (ps *policies) handleSuffixMatches(policyId int, eventID events.ID, equalities filters.StringFilterEqualities, combinedSuffixEqualities, combinedNotSuffixEqualities map[KernelDataFields]struct{}) { +func (ps *policies) handleSuffixMatches(policyId int, eventID events.ID, filter *stringFilterConfig, equalities filters.StringFilterEqualities, combinedSuffixEqualities, combinedNotSuffixEqualities map[KernelDataFields]struct{}) { for k := range equalities.SuffixEqual { reversed := utils.ReverseString(k) combinedSuffixEqualities[KernelDataFields{eventID, reversed}] = struct{}{} - ps.kernellandPolicyMatchStates[policyId].EnableState(suffixMatchEnabled) + + filter.EnableSuffix(policyId) } for k := range equalities.SuffixNotEqual { reversed := utils.ReverseString(k) combinedNotSuffixEqualities[KernelDataFields{eventID, reversed}] = struct{}{} - ps.kernellandPolicyMatchStates[policyId].EnableState(suffixMatchEnabled) - ps.kernellandPolicyMatchStates[policyId].EnableState(notSuffixMatchEnabled) - } -} -func (k *KernelMatchStates) EnableState(state uint8) { - k.matchState |= state -} - -func (k *KernelMatchStates) IsStateEnabled(state uint8) bool { - return k.matchState&state != 0 -} - -func (k *KernelMatchStates) EnabledDataExactMatch() bool { - return k.IsStateEnabled(exactMatchEnabled) -} - -func (k *KernelMatchStates) MatchIfKeyMissingDataExactMatch() bool { - return k.IsStateEnabled(notExactMatchEnabled) -} - -func (k *KernelMatchStates) EnabledDataPrefixMatch() bool { - return k.IsStateEnabled(prefixMatchEnabled) -} - -func (k *KernelMatchStates) MatchIfKeyMissingDataPrefixMatch() bool { - return k.IsStateEnabled(notPrefixMatchEnabled) -} - -func (k *KernelMatchStates) EnabledDataSuffixMatch() bool { - return k.IsStateEnabled(suffixMatchEnabled) -} - -func (k *KernelMatchStates) MatchIfKeyMissingDataSuffixMatch() bool { - return k.IsStateEnabled(notSuffixMatchEnabled) + filter.EnableSuffix(policyId) + filter.EnableSuffixMatchIfKeyMissing(policyId) + } } diff --git a/pkg/policy/policies.go b/pkg/policy/policies.go index c8cf03af4caf..1334711b47ef 100644 --- a/pkg/policy/policies.go +++ b/pkg/policy/policies.go @@ -28,35 +28,33 @@ type policies struct { // computed values - userlandPolicies []*Policy // reduced list with userland filterable policies (read in a hot path) - uidFilterMin uint64 - uidFilterMax uint64 - pidFilterMin uint64 - pidFilterMax uint64 - uidFilterableInUserland bool - pidFilterableInUserland bool - filterableInUserland bool - containerFiltersEnabled uint64 // bitmap of policies that have at least one container filter type enabled - kernellandPolicyMatchStates map[int]*KernelMatchStates + userlandPolicies []*Policy // reduced list with userland filterable policies (read in a hot path) + uidFilterMin uint64 + uidFilterMax uint64 + pidFilterMin uint64 + pidFilterMax uint64 + uidFilterableInUserland bool + pidFilterableInUserland bool + filterableInUserland bool + containerFiltersEnabled uint64 // bitmap of policies that have at least one container filter type enabled } func NewPolicies() *policies { return &policies{ - bpfInnerMaps: map[string]*bpf.BPFMapLow{}, - policiesArray: [PolicyMax]*Policy{}, - policiesMapByID: map[int]*Policy{}, - policiesMapByName: map[string]*Policy{}, - policiesList: []*Policy{}, - userlandPolicies: []*Policy{}, - uidFilterMin: filters.MinNotSetUInt, - uidFilterMax: filters.MaxNotSetUInt, - pidFilterMin: filters.MinNotSetUInt, - pidFilterMax: filters.MaxNotSetUInt, - uidFilterableInUserland: false, - pidFilterableInUserland: false, - filterableInUserland: false, - containerFiltersEnabled: 0, - kernellandPolicyMatchStates: make(map[int]*KernelMatchStates), + bpfInnerMaps: map[string]*bpf.BPFMapLow{}, + policiesArray: [PolicyMax]*Policy{}, + policiesMapByID: map[int]*Policy{}, + policiesMapByName: map[string]*Policy{}, + policiesList: []*Policy{}, + userlandPolicies: []*Policy{}, + uidFilterMin: filters.MinNotSetUInt, + uidFilterMax: filters.MaxNotSetUInt, + pidFilterMin: filters.MinNotSetUInt, + pidFilterMax: filters.MaxNotSetUInt, + uidFilterableInUserland: false, + pidFilterableInUserland: false, + filterableInUserland: false, + containerFiltersEnabled: 0, } } @@ -90,7 +88,6 @@ func set(ps *policies, id int, p *Policy) error { ps.policiesMapByID[id] = p ps.policiesMapByName[p.Name] = p ps.policiesList = append(ps.policiesList, p) - ps.kernellandPolicyMatchStates[id] = &KernelMatchStates{} ps.compute() diff --git a/pkg/policy/policies_test.go b/pkg/policy/policies_test.go index c0e1d5f302af..5cc25a14ed75 100644 --- a/pkg/policy/policies_test.go +++ b/pkg/policy/policies_test.go @@ -63,7 +63,6 @@ func TestPoliciesClone(t *testing.T) { sets.PrefixSet{}, sets.SuffixSet{}, filters.KernelDataFilter{}, - KernelMatchStates{}, ) opt2 := cmp.FilterPath( func(p cmp.Path) bool { diff --git a/tests/integration/event_filters_test.go b/tests/integration/event_filters_test.go index 40be6c02de50..1b1af4a4fe5f 100644 --- a/tests/integration/event_filters_test.go +++ b/tests/integration/event_filters_test.go @@ -1799,6 +1799,91 @@ func Test_EventFilters(t *testing.T) { coolDown: 0, test: ExpectAllInOrderSequentially, }, + { + name: "comm: event: data: trace event security_file_open and magic_write using multiple filter types combined", + policyFiles: []testutils.PolicyFileWithID{ + { + Id: 1, + PolicyFile: v1beta1.PolicyFile{ + Metadata: v1beta1.Metadata{ + Name: "sfo-mw-combined-pol-1", + }, + Spec: k8s.PolicySpec{ + Scope: []string{ + "comm=cat", + }, + DefaultActions: []string{"log"}, + Rules: []k8s.Rule{ + { + Event: "security_file_open", + Filters: []string{ + "data.pathname=/etc/netconfig", + "data.pathname!=/usr/lib/*", + }, + }, + { + Event: "magic_write", + Filters: []string{ + "data.pathname=/tmp/netconfig", + }, + }, + }, + }, + }, + }, + { + Id: 2, + PolicyFile: v1beta1.PolicyFile{ + Metadata: v1beta1.Metadata{ + Name: "sfo-mw-combined-pol-2", + }, + Spec: k8s.PolicySpec{ + Scope: []string{ + "comm=cat", + }, + DefaultActions: []string{"log"}, + Rules: []k8s.Rule{ + { + Event: "security_file_open", + Filters: []string{ + "data.pathname=/etc/netconfig", + }, + }, + { + Event: "magic_write", + Filters: []string{ + "data.pathname!=/tmp/netconfig", + }, + }, + }, + }, + }, + }, + }, + cmdEvents: []cmdEvents{ + newCmdEvents( + // To test certain "not equal" filters, such as exact, prefix, and suffix, + // it was necessary to use a fixed version of Ubuntu to ensure consistent + // library versions. + "docker run --rm ubuntu:jammy-20240911.1 sh -c 'cat /etc/netconfig > /tmp/netconfig'", + 0, + 20*time.Second, + // Running the commands inside a container caused duplicate + // security_file_open events to be generated. This is why the events are duplicated. + []trace.Event{ + expectEvent(anyHost, "cat", anyProcessorID, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-combined-pol-1"), orPolIDs(1), expectArg("pathname", "/etc/ld.so.cache")), + expectEvent(anyHost, "cat", anyProcessorID, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-combined-pol-1"), orPolIDs(1), expectArg("pathname", "/etc/ld.so.cache")), + expectEvent(anyHost, "cat", anyProcessorID, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-combined-pol-1", "sfo-mw-combined-pol-2"), orPolIDs(1, 2), expectArg("pathname", "/etc/netconfig")), + expectEvent(anyHost, "cat", anyProcessorID, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-combined-pol-1", "sfo-mw-combined-pol-2"), orPolIDs(1, 2), expectArg("pathname", "/etc/netconfig")), + expectEvent(anyHost, "cat", anyProcessorID, anyPID, 0, events.MagicWrite, orPolNames("sfo-mw-combined-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/netconfig")), + }, + []string{}, + ), + }, + useSyscaller: false, + coolDown: 0, + test: ExpectAllInOrderSequentially, + }, { name: "comm: event: data: trace event magic_write set in multiple policies using multiple filter types", policyFiles: []testutils.PolicyFileWithID{ @@ -1850,12 +1935,12 @@ func Test_EventFilters(t *testing.T) { }, cmdEvents: []cmdEvents{ newCmdEvents( - "cp /etc/resolv.conf /etc/netconfig /etc/passwd /tmp/", + "sh -c 'cp /etc/resolv.conf /tmp; cp /etc/shadow /tmp; cp /etc/passwd /tmp/'", 0, 1*time.Second, []trace.Event{ expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.MagicWrite, orPolNames("mw-pol-1", "mw-pol-2"), orPolIDs(1, 2), expectArg("pathname", "/tmp/resolv.conf")), - expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.MagicWrite, orPolNames("mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/netconfig")), + expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.MagicWrite, orPolNames("mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/shadow")), expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.MagicWrite, orPolNames("mw-pol-1", "mw-pol-2"), orPolIDs(1, 2), expectArg("pathname", "/tmp/passwd")), }, []string{}, @@ -1863,7 +1948,7 @@ func Test_EventFilters(t *testing.T) { }, useSyscaller: false, coolDown: 0, - test: ExpectAllInOrderSequentially, + test: ExpectAtLeastOneForEach, }, { name: "comm: event: data: trace event security_mmap_file using multiple filter types", @@ -1943,13 +2028,13 @@ func Test_EventFilters(t *testing.T) { }, cmdEvents: []cmdEvents{ newCmdEvents( - "cp /etc/resolv.conf /etc/netconfig /etc/passwd /tmp/", + "sh -c 'cp /etc/resolv.conf /tmp; cp /etc/shadow /tmp; cp /etc/passwd /tmp/'", 0, 1*time.Second, []trace.Event{ expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/resolv.conf")), expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.MagicWrite, orPolNames("sfo-mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/resolv.conf")), - expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/netconfig")), + expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/shadow")), expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.SecurityFileOpen, orPolNames("sfo-mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/passwd")), expectEvent(anyHost, "cp", testutils.CPUForTests, anyPID, 0, events.MagicWrite, orPolNames("sfo-mw-pol-1"), orPolIDs(1), expectArg("pathname", "/tmp/passwd")), }, @@ -1958,7 +2043,7 @@ func Test_EventFilters(t *testing.T) { }, useSyscaller: false, coolDown: 0, - test: ExpectAllInOrderSequentially, + test: ExpectAtLeastOneForEach, }, { name: "comm: event: data: trace event with pathname exceeding 255 characters",