【问题标题】:How can I determine which namespaces a PID is in from kernel space?如何从内核空间确定 PID 位于哪些命名空间?
【发布时间】:2018-07-02 06:55:52
【问题描述】:

我正在尝试编写一个 eBPF 程序来记录系统上运行的容器对特定系统调用的每次调用。我正在使用bcc,并且可以使用bpf_get_current_pid_tgid() 检索PID。

我可以从用户空间检查 proc 文件系统,以确定进程的命名空间是否与根命名空间不同,以猜测它是否是容器进程,但我不知道你如何从内核空间做到这一点?

【问题讨论】:

  • 启动 Linux 4.8,您可以使用 bpf_get_current_task 助手来检索 struct task_structbpf_get_current_task()->nsproxy 为您提供当前进程的namespaces。你到底在找什么?容器内的PID?
  • 太棒了,我会检查一下 - 我希望能够在 eBPF 程序中确定/猜测进程是否在容器内运行,如果是,我想流式传输任何交互式shell 会话返回到某个中心位置以进行审计。这可能允许管理员回放任何潜在的恶意会话
  • 这是一项正在进行中的工作,但我正试图了解 eBPF 的可能性,因为它似乎是一个强大的工具
  • 我会写一个正确的答案。

标签: c linux-kernel bpf ebpf bcc-bpf


【解决方案1】:

您可以使用(仅限 Linux 4.8+)bpf_get_current_task 帮助程序来检索当前进程的 struct task_struct。那么容器内的进程看到的PID就是t->nsproxy->pid_ns_for_children->last_pid

以下显示了跟踪execve 系统调用时的实际情况(您可以在容器内使用top 来检查upid 是否正确):

from bcc import BPF
BPF(text="""
#include <linux/pid_namespace.h>
int kprobe__sys_execve(void *ctx) {
    u32 pid = bpf_get_current_pid_tgid();
    struct task_struct *t = (struct task_struct *)bpf_get_current_task();
    u32 upid = t->nsproxy->pid_ns_for_children->last_pid;
    bpf_trace_printk("pid=%d; upid=%d!\\n", pid, upid);
    return 0;
}
""").trace_print()

以下差异(基于a44d26ed3)扩展了密件抄送的execsnoop.py 以检索upid:

diff --git a/tools/execsnoop.py b/tools/execsnoop.py
index 5711fd1..2134f69 100755
--- a/tools/execsnoop.py
+++ b/tools/execsnoop.py
@@ -53,6 +53,7 @@ bpf_text = """
 #include <uapi/linux/ptrace.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
+#include <linux/pid_namespace.h>

 #define ARGSIZE  128

@@ -63,6 +64,7 @@ enum event_type {

 struct data_t {
     u32 pid;  // PID as in the userspace term (i.e. task->tgid in kernel)
+    u32 upid;
     char comm[TASK_COMM_LEN];
     enum event_type type;
     char argv[ARGSIZE];
@@ -119,6 +121,8 @@ int kretprobe__sys_execve(struct pt_regs *ctx)
 {
     struct data_t data = {};
     data.pid = bpf_get_current_pid_tgid() >> 32;
+    struct task_struct *t = (struct task_struct *)bpf_get_current_task();
+    data.upid = t->nsproxy->pid_ns_for_children->last_pid;
     bpf_get_current_comm(&data.comm, sizeof(data.comm));
     data.type = EVENT_RET;
     data.retval = PT_REGS_RC(ctx);
@@ -134,7 +138,7 @@ b = BPF(text=bpf_text.replace("MAXARG", args.max_args))
 # header
 if args.timestamp:
     print("%-8s" % ("TIME(s)"), end="")
-print("%-16s %-6s %-6s %3s %s" % ("PCOMM", "PID", "PPID", "RET", "ARGS"))
+print("%-16s %-6s %-6s %-6s %3s %s" % ("PCOMM", "PID", "UPID", "PPID", "RET", "ARGS"))

 TASK_COMM_LEN = 16      # linux/sched.h
 ARGSIZE = 128           # should match #define in C above
@@ -142,6 +146,7 @@ ARGSIZE = 128           # should match #define in C above
 class Data(ct.Structure):
     _fields_ = [
         ("pid", ct.c_uint),
+        ("upid", ct.c_uint),
         ("comm", ct.c_char * TASK_COMM_LEN),
         ("type", ct.c_int),
         ("argv", ct.c_char * ARGSIZE),
@@ -189,8 +194,8 @@ def print_event(cpu, data, size):
             if args.timestamp:
                 print("%-8.3f" % (time.time() - start_ts), end="")
             ppid = get_ppid(event.pid)
-            print("%-16s %-6s %-6s %3s %s" % (event.comm.decode(), event.pid,
-                    ppid if ppid > 0 else "?", event.retval,
+            print("%-16s %-6s %-6s %-6s %3s %s" % (event.comm.decode(), event.pid,
+                    event.upid, ppid if ppid > 0 else "?", event.retval,
                     b' '.join(argv[event.pid]).decode()))
         try:
             del(argv[event.pid])

【讨论】:

  • 这真的很酷,谢谢 - 我尝试将其调整为挂载命名空间,但我没有得到预期的结果。我正在打印以下值:t-&gt;nsproxy-&gt;mnt_ns-&gt;ns.inum 但无论我是否在容器中执行,我总是得到值 0
  • 这可能是由于您在 GitHub 问题中回答的内核 4.13 中的随机 task_struct。我在您之前的问题(stackoverflow.com/questions/48392152/…)中发表了评论;你有机会调查一下吗?
  • 谢谢,是的,我切换到了 4.11,一切都运行良好 :) 我回复了一些我正在工作的细节
  • 自 4.15 起,这不再有效,因为 last_pid 字段已被替换。 github.com/torvalds/linux/commit/95846ecf9dac
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-01
  • 2021-10-27
  • 1970-01-01
  • 2011-06-15
  • 2013-03-29
  • 2011-02-14
  • 1970-01-01
相关资源
最近更新 更多