Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
geyslan committed Jul 17, 2023
1 parent d8fa964 commit e3f65d3
Show file tree
Hide file tree
Showing 34 changed files with 578 additions and 83 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,5 @@ require (
)

replace github.com/kubernetes/cri-api => k8s.io/cri-api v0.23.5-rc.0

replace github.com/aquasecurity/tracee/types => ./types
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ github.com/aquasecurity/libbpfgo v0.4.8-libbpf-1.2.0.0.20230509162948-80f41e18e6
github.com/aquasecurity/libbpfgo v0.4.8-libbpf-1.2.0.0.20230509162948-80f41e18e690/go.mod h1:UD3Mfr+JZ/ASK2VMucI/zAdEhb35LtvYXvAUdrdqE9s=
github.com/aquasecurity/libbpfgo/helpers v0.4.6-0.20230321190037-f591a2c5734f h1:l127H3NqJBmw+XMt+haBOeZIrBppuw7TJz26cWMI9kY=
github.com/aquasecurity/libbpfgo/helpers v0.4.6-0.20230321190037-f591a2c5734f/go.mod h1:j/TQLmsZpOIdF3CnJODzYngG4yu1YoDCoRMELxkQSSA=
github.com/aquasecurity/tracee/types v0.0.0-20230602152109-e48d0a548fbf h1:bSWqjqjFPGyn+thqof/rph4A5jSqd2d7xWJK5MGMb0I=
github.com/aquasecurity/tracee/types v0.0.0-20230602152109-e48d0a548fbf/go.mod h1:kHvgUMXGq5QEqSLPgu4RwGSJEoCuMQJnEkGk8OAcSUc=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
Expand Down
12 changes: 7 additions & 5 deletions pkg/bufferdecoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,13 @@ func (decoder *EbpfDecoder) DecodeContext(ctx *Context) error {

ctx.EventID = events.ID(int32(binary.LittleEndian.Uint32(decoder.buffer[offset+96 : offset+100])))
ctx.Syscall = int32(binary.LittleEndian.Uint32(decoder.buffer[offset+100 : offset+104]))
ctx.MatchedPolicies = binary.LittleEndian.Uint64(decoder.buffer[offset+104 : offset+112])
ctx.Retval = int64(binary.LittleEndian.Uint64(decoder.buffer[offset+112 : offset+120]))
ctx.StackID = binary.LittleEndian.Uint32(decoder.buffer[offset+120 : offset+124])
ctx.ProcessorId = binary.LittleEndian.Uint16(decoder.buffer[offset+124 : offset+126])
// 2 byte padding
ctx.PoliciesVersion = binary.LittleEndian.Uint32(decoder.buffer[offset+104 : offset+108])
_ = decoder.buffer[offset+108 : offset+112] // 4 bytes padding
ctx.MatchedPolicies = binary.LittleEndian.Uint64(decoder.buffer[offset+112 : offset+120])
ctx.Retval = int64(binary.LittleEndian.Uint64(decoder.buffer[offset+120 : offset+128]))
ctx.StackID = binary.LittleEndian.Uint32(decoder.buffer[offset+128 : offset+132])
ctx.ProcessorId = binary.LittleEndian.Uint16(decoder.buffer[offset+132 : offset+134])
_ = decoder.buffer[offset+134 : offset+136] // 2 bytes padding
// event_context end

decoder.cursor += ctx.GetSizeBytes()
Expand Down
35 changes: 18 additions & 17 deletions pkg/bufferdecoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,24 @@ import (
func TestDecodeContext(t *testing.T) {
buf := new(bytes.Buffer)
ctxExpected := Context{
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,
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,
PoliciesVersion: 1,
Retval: 0,
StackID: 0,
}
err := binary.Write(buf, binary.LittleEndian, ctxExpected)
assert.Equal(t, nil, err)
Expand Down
4 changes: 3 additions & 1 deletion pkg/bufferdecoder/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type Context struct {
Flags uint32
EventID events.ID // int32
Syscall int32
PoliciesVersion uint32
_ [4]byte // padding
MatchedPolicies uint64
Retval int64
StackID uint32
Expand All @@ -48,7 +50,7 @@ type Context struct {
}

func (Context) GetSizeBytes() int {
return 128
return 136
}

type ChunkMeta struct {
Expand Down
3 changes: 3 additions & 0 deletions pkg/cmd/cobra/cobra.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ func GetTraceeRunner(c *cobra.Command, version string) (cmd.Runner, error) {
}

cfg.Policies = policies
if err := cfg.Policies.StoreSnapshot(); err != nil {
return runner, err
}

// Output command line flags
output, err := flags.PrepareOutput(viper.GetStringSlice("output"), true)
Expand Down
4 changes: 3 additions & 1 deletion pkg/cmd/urfave/urfave.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,10 @@ func GetTraceeRunner(c *cli.Context, version string) (cmd.Runner, error) {
if err != nil {
return runner, err
}

cfg.Policies = policies
if err := cfg.Policies.StoreSnapshot(); err != nil {
return runner, err
}

broadcast, err := printer.NewBroadcast(output.PrinterConfigs, cmd.GetContainerMode(cfg))
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions pkg/ebpf/c/common/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,12 @@ statfunc int events_perf_submit(program_data_t *p, u32 id, long ret)
u32 size = sizeof(event_context_t) + sizeof(u8) +
p->event->args_buf.offset; // context + argnum + arg buffer size

// Check, at the last moment, if the policies version changed during the event processing.
// If so, skip the event submission as it is considered stale.
if (unlikely(p->config->policies_version != p->event->context.policies_version)) {
return -1;
}

// inline bounds check to force compiler to use the register of size
asm volatile("if %[size] < %[max_size] goto +1;\n"
"%[size] = %[max_size];\n"
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 @@ -153,6 +153,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
2 changes: 2 additions & 0 deletions pkg/ebpf/c/common/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ 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;
u32 policies_version;
u32 padding;
u64 matched_policies;
int syscall;
} net_task_context_t;
Expand Down
3 changes: 3 additions & 0 deletions pkg/ebpf/c/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ enum tail_call_id_e
BPF_HASH(kconfig_map, u32, u32, 10240); // kernel config variables
BPF_HASH(containers_map, u32, u8, 10240); // map cgroup id to container status {EXISTED, CREATED, STARTED}
BPF_HASH(args_map, u64, args_t, 1024); // persist args between function entry and return

// policies filters
BPF_HASH(uid_filter, u32, eq_t, 256); // filter events by UID, for specific UIDs either by == or !=
BPF_HASH(pid_filter, u32, eq_t, 256); // filter events by PID
BPF_HASH(mnt_ns_filter, u64, eq_t, 256); // filter events by mount namespace id
Expand All @@ -86,6 +88,7 @@ BPF_HASH(uts_ns_filter, string_filter_t, eq_t, 256); // filter eve
BPF_HASH(comm_filter, string_filter_t, eq_t, 256); // filter events by command name
BPF_HASH(cgroup_id_filter, u32, eq_t, 256); // filter events by cgroup id
BPF_HASH(binary_filter, binary_t, eq_t, 256); // filter events by binary path and mount namespace

BPF_HASH(events_map, u32, event_config_t, MAX_EVENT_ID); // map to persist event configuration data
BPF_HASH(sys_32_to_64_map, u32, u32, 1024); // map 32bit to 64bit syscalls
BPF_HASH(process_tree_map, u32, eq_t, 10240); // filter events by the ancestry of the traced process
Expand Down
12 changes: 12 additions & 0 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ int uprobe_lkm_seeker_submitter(struct pt_regs *ctx)
// Uprobes are not triggered by syscalls, so we need to override the false value.
p.event->context.syscall = NO_SYSCALL;
p.event->context.matched_policies = ULLONG_MAX;
p.event->context.policies_version = p.config->policies_version;

u32 trigger_pid = bpf_get_current_pid_tgid() >> 32;
// Uprobe was triggered from other tracee instance
Expand Down Expand Up @@ -958,6 +959,7 @@ int uprobe_lkm_seeker(struct pt_regs *ctx)
// Uprobes are not triggered by syscalls, so we need to override the false value.
p.event->context.syscall = NO_SYSCALL;
p.event->context.matched_policies = ULLONG_MAX;
p.event->context.policies_version = p.config->policies_version;

// uprobe was triggered from other tracee instance
if (p.config->tracee_pid != p.task_info->context.pid &&
Expand Down Expand Up @@ -1436,6 +1438,7 @@ int uprobe_syscall_trigger(struct pt_regs *ctx)
// Uprobes are not triggered by syscalls, so we need to override the false value.
p.event->context.syscall = NO_SYSCALL;
p.event->context.matched_policies = ULLONG_MAX;
p.event->context.policies_version = p.config->policies_version;

char syscall_table_sym[15] = "sys_call_table";
u64 *syscall_table_addr = (u64 *) get_symbol_addr(syscall_table_sym);
Expand Down Expand Up @@ -1488,6 +1491,7 @@ int uprobe_seq_ops_trigger(struct pt_regs *ctx)
// Uprobes are not triggered by syscalls, so we need to override the false value.
p.event->context.syscall = NO_SYSCALL;
p.event->context.matched_policies = ULLONG_MAX;
p.event->context.policies_version = p.config->policies_version;

// uprobe was triggered from other tracee instance
if (p.config->tracee_pid != p.task_info->context.pid &&
Expand Down Expand Up @@ -1569,6 +1573,7 @@ int uprobe_mem_dump_trigger(struct pt_regs *ctx)
// Uprobes are not triggered by syscalls, so we need to override the false value.
p.event->context.syscall = NO_SYSCALL;
p.event->context.matched_policies = ULLONG_MAX;
p.event->context.policies_version = p.config->policies_version;

// uprobe was triggered from other tracee instance
if (p.config->tracee_pid != p.task_info->context.pid &&
Expand Down Expand Up @@ -4840,6 +4845,7 @@ statfunc void set_net_task_context(event_data_t *event, net_task_context_t *netc
{
netctx->task = event->task;
netctx->matched_policies = event->context.matched_policies;
netctx->policies_version = event->context.policies_version;
netctx->syscall = event->context.syscall;
__builtin_memset(&netctx->taskctx, 0, sizeof(task_context_t));
__builtin_memcpy(&netctx->taskctx, &event->context.task, sizeof(task_context_t));
Expand Down Expand Up @@ -4964,6 +4970,11 @@ statfunc u32 cgroup_skb_submit(void *map,
// set the event type before submitting event
neteventctx->eventctx.eventid = event_type;

// This should be the point to check if the policies version changed, and if so,
// skip the submission of the event. But as the network events processing is
// different from the rest of the events, there's no program_data_t to check
// the policies version.

return bpf_perf_event_output(ctx,
map,
flags,
Expand Down Expand Up @@ -5333,6 +5344,7 @@ int BPF_KPROBE(cgroup_bpf_run_filter_skb)
eventctx->stack_id = 0; // no stack trace
eventctx->processor_id = p.event->context.processor_id; // copy from current ctx
eventctx->matched_policies = netctx->matched_policies; // pick matched_policies from net ctx
eventctx->policies_version = netctx->policies_version; // pick policies_version from net ctx
eventctx->syscall = NO_SYSCALL; // ingress has no orig syscall
if (type == BPF_CGROUP_INET_EGRESS)
eventctx->syscall = netctx->syscall; // egress does have an orig syscall
Expand Down
5 changes: 4 additions & 1 deletion pkg/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ typedef struct event_context {
task_context_t task;
u32 eventid;
s32 syscall; // The syscall which triggered the event
s32 policies_version;
u32 padding1;
u64 matched_policies;
s64 retval;
u32 stack_id;
u16 processor_id; // The ID of the processor which processed the event
u16 padding2;
} event_context_t;

enum event_id_e
Expand Down Expand Up @@ -288,7 +291,7 @@ typedef struct config_entry {
u32 tracee_pid;
u32 options;
u32 cgroup_v1_hid;
u32 padding; // free for further use
u32 policies_version;
// enabled scopes bitmask per filter
u64 uid_filter_enabled_scopes;
u64 pid_filter_enabled_scopes;
Expand Down
29 changes: 25 additions & 4 deletions pkg/ebpf/events_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/aquasecurity/tracee/pkg/errfmt"
"github.com/aquasecurity/tracee/pkg/events"
"github.com/aquasecurity/tracee/pkg/logger"
"github.com/aquasecurity/tracee/pkg/policy"
"github.com/aquasecurity/tracee/pkg/utils"
"github.com/aquasecurity/tracee/types/trace"
)
Expand Down Expand Up @@ -241,6 +242,7 @@ func (t *Tracee) decodeEvents(outerCtx context.Context, sourceChan chan []byte)
evt.Kubernetes = kubernetesData
evt.EventID = int(ctx.EventID)
evt.EventName = eventDefinition.Name
evt.PoliciesVersion = ctx.PoliciesVersion
evt.MatchedPoliciesKernel = ctx.MatchedPolicies
evt.MatchedPoliciesUser = 0
evt.MatchedPolicies = []string{}
Expand Down Expand Up @@ -286,13 +288,19 @@ func (t *Tracee) matchPolicies(event *trace.Event) uint64 {
eventID := events.ID(event.EventID)
bitmap := event.MatchedPoliciesKernel

policies, err := policy.GetSnapshot(event.PoliciesVersion)
if err != nil {
t.handleError(err)
return 0
}

// Short circuit if there are no policies in userland that need filtering.
if bitmap&t.config.Policies.FilterableInUserland() == 0 {
if bitmap&policies.FilterableInUserland() == 0 {
event.MatchedPoliciesUser = bitmap // store untoched bitmap to be used in sink stage
return bitmap
}

for p := range t.config.Policies.FilterableInUserlandMap() { // range through each userland filterable policy
for p := range policies.FilterableInUserlandMap() { // range through each userland filterable policy
// Policy ID is the bit offset in the bitmap.
bitOffset := uint(p.ID)

Expand Down Expand Up @@ -443,8 +451,14 @@ func (t *Tracee) processEvents(ctx context.Context, in <-chan *trace.Event) (
continue
}

policies, err := policy.GetSnapshot(event.PoliciesVersion)
if err != nil {
t.handleError(err)
continue
}

// Get a bitmap with all policies containing container filters
policiesWithContainerFilter := t.config.Policies.ContainerFilterEnabled()
policiesWithContainerFilter := policies.ContainerFilterEnabled()

// Filter out events that don't have a container ID from all the policies that have
// container filters. This will guarantee that any of those policies won't get matched
Expand Down Expand Up @@ -521,6 +535,8 @@ func (t *Tracee) deriveEvents(ctx context.Context, in <-chan *trace.Event) (
}

for i := range derivatives {
derivatives[i].PoliciesVersion = event.PoliciesVersion

// Skip events that dont work with filtering due to missing types being handled.
// https://github.com/aquasecurity/tracee/issues/2486
switch events.ID(derivatives[i].EventID) {
Expand Down Expand Up @@ -568,8 +584,13 @@ func (t *Tracee) sinkEvents(ctx context.Context, in <-chan *trace.Event) <-chan
continue
}

policies, err := policy.GetSnapshot(event.PoliciesVersion)
if err != nil {
t.handleError(err)
continue
}
// Populate the event with the names of the matched policies.
event.MatchedPolicies = t.config.Policies.MatchedNames(event.MatchedPoliciesUser)
event.MatchedPolicies = policies.MatchedNames(event.MatchedPoliciesUser)

// Parse args here if the rule engine is not enabled (parsed there if it is).
if !t.config.EngineConfig.Enabled {
Expand Down
1 change: 1 addition & 0 deletions pkg/ebpf/finding.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func newEvent(id int, f detect.Finding, s trace.Event) *trace.Event {
ReturnValue: s.ReturnValue,
StackAddresses: s.StackAddresses,
ContextFlags: s.ContextFlags,
PoliciesVersion: s.PoliciesVersion,
MatchedPoliciesKernel: s.MatchedPoliciesKernel,
MatchedPoliciesUser: s.MatchedPoliciesUser,
ArgsNum: len(arguments),
Expand Down
Loading

0 comments on commit e3f65d3

Please sign in to comment.