Skip to content

Commit

Permalink
tetragon: Add debug interface to track cgroups to namespace mappings
Browse files Browse the repository at this point in the history
Debugging BPF and some kernel functions I want to understand cgroup to
namespace mappings at event side. This patch maintains a stable mapping
between cgroups and human readable namespaces. The end goal is to filter
out noisy namespaces from execs which will be follow up series. This is
minimally useful as is.

To support this just extend the use of namespace filters from kprobe
and tracepoints into a more general space where we can hook selectors.

Signed-off-by: John Fastabend <[email protected]>
  • Loading branch information
jrfastab committed Jun 11, 2024
1 parent d507441 commit 396b9b1
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 0 deletions.
2 changes: 2 additions & 0 deletions bpf/process/bpf_execve_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "bpf_helpers.h"
#include "bpf_rate.h"

#include "policy_filter.h"

char _license[] __attribute__((section("license"), used)) = "Dual BSD/GPL";

struct {
Expand Down
8 changes: 8 additions & 0 deletions bpf/process/policy_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
#include "bpf_tracing.h"

#define POLICY_FILTER_MAX_POLICIES 128
#define POLICY_FILTER_MAX_NAMESPACES 1024

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, POLICY_FILTER_MAX_NAMESPACES);
__uint(key_size, sizeof(u64));
__uint(value_size, sizeof(u64));
} cgroup_namespace_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
Expand Down
25 changes: 25 additions & 0 deletions cmd/tetra/dump/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,28 @@ func PolicyfilterState(fname string) {
fmt.Printf("%d: %s\n", polId, strings.Join(ids, ","))
}
}

func NamespaceState(fname string) error {
m, err := ebpf.LoadPinnedMap(fname, &ebpf.LoadPinOptions{
ReadOnly: true,
})
if err != nil {
logger.GetLogger().WithError(err).WithField("file", fname).Warn("Could not open process tree map")
return err
}

defer m.Close()

var (
key uint64
val uint64
)

fmt.Printf("cgroupId: stableId\n")
iter := m.Iterate()
for iter.Next(&key, &val) {
fmt.Printf("%d: %d\n", key, val)
}

return nil
}
17 changes: 17 additions & 0 deletions cmd/tetra/policyfilter/policyfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,28 @@ func New() *cobra.Command {
dumpCmd(),
addCommand(),
cgroupGetIDCommand(),
dumpDebugCmd(),
)

return ret
}

func dumpDebugCmd() *cobra.Command {
mapFname := filepath.Join(defaults.DefaultMapRoot, defaults.DefaultMapPrefix, policyfilter.CgrpNsMapName)
ret := &cobra.Command{
Use: "dumpcgrp",
Short: "dump cgroup ID to namespace state",
Args: cobra.ExactArgs(0),
Run: func(_ *cobra.Command, _ []string) {
dump.NamespaceState(mapFname)
},
}

flags := ret.Flags()
flags.StringVar(&mapFname, "map-fname", mapFname, "policyfilter map filename")
return ret
}

