Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pkg/cmd/flags/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@
if err := p.NewContFilter.Parse("!new"); err != nil {
return err
}
case scopeFlag.operatorAndValues == "=started":
// Container started state filter: container=started (only started containers)
if err := p.ContFilter.Parse(scopeFlag.scopeName); err != nil {
return err
}
if err := p.ContStartedFilter.Parse("started"); err != nil {
return err
}

Check warning on line 177 in pkg/cmd/flags/policy.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/flags/policy.go#L170-L177

Added lines #L170 - L177 were not covered by tests
case scopeFlag.operator == "=":
if err := p.ContIDFilter.Parse(scopeFlag.operatorAndValues); err != nil {
return err
Expand Down
10 changes: 10 additions & 0 deletions pkg/ebpf/c/common/filtering.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ statfunc u64 match_scope_filters(program_data_t *p)
res &= bool_filter_matches(match_bitmap, is_new_container) | mask;
}

if (policies_cfg->cont_started_filter_enabled) {
bool is_started = false;
if (p->event->context.task.flags & CONTAINER_STARTED_FLAG)
is_started = true;
u64 match_bitmap = policies_cfg->cont_started_filter_match_if_key_missing;
u64 mask = ~policies_cfg->cont_started_filter_enabled;

res &= bool_filter_matches(match_bitmap, is_started) | mask;
}

