Skip to content

Commit

Permalink
proc_reader: handle in_init_tree
Browse files Browse the repository at this point in the history
Recently we introduced the in_init_tree flag into execve map values to indicate whether
a process is a member of the initial process tree for a container. This worked well for
containers started after Tetragon, but broke for cases where the container was started
before Tetragon, since our procfs walk did not account for the in_init_tree flag. Fix this
behaviour by introducing logic in the procfs walk to account for this.

Signed-off-by: William Findlay <william.findlay@isovalent.com>
  • Loading branch information
will-isovalent committed Jan 24, 2025
1 parent d9924e9 commit 618963e
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 25 deletions.
62 changes: 37 additions & 25 deletions pkg/sensors/exec/procevents/proc_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,41 @@ func updateExecveMapStats(procs int64) {
}
}

func procToKeyValue(p procs, inInitTree map[uint32]struct{}) (*execvemap.ExecveKey, *execvemap.ExecveValue) {
k := &execvemap.ExecveKey{Pid: p.pid}
v := &execvemap.ExecveValue{}

v.Parent.Pid = p.ppid
v.Parent.Ktime = p.pktime
v.Process.Pid = p.pid
v.Process.Ktime = p.ktime
v.Flags = 0
v.Nspid = p.nspid
v.Capabilities.Permitted = p.permitted
v.Capabilities.Effective = p.effective
v.Capabilities.Inheritable = p.inheritable
v.Namespaces.UtsInum = p.uts_ns
v.Namespaces.IpcInum = p.ipc_ns
v.Namespaces.MntInum = p.mnt_ns
v.Namespaces.PidInum = p.pid_ns
v.Namespaces.PidChildInum = p.pid_for_children_ns
v.Namespaces.NetInum = p.net_ns
v.Namespaces.TimeInum = p.time_ns
v.Namespaces.TimeChildInum = p.time_for_children_ns
v.Namespaces.CgroupInum = p.cgroup_ns
v.Namespaces.UserInum = p.user_ns
pathLength := copy(v.Binary.Path[:], p.exe)
v.Binary.PathLength = int32(pathLength)

_, parentInInitTree := inInitTree[p.ppid]
if v.Nspid == 1 || parentInInitTree {
v.Flags |= api.EventInInitTree
inInitTree[p.pid] = struct{}{}
}

return k, v
}

func writeExecveMap(procs []procs) {
mapDir := bpf.MapPrefixPath()

Expand All @@ -335,32 +370,9 @@ func writeExecveMap(procs []procs) {
panic(err)
}
}
inInitTree := make(map[uint32]struct{})
for _, p := range procs {
k := &execvemap.ExecveKey{Pid: p.pid}
v := &execvemap.ExecveValue{}

v.Parent.Pid = p.ppid
v.Parent.Ktime = p.pktime
v.Process.Pid = p.pid
v.Process.Ktime = p.ktime
v.Flags = 0
v.Nspid = p.nspid
v.Capabilities.Permitted = p.permitted
v.Capabilities.Effective = p.effective
v.Capabilities.Inheritable = p.inheritable
v.Namespaces.UtsInum = p.uts_ns
v.Namespaces.IpcInum = p.ipc_ns
v.Namespaces.MntInum = p.mnt_ns
v.Namespaces.PidInum = p.pid_ns
v.Namespaces.PidChildInum = p.pid_for_children_ns
v.Namespaces.NetInum = p.net_ns
v.Namespaces.TimeInum = p.time_ns
v.Namespaces.TimeChildInum = p.time_for_children_ns
v.Namespaces.CgroupInum = p.cgroup_ns
v.Namespaces.UserInum = p.user_ns
pathLength := copy(v.Binary.Path[:], p.exe)
v.Binary.PathLength = int32(pathLength)

k, v := procToKeyValue(p, inInitTree)
err := m.Put(k, v)
if err != nil {
logger.GetLogger().WithField("value", v).WithError(err).Warn("failed to put value in execve_map")
Expand Down
39 changes: 39 additions & 0 deletions pkg/sensors/exec/procevents/proc_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
package procevents

import (
"os/exec"
"strconv"
"strings"
"testing"
"time"

"github.com/cilium/tetragon/pkg/api"
"github.com/cilium/tetragon/pkg/observer/observertesthelper/docker"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand All @@ -20,3 +27,35 @@ func TestListRunningProcs(t *testing.T) {
require.Equal(t, p.pid, p.tid)
}
}

func TestInInitTreeProcfs(t *testing.T) {
if err := exec.Command("docker", "version").Run(); err != nil {
t.Skipf("docker not available. skipping test: %s", err)
}

containerID := docker.DockerCreate(t, "--name", "procfs-in-init-tree-test", "bash", "bash", "-c", "sleep infinity")

docker.DockerStart(t, "procfs-in-init-tree-test")
time.Sleep(1 * time.Second)

rootPidOutput, err := exec.Command("docker", "inspect", "-f", "{{.State.Pid}}", containerID).Output()
require.NoError(t, err, "root pid should fetch")
rootPid, err := strconv.Atoi(strings.TrimSpace(string(rootPidOutput)))
require.NoError(t, err, "root pid should parse")

procs, err := listRunningProcs("/proc")
require.NoError(t, err)
require.NotNil(t, procs)
require.NotEqual(t, 0, len(procs))

inInitTree := make(map[uint32]struct{})
for _, p := range procs {
require.NotZero(t, p.pid)
require.Equal(t, p.pid, p.tid)
_, v := procToKeyValue(p, inInitTree)
if v.Process.Pid == uint32(rootPid) || v.Parent.Pid == uint32(rootPid) {
isInInitTree := v.Flags&api.EventInInitTree == api.EventInInitTree
assert.True(t, isInInitTree)
}
}
}

0 comments on commit 618963e

Please sign in to comment.