func cgroupGetIDCommand() *cobra.Command {
mapFname := filepath.Join(defaults.DefaultMapRoot, defaults.DefaultMapPrefix, policyfilter.MapName)
ret := &cobra.Command{
Expand Down
157 changes: 157 additions & 0 deletions pkg/policyfilter/namespace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

package policyfilter

import (
"fmt"
"os"
"path"
"path/filepath"

"github.com/cilium/ebpf"
"github.com/cilium/tetragon/pkg/bpf"
"github.com/cilium/tetragon/pkg/kernels"
"github.com/cilium/tetragon/pkg/logger"
"github.com/cilium/tetragon/pkg/option"
lru "github.com/hashicorp/golang-lru/v2"
)

const (
CgrpNsMapName = "cgroup_namespace_map"
namespaceCacheSize = 1024
)

// ExecObj returns the exec object based on the kernel version
func execObj() string {
if kernels.EnableV61Progs() {
return "bpf_execve_event_v61.o"
} else if kernels.MinKernelVersion("5.11") {
return "bpf_execve_event_v511.o"
} else if kernels.EnableLargeProgs() {
return "bpf_execve_event_v53.o"
}
return "bpf_execve_event.o"
}

// NamespaceMap is a simple wrapper for ebpf.Map so that we can write methods for it
type NamespaceMap struct {
cgroupIdMap *ebpf.Map
nsIdMap *lru.Cache[uint64, string]
id uint64
}

// newNamespaceMap returns a new namespace mapping. The namespace map consists of
// two pieces. First a cgroup to ID map. The ID is useful for BPF so we can avoid
// strings in BPF side. Then a stable ID to namespace mapping.
func newNamespaceMap() (*NamespaceMap, error) {
cache, err := lru.New[uint64, string](namespaceCacheSize)
if err != nil {
return nil, fmt.Errorf("create namespace ID cache failed")
}

objName := execObj()
objPath := path.Join(option.Config.HubbleLib, objName)
spec, err := ebpf.LoadCollectionSpec(objPath)
if err != nil {
return nil, fmt.Errorf("loading spec for %s failed: %w", objPath, err)
}
nsMapSpec, ok := spec.Maps[CgrpNsMapName]
if !ok {
return nil, fmt.Errorf("%s not found in %s", CgrpNsMapName, objPath)
}

ret, err := ebpf.NewMap(nsMapSpec)
if err != nil {
return nil, err
}

mapDir := bpf.MapPrefixPath()
pinPath := filepath.Join(mapDir, CgrpNsMapName)
os.Remove(pinPath)
os.Mkdir(mapDir, os.ModeDir)
err = ret.Pin(pinPath)
if err != nil {
ret.Close()
return nil, fmt.Errorf("failed to pin Namespace map in %s: %w", pinPath, err)
}

return &NamespaceMap{
cgroupIdMap: ret,
nsIdMap: cache,
}, err
}

// release closes the namespace BPF map, removes (unpin) the bpffs file.
// Then the LRU cache is cleared.
func (m NamespaceMap) release() error {
if err := m.cgroupIdMap.Close(); err != nil {
return err
}

// nolint:revive // ignore "if-return: redundant if just return error" for clarity
if err := m.cgroupIdMap.Unpin(); err != nil {
return err
}

m.nsIdMap.Purge()
return nil
}

func (m NamespaceMap) readBpf() (map[uint64]uint64, error) {
var mapping map[uint64]uint64
var err error

file := filepath.Join(bpf.MapPrefixPath(), CgrpNsMapName)

m.cgroupIdMap, err = ebpf.LoadPinnedMap(file, nil)
if err != nil {
logger.GetLogger().WithError(err).WithField("file", file).Warn("Could not open process tree map")
return mapping, err
}

defer m.cgroupIdMap.Close()

var (
key uint64
val uint64
)

iter := m.cgroupIdMap.Iterate()
for iter.Next(&key, &val) {
mapping[key] = val
}

return mapping, nil
}

func (m NamespaceMap) readNamespace(cgrps map[uint64]uint64) (map[uint64]string, error) {
var mapping map[uint64]string

for _, k := range cgrps {
v, ok := m.nsIdMap.Get(k)
if ok == false {
logger.GetLogger().WithField("cgrpid", k).Warn("Cgrpid not in namespace mapping")
continue
}
mapping[k] = v
}
return mapping, nil
}

// addCgroupIDs add cgroups ids to the policy map
// todo: use batch operations when supported
func (m NamespaceMap) addCgroupIDs(cinfo []containerInfo) error {
for _, c := range cinfo {
if err := m.cgroupIdMap.Update(&c.cgID, m.id, ebpf.UpdateAny); err != nil {
logger.GetLogger().WithError(err).WithField("cgid", c.cgID).WithField("id", m.id).WithField("ns", c.name).Warn("Unable to insert cgroup id map")
continue
}
if ok := m.nsIdMap.Add(m.id, c.name); ok != false {
logger.GetLogger().WithField("cgid", c.cgID).WithField("id", m.id).WithField("ns", c.name).Warn("Id to namespace map caused eviction")
}
m.id++
}

return nil
}
13 changes: 13 additions & 0 deletions pkg/policyfilter/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ type state struct {
// polify filters (outer) map handle
pfMap PfMap

// global policy map handle
global *NamespaceMap

cgidFinder cgidFinder
}

Expand Down Expand Up @@ -279,6 +282,11 @@ func newState(
return nil, err
}

ret.global, err = newNamespaceMap()
if err != nil {
return nil, err
}

return ret, nil
}

Expand Down Expand Up @@ -554,6 +562,11 @@ func (m *state) addPodContainers(pod *podInfo, containerIDs []string,
"containers-info": cinfo,
}).Info("addPodContainers: container(s) added")

// update global
if m.global != nil {
m.global.addCgroupIDs(cinfo)
}

// update matching policy maps
for _, policyID := range pod.matchedPolicies {
pol := m.findPolicy(policyID)
Expand Down

0 comments on commit 396b9b1

Please sign in to comment.