if (policies_cfg->new_pid_filter_enabled) {
u64 match_bitmap = policies_cfg->new_pid_filter_match_if_key_missing;
u64 mask = ~policies_cfg->new_pid_filter_enabled;
Expand Down
2 changes: 2 additions & 0 deletions pkg/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ typedef struct policies_config {
u64 cgroup_id_filter_enabled;
u64 cont_filter_enabled;
u64 new_cont_filter_enabled;
u64 cont_started_filter_enabled;
u64 new_pid_filter_enabled;
u64 proc_tree_filter_enabled;
u64 bin_path_filter_enabled;
Expand All @@ -385,6 +386,7 @@ typedef struct policies_config {
u64 cgroup_id_filter_match_if_key_missing;
u64 cont_filter_match_if_key_missing;
u64 new_cont_filter_match_if_key_missing;
u64 cont_started_filter_match_if_key_missing;
u64 new_pid_filter_match_if_key_missing;
u64 proc_tree_filter_match_if_key_missing;
u64 bin_path_filter_match_if_key_missing;
Expand Down
27 changes: 24 additions & 3 deletions pkg/filters/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
hostNameFilter *StringFilter
cgroupIDFilter *NumericFilter[uint64]
containerFilter *BoolFilter
containerStartedFilter *BoolFilter
containerIDFilter *StringFilter
containerImageFilter *StringFilter
containerImageDigestFilter *StringFilter
Expand Down Expand Up @@ -55,6 +56,7 @@
hostNameFilter: NewStringFilter(nil),
cgroupIDFilter: NewUIntFilter(),
containerFilter: NewBoolFilter(),
containerStartedFilter: NewBoolFilter(),
containerIDFilter: NewStringFilter(nil),
containerImageFilter: NewStringFilter(nil),
containerImageDigestFilter: NewStringFilter(nil),
Expand Down Expand Up @@ -88,6 +90,7 @@
// if we order this by most to least likely filter to be set
// we can short circuit this logic.
return f.containerFilter.Filter(evt.Container.ID != "") &&
f.containerStartedFilter.Filter(evt.ContextFlags.ContainerStarted) &&
f.processNameFilter.Filter(evt.ProcessName) &&
f.timestampFilter.Filter(int64(evt.Timestamp)) &&
f.cgroupIDFilter.Filter(uint64(evt.CgroupID)) &&
Expand Down Expand Up @@ -160,9 +163,26 @@
filter.Enable()
return filter.add(false, Equal)
case "container":
filter := f.containerFilter
filter.Enable()
return filter.add(true, Equal)
// Handle container state filtering: container, container=started
// All container filters require containerFilter to be enabled
f.containerFilter.Enable()
if err := f.containerFilter.add(true, Equal); err != nil {
return errfmt.WrapError(err)
}

Check warning on line 171 in pkg/filters/scope.go

View check run for this annotation

Codecov / codecov/patch

pkg/filters/scope.go#L170-L171

Added lines #L170 - L171 were not covered by tests

if operatorAndValues == "" {
// Just "container" - any container (ID != "")
return nil
}

// Parse container=started
if operatorAndValues == "=started" {
// Only started containers (ContainerStarted == true)
f.containerStartedFilter.Enable()
return f.containerStartedFilter.add(true, Equal)
}

return InvalidExpression(operatorAndValues)
Comment on lines 165 to +185
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yanivagman sorry, I might be missing context but from the PR description I couldn't understand why we also adding a userland filter for it, if we have a ebpf filter. Shouldn't this be like container=new filtered directly in ebpf, without the need for another filter here?

// TODO: change this and below container filters to the format
// eventname.scope.container.id and so on...
case "containerId":
Expand Down Expand Up @@ -233,6 +253,7 @@
n.hostNameFilter = f.hostNameFilter.Clone()
n.cgroupIDFilter = f.cgroupIDFilter.Clone()
n.containerFilter = f.containerFilter.Clone()
n.containerStartedFilter = f.containerStartedFilter.Clone()
n.containerIDFilter = f.containerIDFilter.Clone()
n.containerImageFilter = f.containerImageFilter.Clone()
n.containerImageDigestFilter = f.containerImageDigestFilter.Clone()
Expand Down
123 changes: 123 additions & 0 deletions pkg/filters/scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/aquasecurity/tracee/pkg/filters/sets"
"github.com/aquasecurity/tracee/types/trace"
)

func TestScopeFilterClone(t *testing.T) {
Expand Down Expand Up @@ -39,3 +41,124 @@ func TestScopeFilterClone(t *testing.T) {
t.Errorf("Changes to copied filter affected the original")
}
}

func TestScopeFilter_ContainerStarted(t *testing.T) {
t.Parallel()

tests := []struct {
name string
field string
operatorValues string
event trace.Event
expectedMatch bool
expectedErrStr string
}{
{
name: "container - matches any container",
field: "container",
operatorValues: "",
event: trace.Event{
Container: trace.Container{ID: "abc123"},
ContextFlags: trace.ContextFlags{ContainerStarted: false},
},
expectedMatch: true,
},
{
name: "container - matches started container",
field: "container",
operatorValues: "",
event: trace.Event{
Container: trace.Container{ID: "abc123"},
ContextFlags: trace.ContextFlags{ContainerStarted: true},
},
expectedMatch: true,
},
{
name: "container - does not match host",
field: "container",
operatorValues: "",
event: trace.Event{
Container: trace.Container{ID: ""},
ContextFlags: trace.ContextFlags{ContainerStarted: false},
},
expectedMatch: false,
},
{
name: "container=started - matches started container",
field: "container",
operatorValues: "=started",
event: trace.Event{
Container: trace.Container{ID: "abc123"},
ContextFlags: trace.ContextFlags{ContainerStarted: true},
},
expectedMatch: true,
},
{
name: "container=started - does not match init container",
field: "container",
operatorValues: "=started",
event: trace.Event{
Container: trace.Container{ID: "abc123"},
ContextFlags: trace.ContextFlags{ContainerStarted: false},
},
expectedMatch: false,
},
{
name: "container=started - does not match host",
field: "container",
operatorValues: "=started",
event: trace.Event{
Container: trace.Container{ID: ""},
ContextFlags: trace.ContextFlags{ContainerStarted: false},
},
expectedMatch: false,
},
{
name: "host - matches host events",
field: "host",
operatorValues: "",
event: trace.Event{
Container: trace.Container{ID: ""},
ContextFlags: trace.ContextFlags{ContainerStarted: false},
},
expectedMatch: true,
},
{
name: "host - does not match container",
field: "host",
operatorValues: "",
event: trace.Event{
Container: trace.Container{ID: "abc123"},
ContextFlags: trace.ContextFlags{ContainerStarted: false},
},
expectedMatch: false,
},
{
name: "container=invalid - returns error",
field: "container",
operatorValues: "=invalid",
event: trace.Event{},
expectedMatch: false,
expectedErrStr: "invalid filter expression",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

filter := NewScopeFilter()
err := filter.Parse(tt.field, tt.operatorValues)

if tt.expectedErrStr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedErrStr)
return
}

require.NoError(t, err)
match := filter.Filter(tt.event)
assert.Equal(t, tt.expectedMatch, match, "Event should match=%v, got=%v", tt.expectedMatch, match)
})
}
}
60 changes: 34 additions & 26 deletions pkg/policy/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -841,32 +841,34 @@
// Order of fields is important, as it is used as a value for
// the PoliciesConfigMap BPF map.
type PoliciesConfig struct {
UIDFilterEnabled uint64
PIDFilterEnabled uint64
MntNsFilterEnabled uint64
PidNsFilterEnabled uint64
UtsNsFilterEnabled uint64
CommFilterEnabled uint64
CgroupIdFilterEnabled uint64
ContFilterEnabled uint64
NewContFilterEnabled uint64
NewPidFilterEnabled uint64
ProcTreeFilterEnabled uint64
BinPathFilterEnabled uint64
FollowFilterEnabled uint64

UIDFilterMatchIfKeyMissing uint64
PIDFilterMatchIfKeyMissing uint64
MntNsFilterMatchIfKeyMissing uint64
PidNsFilterMatchIfKeyMissing uint64
UtsNsFilterMatchIfKeyMissing uint64
CommFilterMatchIfKeyMissing uint64
CgroupIdFilterMatchIfKeyMissing uint64
ContFilterMatchIfKeyMissing uint64
NewContFilterMatchIfKeyMissing uint64
NewPidFilterMatchIfKeyMissing uint64
ProcTreeFilterMatchIfKeyMissing uint64
BinPathFilterMatchIfKeyMissing uint64
UIDFilterEnabled uint64
PIDFilterEnabled uint64
MntNsFilterEnabled uint64
PidNsFilterEnabled uint64
UtsNsFilterEnabled uint64
CommFilterEnabled uint64
CgroupIdFilterEnabled uint64
ContFilterEnabled uint64
NewContFilterEnabled uint64
ContStartedFilterEnabled uint64
NewPidFilterEnabled uint64
ProcTreeFilterEnabled uint64
BinPathFilterEnabled uint64
FollowFilterEnabled uint64

UIDFilterMatchIfKeyMissing uint64
PIDFilterMatchIfKeyMissing uint64
MntNsFilterMatchIfKeyMissing uint64
PidNsFilterMatchIfKeyMissing uint64
UtsNsFilterMatchIfKeyMissing uint64
CommFilterMatchIfKeyMissing uint64
CgroupIdFilterMatchIfKeyMissing uint64
ContFilterMatchIfKeyMissing uint64
NewContFilterMatchIfKeyMissing uint64
ContStartedFilterMatchIfKeyMissing uint64
NewPidFilterMatchIfKeyMissing uint64
ProcTreeFilterMatchIfKeyMissing uint64
BinPathFilterMatchIfKeyMissing uint64

EnabledPolicies uint64

Expand Down Expand Up @@ -924,6 +926,9 @@
if p.NewContFilter.Enabled() {
cfg.NewContFilterEnabled |= 1 << offset
}
if p.ContStartedFilter.Enabled() {
cfg.ContStartedFilterEnabled |= 1 << offset
}

Check warning on line 931 in pkg/policy/ebpf.go

View check run for this annotation

Codecov / codecov/patch

pkg/policy/ebpf.go#L929-L931

Added lines #L929 - L931 were not covered by tests
if p.NewPidFilter.Enabled() {
cfg.NewPidFilterEnabled |= 1 << offset
}
Expand Down Expand Up @@ -964,6 +969,9 @@
if p.NewContFilter.MatchIfKeyMissing() {
cfg.NewContFilterMatchIfKeyMissing |= 1 << offset
}
if p.ContStartedFilter.MatchIfKeyMissing() {
cfg.ContStartedFilterMatchIfKeyMissing |= 1 << offset
}

Check warning on line 974 in pkg/policy/ebpf.go

View check run for this annotation

Codecov / codecov/patch

pkg/policy/ebpf.go#L972-L974

Added lines #L972 - L974 were not covered by tests
if p.NewPidFilter.MatchIfKeyMissing() {
cfg.NewPidFilterMatchIfKeyMissing |= 1 << offset
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Policy struct {
ContFilter *filters.BoolFilter
NewContFilter *filters.BoolFilter
ContIDFilter *filters.StringFilter
ContStartedFilter *filters.BoolFilter
ProcessTreeFilter *filters.ProcessTreeFilter
BinaryFilter *filters.BinaryFilter
Follow bool
Expand Down Expand Up @@ -49,6 +50,7 @@ func NewPolicy() *Policy {
ContFilter: filters.NewBoolFilter(),
NewContFilter: filters.NewBoolFilter(),
ContIDFilter: filters.NewStringFilter(nil),
ContStartedFilter: filters.NewBoolFilter(),
ProcessTreeFilter: filters.NewProcessTreeFilter(),
BinaryFilter: filters.NewBinaryFilter(),
Follow: false,
Expand All @@ -60,7 +62,8 @@ func NewPolicy() *Policy {
func (p *Policy) ContainerFilterEnabled() bool {
return (p.ContFilter.Enabled() && p.ContFilter.Value()) ||
(p.NewContFilter.Enabled() && p.NewContFilter.Value()) ||
p.ContIDFilter.Enabled()
p.ContIDFilter.Enabled() ||
p.ContStartedFilter.Enabled()
}

func (p *Policy) Clone() *Policy {
Expand All @@ -82,6 +85,7 @@ func (p *Policy) Clone() *Policy {
n.ContFilter = p.ContFilter.Clone()
n.NewContFilter = p.NewContFilter.Clone()
n.ContIDFilter = p.ContIDFilter.Clone()
n.ContStartedFilter = p.ContStartedFilter.Clone()
n.ProcessTreeFilter = p.ProcessTreeFilter.Clone()
n.BinaryFilter = p.BinaryFilter.Clone()
n.Follow = p.Follow
Expand Down
Loading