From abb0a1eb109d233db643232757a5bf36c4fbee56 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 23 Jul 2024 11:33:58 +0200 Subject: [PATCH 01/11] crd: add followChildren in matchBinaries Signed-off-by: Kornilios Kourtis --- pkg/k8s/apis/cilium.io/v1alpha1/types.go | 2 ++ pkg/k8s/apis/cilium.io/v1alpha1/version.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/k8s/apis/cilium.io/v1alpha1/types.go b/pkg/k8s/apis/cilium.io/v1alpha1/types.go index 9d9c7744281..c3342366aa2 100644 --- a/pkg/k8s/apis/cilium.io/v1alpha1/types.go +++ b/pkg/k8s/apis/cilium.io/v1alpha1/types.go @@ -95,6 +95,8 @@ type BinarySelector struct { Operator string `json:"operator"` // Value to compare the argument against. Values []string `json:"values"` + // In addition to binaries, match children processes of specified binaries. + FollowChildren bool `json:"followChildren"` } // KProbeSelector selects function calls for kprobe based on PIDs and function arguments. The diff --git a/pkg/k8s/apis/cilium.io/v1alpha1/version.go b/pkg/k8s/apis/cilium.io/v1alpha1/version.go index 203377ec2ee..e6e5eb689dd 100644 --- a/pkg/k8s/apis/cilium.io/v1alpha1/version.go +++ b/pkg/k8s/apis/cilium.io/v1alpha1/version.go @@ -7,4 +7,4 @@ package v1alpha1 // Used to determine if CRD needs to be updated in cluster // // Developers: Bump patch for each change in the CRD schema. -const CustomResourceDefinitionSchemaVersion = "1.2.1" +const CustomResourceDefinitionSchemaVersion = "1.2.2" From 962e158bb02a20811128a5bd646636e12201a04b Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 23 Jul 2024 11:36:24 +0200 Subject: [PATCH 02/11] autochore: make crds && make -C install/kubernetes Signed-off-by: Kornilios Kourtis --- .../crds-yaml/cilium.io_tracingpolicies.yaml | 20 +++++++++++++++++++ .../cilium.io_tracingpoliciesnamespaced.yaml | 20 +++++++++++++++++++ .../v1alpha1/cilium.io_tracingpolicies.yaml | 20 +++++++++++++++++++ .../cilium.io_tracingpoliciesnamespaced.yaml | 20 +++++++++++++++++++ .../v1alpha1/cilium.io_tracingpolicies.yaml | 20 +++++++++++++++++++ .../cilium.io_tracingpoliciesnamespaced.yaml | 20 +++++++++++++++++++ .../pkg/k8s/apis/cilium.io/v1alpha1/types.go | 2 ++ .../k8s/apis/cilium.io/v1alpha1/version.go | 2 +- 8 files changed, 123 insertions(+), 1 deletion(-) diff --git a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml index d3b14464c16..a929acd9d0f 100644 --- a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml +++ b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpolicies.yaml @@ -444,6 +444,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -458,6 +462,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1048,6 +1053,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1062,6 +1071,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1683,6 +1693,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1697,6 +1711,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -2255,6 +2270,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -2269,6 +2288,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object diff --git a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml index 595c2db0235..d30412be600 100644 --- a/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml +++ b/install/kubernetes/tetragon/crds-yaml/cilium.io_tracingpoliciesnamespaced.yaml @@ -444,6 +444,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -458,6 +462,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1048,6 +1053,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1062,6 +1071,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1683,6 +1693,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1697,6 +1711,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -2255,6 +2270,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -2269,6 +2288,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object diff --git a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml index d3b14464c16..a929acd9d0f 100644 --- a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml +++ b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml @@ -444,6 +444,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -458,6 +462,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1048,6 +1053,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1062,6 +1071,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1683,6 +1693,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1697,6 +1711,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -2255,6 +2270,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -2269,6 +2288,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object diff --git a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml index 595c2db0235..d30412be600 100644 --- a/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml +++ b/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml @@ -444,6 +444,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -458,6 +462,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1048,6 +1053,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1062,6 +1071,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1683,6 +1693,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1697,6 +1711,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -2255,6 +2270,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -2269,6 +2288,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml index d3b14464c16..a929acd9d0f 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpolicies.yaml @@ -444,6 +444,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -458,6 +462,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1048,6 +1053,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1062,6 +1071,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1683,6 +1693,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1697,6 +1711,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -2255,6 +2270,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -2269,6 +2288,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml index 595c2db0235..d30412be600 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/client/crds/v1alpha1/cilium.io_tracingpoliciesnamespaced.yaml @@ -444,6 +444,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -458,6 +462,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1048,6 +1053,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1062,6 +1071,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -1683,6 +1693,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -1697,6 +1711,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object @@ -2255,6 +2270,10 @@ spec: description: A list of binary exec name filters. items: properties: + followChildren: + description: In addition to binaries, match children + processes of specified binaries. + type: boolean operator: description: Filter operation. enum: @@ -2269,6 +2288,7 @@ spec: type: string type: array required: + - followChildren - operator - values type: object diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go index 9d9c7744281..c3342366aa2 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/types.go @@ -95,6 +95,8 @@ type BinarySelector struct { Operator string `json:"operator"` // Value to compare the argument against. Values []string `json:"values"` + // In addition to binaries, match children processes of specified binaries. + FollowChildren bool `json:"followChildren"` } // KProbeSelector selects function calls for kprobe based on PIDs and function arguments. The diff --git a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go index 203377ec2ee..e6e5eb689dd 100644 --- a/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go +++ b/vendor/github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1/version.go @@ -7,4 +7,4 @@ package v1alpha1 // Used to determine if CRD needs to be updated in cluster // // Developers: Bump patch for each change in the CRD schema. -const CustomResourceDefinitionSchemaVersion = "1.2.1" +const CustomResourceDefinitionSchemaVersion = "1.2.2" From f37a6181e8f70a614ad3eca9cfdb074eaaf5d8b0 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 23 Jul 2024 19:47:49 +0200 Subject: [PATCH 03/11] bpf: move MATCH_BINARIES_PATH_MAX_LENGTH Move MATCH_BINARIES_PATH_MAX_LENGTH to bpf_process_event.h so that it can be used in the exec hook in subsequent patches. Signed-off-by: Kornilios Kourtis --- bpf/process/bpf_process_event.h | 3 ++- bpf/process/types/basic.h | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bpf/process/bpf_process_event.h b/bpf/process/bpf_process_event.h index 501137c646d..6695e63e428 100644 --- a/bpf/process/bpf_process_event.h +++ b/bpf/process/bpf_process_event.h @@ -11,7 +11,8 @@ #define ENAMETOOLONG 36 /* File name too long */ -#define MAX_BUF_LEN 256 +#define MATCH_BINARIES_PATH_MAX_LENGTH 256 +#define MAX_BUF_LEN 256 struct buffer_heap_map_value { // Buffer is twice the needed size because of the verifier. In prepend_name diff --git a/bpf/process/types/basic.h b/bpf/process/types/basic.h index 74a0d44c849..a6343ebed90 100644 --- a/bpf/process/types/basic.h +++ b/bpf/process/types/basic.h @@ -1530,8 +1530,6 @@ struct { __type(value, struct match_binaries_sel_opts); } tg_mb_sel_opts SEC(".maps"); -#define MATCH_BINARIES_PATH_MAX_LENGTH 256 - struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __uint(max_entries, MAX_SELECTORS); // only one matchBinaries per selector From fd0720c9ee168fb20c0098916c3848bb0ec0b289 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 23 Jul 2024 20:24:12 +0200 Subject: [PATCH 04/11] mbset: introduce the tg_mbset_map The goal is to implement a 'followChildren: true' attribute in the MatchBinaries operator that can be used to match not only processes with the specified binary name, but also their children. To do so, we need to mark processes at exec time. This patch introduces tha bpf map that will be used for the marking. The map has paths as keys and a bitset of ids as a value. The map is written by user-space and read in BPF. Each id corresponds to a MatchBinaries section with 'followChildren: true'. The mbset package implements the user-space side of the map. For example, consider the following sections: - matchBinaries: - operator: "In" values: - "/usr/bin/bash" - "/usr/bin/dash" followChildren: true ID: 0 - matchBinaries: - operator: "In" values: - "/usr/bin/bash" - "/usr/bin/csh" followChildren: true ID: 5 The map then will look like: /usr/bin/bash -> (1<<0) | (1<<5) = 0x21 /usr/bin/dash -> (1<<0) = 0x1 /usr/bin/csh -> (1<<5) = 0x20 Signed-off-by: Kornilios Kourtis --- bpf/process/bpf_execve_event.c | 13 ++++ pkg/mbset/mbset.go | 130 +++++++++++++++++++++++++++++++++ pkg/sensors/base/base.go | 4 + 3 files changed, 147 insertions(+) create mode 100644 pkg/mbset/mbset.go diff --git a/bpf/process/bpf_execve_event.c b/bpf/process/bpf_execve_event.c index a2484ff0221..3cef94c4a8b 100644 --- a/bpf/process/bpf_execve_event.c +++ b/bpf/process/bpf_execve_event.c @@ -31,6 +31,19 @@ struct { __type(value, struct msg_data); } data_heap SEC(".maps"); +typedef __u64 mbset_t; + +/* tg_mbset_map holds a mapping from (binary) paths to a bitset of ids that it matches. The map is + * written by user-space and read in the exec hook to determine the bitset of ids of a binary that + * is executed. + */ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u8[MATCH_BINARIES_PATH_MAX_LENGTH]); + __type(value, mbset_t); +} tg_mbset_map SEC(".maps"); + FUNC_INLINE __u32 read_args(void *ctx, struct msg_execve_event *event) { diff --git a/pkg/mbset/mbset.go b/pkg/mbset/mbset.go new file mode 100644 index 00000000000..a5b9eca8f81 --- /dev/null +++ b/pkg/mbset/mbset.go @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package mbset + +import ( + "errors" + "fmt" + "path/filepath" + "sync" + + "github.com/cilium/ebpf" + "github.com/cilium/tetragon/pkg/api/processapi" + "github.com/cilium/tetragon/pkg/bpf" +) + +const ( + MapName = "tg_mbset_map" + InvalidID = ^uint32(0) + MaxIDs = 64 // this value should correspond to the number of bits we can fit in mbset_t +) + +type bitSet = uint64 + +func openMap() (*ebpf.Map, error) { + fname := filepath.Join(bpf.MapPrefixPath(), MapName) + ret, err := ebpf.LoadPinnedMap(fname, &ebpf.LoadPinOptions{}) + if err != nil { + return nil, fmt.Errorf("%s: %w", fname, err) + } + return ret, nil +} + +type state struct { + mu sync.Mutex + nextID uint32 + mbsetMap *ebpf.Map +} + +func newState() (*state, error) { + m, err := openMap() + if err != nil { + return nil, fmt.Errorf("failed to open map: %w", err) + } + return &state{ + mbsetMap: m, + }, nil +} + +func (s *state) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + return s.mbsetMap.Close() +} + +// AllocID allocates a new ID +func (s *state) AllocID() (uint32, error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.nextID >= MaxIDs { + return InvalidID, fmt.Errorf("cannot allocate new id") + } + ret := s.nextID + s.nextID++ + return ret, nil +} + +// UpadteMap updates the map for a given id and its paths +// (NB: only an In operator for the paths is supported) +func (s *state) UpdateMap(id uint32, paths [][processapi.BINARY_PATH_MAX_LEN]byte) error { + s.mu.Lock() + defer s.mu.Unlock() + if id == InvalidID { + return fmt.Errorf("invalid id") + } else if id >= MaxIDs { + return fmt.Errorf("unexpected id") + } + + bit := uint64(1) << id + for _, path := range paths { + var val bitSet + var uflags ebpf.MapUpdateFlags + err := s.mbsetMap.Lookup(path, &val) + if errors.Is(err, ebpf.ErrKeyNotExist) { + val = bit + uflags = ebpf.UpdateNoExist + } else if err != nil { + return fmt.Errorf("failed to lookup mbset map: %w", err) + } else { + val |= bit + uflags = ebpf.UpdateExist + } + + if err := s.mbsetMap.Update(path, val, uflags); err != nil { + return fmt.Errorf("failed to update mbset map: %w", err) + } + } + + return nil +} + +var ( + glbSt *state + glbErr error + setGlobalState sync.Once +) + +func getState() (*state, error) { + setGlobalState.Do(func() { + glbSt, glbErr = newState() + }) + + return glbSt, glbErr +} + +func AllocID() (uint32, error) { + s, err := getState() + if err != nil { + return InvalidID, err + } + return s.AllocID() +} + +func UpdateMap(id uint32, paths [][processapi.BINARY_PATH_MAX_LEN]byte) error { + s, err := getState() + if err != nil { + return err + } + return s.UpdateMap(id, paths) +} diff --git a/pkg/sensors/base/base.go b/pkg/sensors/base/base.go index 708961411bf..b2bf22c11e4 100644 --- a/pkg/sensors/base/base.go +++ b/pkg/sensors/base/base.go @@ -9,6 +9,7 @@ import ( "github.com/cilium/tetragon/pkg/ksyms" "github.com/cilium/tetragon/pkg/logger" + "github.com/cilium/tetragon/pkg/mbset" "github.com/cilium/tetragon/pkg/option" "github.com/cilium/tetragon/pkg/sensors" "github.com/cilium/tetragon/pkg/sensors/exec/config" @@ -80,6 +81,8 @@ var ( CgroupRateMap = program.MapBuilder("cgroup_rate_map", Execve, Exit, Fork, CgroupRmdir) CgroupRateOptionsMap = program.MapBuilder("cgroup_rate_options_map", Execve) + MatchBinariesSetMap = program.MapBuilder(mbset.MapName, Execve) + sensor = sensors.Sensor{ Name: "__base__", } @@ -150,6 +153,7 @@ func GetDefaultMaps(cgroupRate bool) []*program.Map { TCPMonMap, TetragonConfMap, StatsMap, + MatchBinariesSetMap, } if cgroupRate { maps = append(maps, CgroupRateMap, CgroupRateOptionsMap) From 5d5a248a19f84e1174b349c5cd13f77ab48d09eb Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 23 Jul 2024 20:28:18 +0200 Subject: [PATCH 05/11] mbset: initialize tg_mbset_map This patch initialized the tg_mbset_map based on MatchBinaries sections. An id is allocated when the we parse the section. Subsequently, we update the given id for the patches specified in the section. Signed-off-by: Kornilios Kourtis --- bpf/process/types/basic.h | 1 + pkg/selectors/kernel.go | 11 +++++++++++ pkg/selectors/selectors.go | 2 ++ pkg/sensors/tracing/selectors.go | 8 ++++++++ 4 files changed, 22 insertions(+) diff --git a/bpf/process/types/basic.h b/bpf/process/types/basic.h index a6343ebed90..4d84734abe4 100644 --- a/bpf/process/types/basic.h +++ b/bpf/process/types/basic.h @@ -1520,6 +1520,7 @@ FUNC_INLINE size_t type_to_min_size(int type, int argm) struct match_binaries_sel_opts { __u32 op; __u32 map_id; + __u32 mbset_id; }; // This map is used by the matchBinaries selectors to retrieve their options diff --git a/pkg/selectors/kernel.go b/pkg/selectors/kernel.go index 2f6d66b79f3..a4eb84ef385 100644 --- a/pkg/selectors/kernel.go +++ b/pkg/selectors/kernel.go @@ -16,6 +16,7 @@ import ( "github.com/cilium/tetragon/pkg/idtable" "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" "github.com/cilium/tetragon/pkg/kernels" + "github.com/cilium/tetragon/pkg/mbset" "github.com/cilium/tetragon/pkg/reader/namespace" "github.com/cilium/tetragon/pkg/reader/network" ) @@ -1181,6 +1182,16 @@ func ParseMatchBinary(k *KernelSelectorState, b *v1alpha1.BinarySelector, selIdx // prepare the selector options sel := MatchBinariesSelectorOptions{} sel.Op = op + sel.MBSetID = mbset.InvalidID + if b.FollowChildren { + if op != SelectorOpIn { + return fmt.Errorf("matchBinary: followChildren not yet implemented for operation '%s'", b.Operator) + } + sel.MBSetID, err = mbset.AllocID() + if err != nil { + return fmt.Errorf("matchBinary followChildren: failed to allocate ID: %w", err) + } + } switch op { case SelectorOpIn, SelectorOpNotIn: diff --git a/pkg/selectors/selectors.go b/pkg/selectors/selectors.go index 27bd49fd82b..18b228a702b 100644 --- a/pkg/selectors/selectors.go +++ b/pkg/selectors/selectors.go @@ -96,6 +96,8 @@ type KernelSelectorMaps struct { type MatchBinariesSelectorOptions struct { Op uint32 MapID uint32 + // matchBinaries set for the selector + MBSetID uint32 } type KernelSelectorData struct { diff --git a/pkg/sensors/tracing/selectors.go b/pkg/sensors/tracing/selectors.go index c9b4b5b6ed6..06f432c803a 100644 --- a/pkg/sensors/tracing/selectors.go +++ b/pkg/sensors/tracing/selectors.go @@ -10,6 +10,7 @@ import ( "github.com/cilium/tetragon/pkg/api/processapi" "github.com/cilium/tetragon/pkg/bpf" "github.com/cilium/tetragon/pkg/kernels" + "github.com/cilium/tetragon/pkg/mbset" "github.com/cilium/tetragon/pkg/selectors" "github.com/cilium/tetragon/pkg/sensors" "github.com/cilium/tetragon/pkg/sensors/program" @@ -405,6 +406,7 @@ func populateMatchBinariesPathsMaps( outerMap *ebpf.Map, ) error { maxEntriesFromAllSelector := k.MatchBinariesPathsMaxEntries() + matchBinaries := k.MatchBinaries() for selectorID, paths := range k.MatchBinariesPaths() { maxEntries := len(paths) // Versions before 5.9 do not allow inner maps to have different sizes. @@ -440,6 +442,12 @@ func populateMatchBinariesPathsMaps( return fmt.Errorf("failed to insert %s: %w", innerName, err) } + mbSelector := matchBinaries[selectorID] + if mbSelector.MBSetID != mbset.InvalidID { + if err := mbset.UpdateMap(mbSelector.MBSetID, paths); err != nil { + return fmt.Errorf("updating mbset map failed: %w", err) + } + } } return nil } From e2be2b79397c092aa2bc0afd67b86750eaf11d6a Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 23 Jul 2024 20:30:42 +0200 Subject: [PATCH 06/11] exec: track matchbinary children This commit adds an mb_bitset field in the binary structure. The filed is set in the exec hook using the parent bitset and the contents of the tg_mbset_map. Note that in bpf_fork, we memcpy the old binary so the bitset will be propageted to children. Signed-off-by: Kornilios Kourtis --- bpf/lib/process.h | 4 ++++ bpf/process/bpf_execve_event.c | 27 +++++++++++++++++++++++++-- pkg/api/processapi/processapi.go | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/bpf/lib/process.h b/bpf/lib/process.h index 2c9bd5a9d92..366ac7057d8 100644 --- a/bpf/lib/process.h +++ b/bpf/lib/process.h @@ -305,6 +305,8 @@ struct msg_execve_event { #endif }; // All fields aligned so no 'packed' attribute. +typedef __u64 mbset_t; + // This structure stores the binary path that was recorded on execve. // Technically PATH_MAX is 4096 but we limit the length we store since we have // limits on the length of the string to compare: @@ -318,6 +320,8 @@ struct binary { __s64 path_length; // BINARY_PATH_MAX_LEN first bytes of the path char path[BINARY_PATH_MAX_LEN]; + // matchBinary bitset for binary + mbset_t mb_bitset; }; // All fields aligned so no 'packed' attribute // The execve_map_value is tracked by the TGID of the thread group diff --git a/bpf/process/bpf_execve_event.c b/bpf/process/bpf_execve_event.c index 3cef94c4a8b..a3cc249434e 100644 --- a/bpf/process/bpf_execve_event.c +++ b/bpf/process/bpf_execve_event.c @@ -31,8 +31,6 @@ struct { __type(value, struct msg_data); } data_heap SEC(".maps"); -typedef __u64 mbset_t; - /* tg_mbset_map holds a mapping from (binary) paths to a bitset of ids that it matches. The map is * written by user-space and read in the exec hook to determine the bitset of ids of a binary that * is executed. @@ -271,6 +269,29 @@ execve_rate(void *ctx) return 0; } +/* update bitset mark */ +FUNC_INLINE +void update_mb_bitset(struct binary *bin) +{ + __u64 *bitsetp; + struct execve_map_value *parent; + + parent = event_find_parent(); + if (parent) { + /* ->mb_bitset is used to track matchBinary matches to children (followChildren), so + * here we propagate the parent value to the child. + */ + bin->mb_bitset = parent->bin.mb_bitset; + } + + /* check the map and see if the binary path matches a binary + * NB: only the In operator is supported for now + */ + bitsetp = map_lookup_elem(&tg_mbset_map, bin->path); + if (bitsetp) + bin->mb_bitset |= *bitsetp; +} + /** * execve_send() sends the collected execve event data. * @@ -357,6 +378,8 @@ execve_send(void *ctx) curr->bin.path_length--; } #endif + + update_mb_bitset(&curr->bin); } event->common.flags = 0; diff --git a/pkg/api/processapi/processapi.go b/pkg/api/processapi/processapi.go index a7b2ae06a09..7854630282c 100644 --- a/pkg/api/processapi/processapi.go +++ b/pkg/api/processapi/processapi.go @@ -141,6 +141,7 @@ type MsgCapabilities struct { type Binary struct { PathLength int64 Path [BINARY_PATH_MAX_LEN]byte + MBSet uint64 } type MsgNamespaces struct { From 2acaaa9262284cab7a4063f829089eb691937b98 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Sun, 4 Aug 2024 09:46:43 +0200 Subject: [PATCH 07/11] bpf: introduce fallthrough in compiler.h This is going to be used in the next patch. Signed-off-by: Kornilios Kourtis --- bpf/include/compiler.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bpf/include/compiler.h b/bpf/include/compiler.h index 737f818488e..6610d52b5f5 100644 --- a/bpf/include/compiler.h +++ b/bpf/include/compiler.h @@ -13,4 +13,8 @@ #define FUNC_INLINE static inline __attribute__((always_inline)) #endif +#ifndef fallthrough +#define fallthrough __attribute__((fallthrough)) +#endif + #endif /* __BPF_COMPILER_H__ */ From 1ad85a8519b27440640f56b14e135349f6b4a7f7 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Tue, 23 Jul 2024 20:33:47 +0200 Subject: [PATCH 08/11] bpf: check the bitset in match_binaries This commit adds a check in match_binaries that uses the bitset that is tracked from parent to children, allowing to track children of a given binary. Signed-off-by: Kornilios Kourtis --- bpf/process/types/basic.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bpf/process/types/basic.h b/bpf/process/types/basic.h index 4d84734abe4..409cb10107a 100644 --- a/bpf/process/types/basic.h +++ b/bpf/process/types/basic.h @@ -1578,6 +1578,14 @@ FUNC_INLINE int match_binaries(__u32 selidx) switch (selector_options->op) { case op_filter_in: + /* Check if we match the selector's bit in ->mb_bitset, which means that the + * process matches a matchBinaries section with a followChidren:true + * attribute either because the binary matches or because the binary of a + * parent matched. + */ + if (current->bin.mb_bitset & (1UL << selector_options->mbset_id)) + return 1; + fallthrough; case op_filter_notin: path_map = map_lookup_elem(&tg_mb_paths, &selidx); if (!path_map) From 9ec1054906f437ee1d0f2acdd73e5d33a6cb69ca Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Wed, 24 Jul 2024 11:01:02 +0200 Subject: [PATCH 09/11] testutils: add a CopyFileToTmp function CopyFileToTmp copies a file to /tmp and returns its name. It also chmods the destination file, so it can be used to copy binaries. It's going to be used the next patch. Signed-off-by: Kornilios Kourtis --- pkg/testutils/copyfile.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pkg/testutils/copyfile.go b/pkg/testutils/copyfile.go index cf79852f151..c474628a4b4 100644 --- a/pkg/testutils/copyfile.go +++ b/pkg/testutils/copyfile.go @@ -4,8 +4,10 @@ package testutils import ( + "fmt" "io" "os" + "path" "syscall" ) @@ -28,3 +30,38 @@ func CopyFile(toPath, fromPath string, perm os.FileMode) error { } return err } + +func CopyFileToTmp(fname string) (string, error) { + bname := path.Base(fname) + pattern := fmt.Sprintf("%s.*", bname) + outf, err := os.CreateTemp("", pattern) + if err != nil { + return "", err + } + defer outf.Close() + + inf, err := os.Open(fname) + if err != nil { + os.Remove(outf.Name()) + return "", err + } + defer inf.Close() + + info, err := inf.Stat() + if err != nil { + os.Remove(outf.Name()) + return "", err + } + err = os.Chmod(outf.Name(), info.Mode()) + if err != nil { + os.Remove(outf.Name()) + return "", err + } + + if _, err := io.Copy(outf, inf); err != nil { + os.Remove(outf.Name()) + return "", err + } + + return outf.Name(), nil +} From 3c43d96455a98397b1088e11788649155de8b800 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Wed, 24 Jul 2024 11:02:28 +0200 Subject: [PATCH 10/11] testutils: improve loadGenericSensorTest loadGenericSensorTest is a helper function to load a generic sensor. With the addition of the tg_mbset_map in the previous commits, when using a matchBinaries with followChildren: true because for the latter, we need to base sensor to be loaded. This fixes the above issue by initializing the base (exec) sensor before running SensorsFromPolicy. Signed-off-by: Kornilios Kourtis --- pkg/sensors/tracing/selectors_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/sensors/tracing/selectors_test.go b/pkg/sensors/tracing/selectors_test.go index a77ac3905c1..329d6fe767b 100644 --- a/pkg/sensors/tracing/selectors_test.go +++ b/pkg/sensors/tracing/selectors_test.go @@ -46,6 +46,10 @@ func loadGenericSensorTest(t *testing.T, spec *v1alpha1.TracingPolicySpec) *sens Metadata: v1.ObjectMeta{Name: "name"}, Spec: *spec, } + + tus.LoadSensor(t, base.GetInitialSensor()) + tus.LoadSensor(t, testsensor.GetTestSensor()) + ret, err := sensors.SensorsFromPolicy(tp, policyfilter.NoFilterID) if err != nil { t.Fatalf("GetSensorsFromParserPolicy failed: %v", err) @@ -54,8 +58,6 @@ func loadGenericSensorTest(t *testing.T, spec *v1alpha1.TracingPolicySpec) *sens } tpSensor := ret[0] option.Config.HubbleLib = tus.Conf().TetragonLib - tus.LoadSensor(t, base.GetInitialSensor()) - tus.LoadSensor(t, testsensor.GetTestSensor()) tus.LoadSensor(t, tpSensor) return tpSensor.(*sensors.Sensor) } From dc2d413421dca6ae1da8de351accad17a00f2153 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Wed, 24 Jul 2024 11:04:57 +0200 Subject: [PATCH 11/11] tracing: add a test for MatchBinaries followChidren The test copies /bin/sh into a temp location and Creates a policy that mathes the temp sh and its children. It executes the getcpu program and checks that the policy caught the syscall. Signed-off-by: Kornilios Kourtis --- .../matchbinaries_follow_children_test.go | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 pkg/sensors/tracing/matchbinaries_follow_children_test.go diff --git a/pkg/sensors/tracing/matchbinaries_follow_children_test.go b/pkg/sensors/tracing/matchbinaries_follow_children_test.go new file mode 100644 index 00000000000..3f0af341145 --- /dev/null +++ b/pkg/sensors/tracing/matchbinaries_follow_children_test.go @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package tracing + +import ( + "context" + "fmt" + "os" + "os/exec" + "testing" + + "github.com/cilium/tetragon/pkg/grpc/tracing" + "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" + "github.com/cilium/tetragon/pkg/logger" + "github.com/cilium/tetragon/pkg/reader/notify" + "github.com/cilium/tetragon/pkg/testutils" + "github.com/cilium/tetragon/pkg/testutils/perfring" + tus "github.com/cilium/tetragon/pkg/testutils/sensors" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func TestMatchBinariesFollowChildren(t *testing.T) { + + testutils.CaptureLog(t, logger.GetLogger().(*logrus.Logger)) + ctx, cancel := context.WithTimeout(context.Background(), tus.Conf().CmdWaitTime) + defer cancel() + shPath, err := exec.LookPath("sh") + if err != nil { + t.Fatalf("failed to find 'sh' exec: %v", err) + } + tmpShPath, err := testutils.CopyFileToTmp(shPath) + if err != nil { + t.Fatalf("failed to copy 'sh' exec: %v", err) + } + t.Cleanup(func() { + os.Remove(tmpShPath) + }) + + event := "sys_enter_getcpu" + spec := &v1alpha1.TracingPolicySpec{ + Tracepoints: []v1alpha1.TracepointSpec{{ + Subsystem: "syscalls", + Event: event, + Args: []v1alpha1.KProbeArg{}, + Selectors: []v1alpha1.KProbeSelector{{ + MatchBinaries: []v1alpha1.BinarySelector{{ + Operator: "In", + Values: []string{ + tmpShPath, + }, + FollowChildren: true, + }}, + }}, + }}, + } + + loadGenericSensorTest(t, spec) + getcpuCnt := 0 + eventFn := func(ev notify.Message) error { + if tpEvent, ok := ev.(*tracing.MsgGenericTracepointUnix); ok { + if tpEvent.Event != event { + return fmt.Errorf("unexpected tracepoint event, %s:%s", tpEvent.Subsys, tpEvent.Event) + } + getcpuCnt++ + } + return nil + } + + getcpuBin := testutils.RepoRootPath("contrib/tester-progs/getcpu") + ops := func() { + cmd := exec.Command(tmpShPath, "-c", getcpuBin) + if err := cmd.Run(); err != nil { + t.Fatalf("failed to run command %s: %v", cmd, err) + } + } + + perfring.RunTest(t, ctx, ops, eventFn) + require.Equal(t, 1, getcpuCnt) +}