Skip to content

Commit df0c42a

Browse files
committed
Find process in mount NS if none are found in the cache
The PIDs in mount NS cache is updated from `sched_process_exec` events, but the PIDs from the events cannot be used for procfs access if Tracee is not running in the root PID namespace (for example on WSL). To work around this limitation, if no PID in the cache exists for the requested mount NS, we search through procfs to find any PID in the requested mount NS and add it to the cache.
1 parent e79c970 commit df0c42a

File tree

3 files changed

+75
-24
lines changed

3 files changed

+75
-24
lines changed

pkg/containers/path_resolver.go

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/aquasecurity/tracee/pkg/bucketscache"
1111
"github.com/aquasecurity/tracee/pkg/errfmt"
1212
"github.com/aquasecurity/tracee/pkg/logger"
13+
"github.com/aquasecurity/tracee/pkg/utils/proc"
1314
)
1415

1516
// ContainerPathResolver generates an accessible absolute path from the root
@@ -45,36 +46,57 @@ func (cPathRes *ContainerPathResolver) GetHostAbsPath(mountNSAbsolutePath string
4546
pids := cPathRes.mountNSPIDsCache.GetBucket(uint32(mountNS))
4647

4748
for _, pid := range pids {
48-
// cap.SYS_PTRACE is needed here. Instead of raising privileges, since
49-
// this is called too frequently, if the needed event is being traced,
50-
// the needed capabilities are added to the Base ring and are always set
51-
// as effective.
52-
//
53-
// (Note: To change this behavior we need a privileged process/server)
54-
55-
procRootPath := fmt.Sprintf("/proc/%d/root", int(pid))
56-
57-
// fs.FS interface requires relative paths, so the '/' prefix should be trimmed.
58-
entries, err := fs.ReadDir(cPathRes.fs, strings.TrimPrefix(procRootPath, "/"))
49+
procRoot, err := cPathRes.getProcessFSRoot(uint(pid))
5950
if err != nil {
60-
// This process is either not alive or we don't have permissions to access.
6151
// Try next pid in mount ns to find accessible path to mount ns files.
62-
logger.Debugw(
63-
"Finding mount NS path",
64-
"Unreachable proc root path", procRootPath,
65-
"error", err.Error(),
66-
)
52+
logger.Debugw("Could not access process FS", "pid", pid, "error", err)
6753
continue
6854
}
69-
if len(entries) == 0 {
70-
return "", errfmt.Errorf("empty directory")
71-
}
72-
if err == nil {
73-
return fmt.Sprintf("%s%s", procRootPath, mountNSAbsolutePath), nil
74-
}
55+
56+
return fmt.Sprintf("%s%s", procRoot, mountNSAbsolutePath), nil
57+
}
58+
59+
// No PIDs registered in this namespace, or couldn't access FS root of any of the PIDs found.
60+
// Try finding one in procfs.
61+
pid, err := proc.GetAnyProcessInNS("mnt", mountNS)
62+
if err != nil {
63+
// Couldn't find a process in this namespace using procfs
64+
return "", ErrContainerFSUnreachable
65+
}
66+
67+
procRoot, err := cPathRes.getProcessFSRoot(pid)
68+
if err != nil {
69+
logger.Debugw("Could not access process FS", "pid", pid, "error", err)
70+
return "", ErrContainerFSUnreachable
71+
}
72+
73+
// Register this process in the mount namespace
74+
cPathRes.mountNSPIDsCache.AddBucketItem(uint32(mountNS), uint32(pid))
75+
76+
return fmt.Sprintf("%s%s", procRoot, mountNSAbsolutePath), nil
77+
}
78+
79+
func (cPathRes *ContainerPathResolver) getProcessFSRoot(pid uint) (string, error) {
80+
// cap.SYS_PTRACE is needed here. Instead of raising privileges, since
81+
// this is called too frequently, if the needed event is being traced,
82+
// the needed capabilities are added to the Base ring and are always set
83+
// as effective.
84+
//
85+
// (Note: To change this behavior we need a privileged process/server)
86+
87+
procRootPath := fmt.Sprintf("/proc/%d/root", pid)
88+
89+
// fs.FS interface requires relative paths, so the '/' prefix should be trimmed.
90+
entries, err := fs.ReadDir(cPathRes.fs, strings.TrimPrefix(procRootPath, "/"))
91+
if err != nil {
92+
// This process is either not alive or we don't have permissions to access.
93+
return "", errfmt.Errorf("failed accessing process FS root %s: %v", procRootPath, err)
94+
}
95+
if len(entries) == 0 {
96+
return "", errfmt.Errorf("process FS root (%s) is empty", procRootPath)
7597
}
7698

77-
return "", ErrContainerFSUnreachable
99+
return procRootPath, nil
78100
}
79101

80102
var (

pkg/ebpf/processor_funcs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func processKernelReadFile(event *trace.Event) error {
120120
// processSchedProcessExec processes a sched_process_exec event by capturing the executed file.
121121
func (t *Tracee) processSchedProcessExec(event *trace.Event) error {
122122
// cache this pid by it's mnt ns
123+
// TODO: don't do this if Tracee is not in the root PID NS?
123124
if event.ProcessID == 1 {
124125
t.pidsInMntns.ForceAddBucketItem(uint32(event.MountNS), uint32(event.HostProcessID))
125126
} else {

pkg/utils/proc/ns.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,31 @@ func extractNSFromLink(link string) (int, error) {
191191
}
192192
return ns, nil
193193
}
194+
195+
// GetAnyProcessInNS returns the PID of any process in the given namespace type and number.
196+
// It returns the first process it finds when iterating over /proc that satisfies the request.
197+
// To do so, it requires access to the /proc file system, and CAP_SYS_PTRACE capability.
198+
func GetAnyProcessInNS(nsName string, nsNum int) (uint, error) {
199+
entries, err := os.ReadDir("/proc")
200+
if err != nil {
201+
return 0, errfmt.Errorf("could not read proc dir: %v", err)
202+
}
203+
204+
for _, entry := range entries {
205+
pid, err := strconv.ParseUint(entry.Name(), 10, 32)
206+
if err != nil {
207+
// Not a PID directory
208+
continue
209+
}
210+
ns, err := GetProcNS(uint(pid), nsName)
211+
if err != nil {
212+
logger.Infow("Failed fetching process namespace", "pid", pid, "namespace", nsName, "error", err)
213+
continue
214+
}
215+
if ns == nsNum {
216+
return uint(pid), nil
217+
}
218+
}
219+
220+
return 0, errfmt.Errorf("could not find any process in %s namesapce %d", nsName, nsNum)
221+
}

0 commit comments

Comments
 (0)