Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Policies versioning (map of maps) #3305

Merged
merged 2 commits into from
Jan 15, 2024
Merged
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
10 changes: 5 additions & 5 deletions pkg/bufferdecoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ func (decoder *EbpfDecoder) DecodeContext(eCtx *EventContext) error {

eCtx.EventID = events.ID(int32(binary.LittleEndian.Uint32(decoder.buffer[offset+112 : offset+116])))
eCtx.Syscall = int32(binary.LittleEndian.Uint32(decoder.buffer[offset+116 : offset+120]))
eCtx.MatchedPolicies = binary.LittleEndian.Uint64(decoder.buffer[offset+120 : offset+128])
eCtx.Retval = int64(binary.LittleEndian.Uint64(decoder.buffer[offset+128 : offset+136]))
eCtx.StackID = binary.LittleEndian.Uint32(decoder.buffer[offset+136 : offset+140])
eCtx.ProcessorId = binary.LittleEndian.Uint16(decoder.buffer[offset+140 : offset+142])
// 2 byte padding
eCtx.Retval = int64(binary.LittleEndian.Uint64(decoder.buffer[offset+120 : offset+128]))
eCtx.StackID = binary.LittleEndian.Uint32(decoder.buffer[offset+128 : offset+132])
eCtx.ProcessorId = binary.LittleEndian.Uint16(decoder.buffer[offset+132 : offset+134])
eCtx.PoliciesVersion = binary.LittleEndian.Uint16(decoder.buffer[offset+134 : offset+136])
eCtx.MatchedPolicies = binary.LittleEndian.Uint64(decoder.buffer[offset+136 : offset+144])
// event_context end

decoder.cursor += eCtx.GetSizeBytes()
Expand Down
41 changes: 24 additions & 17 deletions pkg/bufferdecoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,30 @@ func TestDecodeContext(t *testing.T) {

buf := new(bytes.Buffer)
eCtxExpected := EventContext{
Ts: 11,
CgroupID: 22,
ProcessorId: 5,
Pid: 543,
Tid: 77,
Ppid: 4567,
HostPid: 5430,
HostTid: 124,
HostPpid: 555,
Uid: 9876,
MntID: 1357,
PidID: 3758,
Comm: [16]byte{1, 3, 5, 3, 1, 5, 56, 6, 7, 32, 2, 4},
UtsName: [16]byte{5, 6, 7, 8, 9, 4, 3, 2},
EventID: 0,
Retval: 0,
StackID: 0,
Ts: 11,
StartTime: 0,
CgroupID: 22,
Pid: 543,
Tid: 77,
Ppid: 4567,
HostPid: 5430,
HostTid: 124,
HostPpid: 555,
Uid: 9876,
MntID: 1357,
PidID: 3758,
Comm: [16]byte{1, 3, 5, 3, 1, 5, 56, 6, 7, 32, 2, 4},
UtsName: [16]byte{5, 6, 7, 8, 9, 4, 3, 2},
Flags: 0,
LeaderStartTime: 1331,
ParentStartTime: 1221,
EventID: 5,
Syscall: 0,
Retval: 0,
StackID: 0,
ProcessorId: 5,
PoliciesVersion: 11,
MatchedPolicies: 1917,
}
err := binary.Write(buf, binary.LittleEndian, eCtxExpected)
assert.Equal(t, nil, err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/bufferdecoder/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ type EventContext struct {

EventID events.ID // int32
Syscall int32
MatchedPolicies uint64
Retval int64
StackID uint32
ProcessorId uint16
_ [2]byte // padding
PoliciesVersion uint16
MatchedPolicies uint64
}

func (EventContext) GetSizeBytes() int {
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/cobra/cobra.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ func GetTraceeRunner(c *cobra.Command, version string) (cmd.Runner, error) {
}

cfg.Policies = policies
policy.Snapshots().Store(cfg.Policies)
geyslan marked this conversation as resolved.
Show resolved Hide resolved

// Output command line flags

Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/urfave/urfave.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/aquasecurity/tracee/pkg/config"
"github.com/aquasecurity/tracee/pkg/errfmt"
"github.com/aquasecurity/tracee/pkg/logger"
"github.com/aquasecurity/tracee/pkg/policy"
)

func GetTraceeRunner(c *cli.Context, version string) (cmd.Runner, error) {
Expand Down Expand Up @@ -125,8 +126,8 @@ func GetTraceeRunner(c *cli.Context, version string) (cmd.Runner, error) {
if err != nil {
return runner, err
}

cfg.Policies = policies
policy.Snapshots().Store(cfg.Policies)

broadcast, err := printer.NewBroadcast(output.PrinterConfigs, cmd.GetContainerMode(cfg))
if err != nil {
Expand Down
12 changes: 10 additions & 2 deletions pkg/containers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ func (c *Containers) CgroupMkdir(cgroupId uint64, subPath string, hierarchyID ui
}

// FindContainerCgroupID32LSB returns the 32 LSB of the Cgroup ID for a given container ID.
func (c *Containers) FindContainerCgroupID32LSB(containerID string) []uint32 {
func (c *Containers) FindContainerCgroupID32LSB(containerID string) ([]uint32, error) {
var cgroupIDs []uint32
c.cgroupsMutex.RLock()
defer c.cgroupsMutex.RUnlock()
Expand All @@ -386,7 +386,15 @@ func (c *Containers) FindContainerCgroupID32LSB(containerID string) []uint32 {
cgroupIDs = append(cgroupIDs, k)
}
}
return cgroupIDs

if cgroupIDs == nil {
return nil, errfmt.Errorf("container id not found: %s", containerID)
}
if len(cgroupIDs) > 1 {
return cgroupIDs, errfmt.Errorf("container id is ambiguous: %s", containerID)
}

return cgroupIDs, nil
}

// GetCgroupInfo returns the contents of the Containers struct cgroupInfo data of a given cgroupId.
Expand Down
1 change: 1 addition & 0 deletions pkg/ebpf/c/common/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ statfunc int init_program_data(program_data_t *p, void *ctx)
// in some places we don't call should_trace() (e.g. sys_exit) which also initializes
// matched_policies. Use previously found scopes then to initialize it.
p->event->context.matched_policies = p->task_info->matched_scopes;
p->event->context.policies_version = p->config->policies_version;

// check if we need to recompute scope due to context change
if (context_changed(&p->task_info->context, &p->event->context.task))
Expand Down
134 changes: 90 additions & 44 deletions pkg/ebpf/c/common/filtering.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

// PROTOTYPES

statfunc void *get_filter_map(void *, u16);
statfunc u64 uint_filter_range_matches(u64, void *, u64, u64, u64);
statfunc u64 binary_filter_matches(u64, proc_info_t *);
statfunc u64 binary_filter_matches(u64, void *, proc_info_t *);
statfunc u64 equality_filter_matches(u64, void *, void *);
statfunc u64 bool_filter_matches(u64, bool val);
statfunc u64 compute_scopes(program_data_t *);
Expand Down Expand Up @@ -50,17 +51,26 @@ statfunc u64 should_submit(u32, event_data_t *);

// FUNCTIONS

// get_filter_map returns the filter map for the given version and outer map
statfunc void *get_filter_map(void *outer_map, u16 pols_version)
{
return bpf_map_lookup_elem(outer_map, &pols_version);
}

statfunc u64
uint_filter_range_matches(u64 filter_out_scopes, void *filter_map, u64 value, u64 max, u64 min)
{
// check equality_filter_matches() for more info

u64 equal_in_scopes = 0;
u64 equality_set_in_scopes = 0;
eq_t *equality = bpf_map_lookup_elem(filter_map, &value);
if (equality != NULL) {
equal_in_scopes = equality->equal_in_scopes;
equality_set_in_scopes = equality->equality_set_in_scopes;

if (filter_map) {
eq_t *equality = bpf_map_lookup_elem(filter_map, &value);
if (equality != NULL) {
equal_in_scopes = equality->equal_in_scopes;
equality_set_in_scopes = equality->equality_set_in_scopes;
}
}

if ((max != FILTER_MAX_NOT_SET) && (value >= max))
Expand All @@ -72,20 +82,23 @@ uint_filter_range_matches(u64 filter_out_scopes, void *filter_map, u64 value, u6
return equal_in_scopes | (filter_out_scopes & ~equality_set_in_scopes);
}

statfunc u64 binary_filter_matches(u64 filter_out_scopes, proc_info_t *proc_info)
statfunc u64 binary_filter_matches(u64 filter_out_scopes, void *filter_map, proc_info_t *proc_info)
{
// check equality_filter_matches() for more info

u64 equal_in_scopes = 0;
u64 equality_set_in_scopes = 0;
eq_t *equality = bpf_map_lookup_elem(&binary_filter, proc_info->binary.path);
if (equality == NULL) {
// lookup by binary path and mount namespace
equality = bpf_map_lookup_elem(&binary_filter, &proc_info->binary);
}
if (equality != NULL) {
equal_in_scopes = equality->equal_in_scopes;
equality_set_in_scopes = equality->equality_set_in_scopes;

if (filter_map) {
eq_t *equality = bpf_map_lookup_elem(filter_map, proc_info->binary.path);
if (equality == NULL) {
// lookup by binary path and mount namespace
equality = bpf_map_lookup_elem(filter_map, &proc_info->binary);
}
if (equality != NULL) {
equal_in_scopes = equality->equal_in_scopes;
equality_set_in_scopes = equality->equality_set_in_scopes;
}
}

return equal_in_scopes | (filter_out_scopes & ~equality_set_in_scopes);
Expand Down Expand Up @@ -135,10 +148,13 @@ statfunc u64 equality_filter_matches(u64 filter_out_scopes, void *filter_map, vo

u64 equal_in_scopes = 0;
u64 equality_set_in_scopes = 0;
eq_t *equality = bpf_map_lookup_elem(filter_map, key);
if (equality != NULL) {
equal_in_scopes = equality->equal_in_scopes;
equality_set_in_scopes = equality->equality_set_in_scopes;

if (filter_map) {
eq_t *equality = bpf_map_lookup_elem(filter_map, key);
if (equality != NULL) {
equal_in_scopes = equality->equal_in_scopes;
equality_set_in_scopes = equality->equality_set_in_scopes;
}
}

return equal_in_scopes | (filter_out_scopes & ~equality_set_in_scopes);
Expand Down Expand Up @@ -189,6 +205,11 @@ statfunc u64 compute_scopes(program_data_t *p)
return 0;
}

//
// unversioned filters
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are bool filters unversioned?

Copy link
Member Author

Choose a reason for hiding this comment

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

Odd, I need to recheck my local branches.

Copy link
Member Author

Choose a reason for hiding this comment

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

We've remembered why bool filters are not versioned: they are not stored in specific maps, they are part of the config.

Copy link
Collaborator

@yanivagman yanivagman Jan 14, 2024

Choose a reason for hiding this comment

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

If I understand correctly, we should add boolean filters versioning in the future by adding a dedicated map for each one of them. If we won't do it, there might by a mismatch between the policies version used by the versioned filters to those that are not versioned. Is that right?
If so, it is ok that we postpone this for a future PR, but we should add a comment explaining this (and an issue) to track this required change

Copy link
Member Author

Choose a reason for hiding this comment

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

I already added it as a task in the EPIC. Gonna create the issue and put the comment.

geyslan marked this conversation as resolved.
Show resolved Hide resolved
//

// TODO: Create a filter map for each boolean filter (versioning it) #3805
if (p->config->cont_filter_enabled_scopes) {
bool is_container = false;
u8 state = p->task_info->container_state;
Expand All @@ -209,80 +230,100 @@ statfunc u64 compute_scopes(program_data_t *p)
res &= bool_filter_matches(filter_out_scopes, is_new_container) | mask;
}

if (p->config->new_pid_filter_enabled_scopes) {
u64 filter_out_scopes = p->config->new_pid_filter_out_scopes;
u64 mask = ~p->config->new_pid_filter_enabled_scopes;
res &= bool_filter_matches(filter_out_scopes, proc_info->new_proc) | mask;
}

//
// versioned filters
//

u16 pols_version = p->event->context.policies_version;
void *filter_map = NULL;

if (p->config->pid_filter_enabled_scopes) {
filter_map = get_filter_map(&pid_filter_version, pols_version);
u64 filter_out_scopes = p->config->pid_filter_out_scopes;
u64 mask = ~p->config->pid_filter_enabled_scopes;
u64 max = p->config->pid_max;
u64 min = p->config->pid_min;

// the user might have given us a tid - check for it too
res &=
uint_filter_range_matches(filter_out_scopes, &pid_filter, context->host_pid, max, min) |
uint_filter_range_matches(filter_out_scopes, &pid_filter, context->host_tid, max, min) |
uint_filter_range_matches(filter_out_scopes, filter_map, context->host_pid, max, min) |
uint_filter_range_matches(filter_out_scopes, filter_map, context->host_tid, max, min) |
mask;
}

if (p->config->new_pid_filter_enabled_scopes) {
u64 filter_out_scopes = p->config->new_pid_filter_out_scopes;
u64 mask = ~p->config->new_pid_filter_enabled_scopes;
res &= bool_filter_matches(filter_out_scopes, proc_info->new_proc) | mask;
}

if (p->config->uid_filter_enabled_scopes) {
filter_map = get_filter_map(&uid_filter_version, pols_version);
u64 filter_out_scopes = p->config->uid_filter_out_scopes;
u64 mask = ~p->config->uid_filter_enabled_scopes;
u64 max = p->config->uid_max;
u64 min = p->config->uid_min;
res &= uint_filter_range_matches(filter_out_scopes, &uid_filter, context->uid, max, min) |
mask;
res &=
uint_filter_range_matches(filter_out_scopes, filter_map, context->uid, max, min) | mask;
}

if (p->config->mnt_ns_filter_enabled_scopes) {
filter_map = get_filter_map(&mnt_ns_filter_version, pols_version);
u64 filter_out_scopes = p->config->mnt_ns_filter_out_scopes;
u64 mask = ~p->config->mnt_ns_filter_enabled_scopes;
u64 mnt_id = context->mnt_id;
res &= equality_filter_matches(filter_out_scopes, &mnt_ns_filter, &mnt_id) | mask;
res &= equality_filter_matches(filter_out_scopes, filter_map, &mnt_id) | mask;
}

if (p->config->pid_ns_filter_enabled_scopes) {
filter_map = get_filter_map(&pid_ns_filter_version, pols_version);
u64 filter_out_scopes = p->config->pid_ns_filter_out_scopes;
u64 mask = ~p->config->pid_ns_filter_enabled_scopes;
u64 pid_id = context->pid_id;
res &= equality_filter_matches(filter_out_scopes, &pid_ns_filter, &pid_id) | mask;
res &= equality_filter_matches(filter_out_scopes, filter_map, &pid_id) | mask;
}

if (p->config->uts_ns_filter_enabled_scopes) {
filter_map = get_filter_map(&uts_ns_filter_version, pols_version);
u64 filter_out_scopes = p->config->uts_ns_filter_out_scopes;
u64 mask = ~p->config->uts_ns_filter_enabled_scopes;
res &=
equality_filter_matches(filter_out_scopes, &uts_ns_filter, &context->uts_name) | mask;
res &= equality_filter_matches(filter_out_scopes, filter_map, &context->uts_name) | mask;
}

if (p->config->comm_filter_enabled_scopes) {
filter_map = get_filter_map(&comm_filter_version, pols_version);
u64 filter_out_scopes = p->config->comm_filter_out_scopes;
u64 mask = ~p->config->comm_filter_enabled_scopes;
res &= equality_filter_matches(filter_out_scopes, &comm_filter, &context->comm) | mask;
}

if (p->config->proc_tree_filter_enabled_scopes) {
u64 filter_out_scopes = p->config->proc_tree_filter_out_scopes;
u64 mask = ~p->config->proc_tree_filter_enabled_scopes;
res &= equality_filter_matches(filter_out_scopes, &process_tree_map, &context->host_pid) |
mask;
res &= equality_filter_matches(filter_out_scopes, filter_map, &context->comm) | mask;
}

if (p->config->cgroup_id_filter_enabled_scopes) {
filter_map = get_filter_map(&cgroup_id_filter_version, pols_version);
u64 filter_out_scopes = p->config->cgroup_id_filter_out_scopes;
u64 mask = ~p->config->cgroup_id_filter_enabled_scopes;
u64 cgroup_id_lsb = context->cgroup_id;
res &= equality_filter_matches(filter_out_scopes, &cgroup_id_filter, &cgroup_id_lsb) | mask;
u32 cgroup_id_lsb = context->cgroup_id;
res &= equality_filter_matches(filter_out_scopes, filter_map, &cgroup_id_lsb) | mask;
}

if (p->config->proc_tree_filter_enabled_scopes) {
filter_map = get_filter_map(&process_tree_map_version, pols_version);
u64 filter_out_scopes = p->config->proc_tree_filter_out_scopes;
u64 mask = ~p->config->proc_tree_filter_enabled_scopes;
u32 host_pid = context->host_pid;
res &= equality_filter_matches(filter_out_scopes, filter_map, &host_pid) | mask;
}

if (p->config->bin_path_filter_enabled_scopes) {
filter_map = get_filter_map(&binary_filter_version, pols_version);
u64 filter_out_scopes = p->config->bin_path_filter_out_scopes;
u64 mask = ~p->config->bin_path_filter_enabled_scopes;
res &= binary_filter_matches(filter_out_scopes, proc_info) | mask;
res &= binary_filter_matches(filter_out_scopes, filter_map, proc_info) | mask;
}

//
// follow filter
//

if (p->config->follow_filter_enabled_scopes) {
// trace this proc anyway if follow was set by a scope
res |= proc_info->follow_in_scopes & p->config->follow_filter_enabled_scopes;
Expand All @@ -307,7 +348,12 @@ statfunc u64 should_trace(program_data_t *p)

statfunc u64 should_submit(u32 event_id, event_data_t *event)
{
event_config_t *event_config = bpf_map_lookup_elem(&events_map, &event_id);
u16 pols_version = event->context.policies_version;
void *inner_events_map = bpf_map_lookup_elem(&events_map_version, &pols_version);
if (inner_events_map == NULL)
return 0;

event_config_t *event_config = bpf_map_lookup_elem(inner_events_map, &event_id);
// if event config not set, don't submit
if (event_config == NULL)
return 0;
Expand Down
4 changes: 3 additions & 1 deletion pkg/ebpf/c/common/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,10 @@ cgrpctxmap_t cgrpctxmap_eg SEC(".maps"); // saved info SKB caller <=> SKB egr
typedef struct net_task_context {
struct task_struct *task;
task_context_t taskctx;
s32 syscall;
Copy link
Collaborator

Choose a reason for hiding this comment

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

any reason for using s32 and not u32?

Copy link
Member Author

Choose a reason for hiding this comment

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

IIRC due to respective userland EventContext field:

u16 padding;
u16 policies_version;
u64 matched_policies;
int syscall;
} net_task_context_t;

struct {
Expand Down
Loading
Loading