-
-
Notifications
You must be signed in to change notification settings - Fork 49
Feat/container uprobe support #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yoloyyh
wants to merge
9
commits into
eunomia-bpf:master
Choose a base branch
from
yoloyyh:feat/container-uprobe-support
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
4596fac
feat: complete container process event collection
effa79c
fix(frontend): handle http_parser SSL events in adaptSSLEvent
139e52f
fix(bpf): read full argv using bpf_probe_read_user instead of _str
a08a32b
Merge pull request #1 from yoloyyh/feat/container-uprobe-support
yoloyyh 30edc73
fix(bpf): add bitmask constraint for verifier on bpf_probe_read_user
fc92e95
Merge pull request #2 from yoloyyh/feat/container-uprobe-support
yoloyyh c25ee54
fix(bpf): move argv post-processing to userspace to fix verifier limit
c050083
Merge branch 'master' into feat/container-uprobe-support
yoloyyh 395335b
fix: address PR #39 review comments
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,3 +17,5 @@ record.log.* | |
| # Log files | ||
| *.log | ||
| docs/blog | ||
|
|
||
| node_modules/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ | ||
| /* container_info.h — Userspace helpers for container metadata. | ||
| * Provides ns_pid and container_id extraction from /proc. | ||
| * Shared by process_new.c and sslsniff.c. | ||
| */ | ||
| #ifndef __CONTAINER_INFO_H | ||
| #define __CONTAINER_INFO_H | ||
|
|
||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include <stdlib.h> | ||
| #include <sys/types.h> | ||
|
|
||
| /* Read namespace PID from /proc/<pid>/status NSpid line. | ||
| * Returns the innermost namespace PID, or -1 if not in a namespace. */ | ||
| static int get_ns_pid(pid_t host_pid) | ||
| { | ||
| char path[64]; | ||
|
yoloyyh marked this conversation as resolved.
|
||
| char line[256]; | ||
| snprintf(path, sizeof(path), "/proc/%d/status", host_pid); | ||
| FILE *f = fopen(path, "r"); | ||
| if (!f) | ||
| return -1; | ||
| int ns_pid = -1; | ||
| while (fgets(line, sizeof(line), f)) { | ||
| if (strncmp(line, "NSpid:", 6) == 0) { | ||
| char *p = line + 6; | ||
| int last_pid = -1; | ||
| int count = 0; | ||
| while (*p) { | ||
| while (*p == '\t' || *p == ' ') | ||
| p++; | ||
| if (*p >= '0' && *p <= '9') { | ||
| last_pid = (int)strtol(p, &p, 10); | ||
| count++; | ||
| } else { | ||
| break; | ||
| } | ||
| } | ||
| if (count >= 2) | ||
| ns_pid = last_pid; | ||
| break; | ||
| } | ||
| } | ||
| fclose(f); | ||
| return ns_pid; | ||
| } | ||
|
|
||
| /* Read container ID from /proc/<pid>/cgroup (docker/containerd format). | ||
| * Writes short (12-char) container ID to out. Returns 0 on success. */ | ||
| static int get_container_id(pid_t host_pid, char *out, size_t out_len) | ||
| { | ||
| char path[64]; | ||
| char line[512]; | ||
| snprintf(path, sizeof(path), "/proc/%d/cgroup", host_pid); | ||
| FILE *f = fopen(path, "r"); | ||
| if (!f) | ||
| return -1; | ||
| out[0] = '\0'; | ||
| while (fgets(line, sizeof(line), f)) { | ||
| char *p = line; | ||
| while (*p) { | ||
| int hex_len = 0; | ||
| char *start = p; | ||
| while ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f')) { | ||
| hex_len++; | ||
| p++; | ||
| } | ||
| if (hex_len == 64) { | ||
| int copy_len = (int)out_len - 1 < 12 ? (int)out_len - 1 : 12; | ||
| memcpy(out, start, copy_len); | ||
| out[copy_len] = '\0'; | ||
| fclose(f); | ||
| return 0; | ||
| } | ||
| if (hex_len == 0) | ||
| p++; | ||
| } | ||
| } | ||
| fclose(f); | ||
| return -1; | ||
| } | ||
|
|
||
| /* Print container JSON fields: ,\"ns_pid\":N,\"container_id\":\"xxx\" | ||
| * Prints nothing if not in a container. */ | ||
| static void print_container_fields(pid_t host_pid) | ||
| { | ||
| int ns_pid = get_ns_pid(host_pid); | ||
| if (ns_pid > 0) { | ||
| printf(",\"ns_pid\":%d", ns_pid); | ||
| char container_id[16]; | ||
| if (get_container_id(host_pid, container_id, sizeof(container_id)) == 0 && | ||
| container_id[0] != '\0') { | ||
| printf(",\"container_id\":\"%s\"", container_id); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #endif /* __CONTAINER_INFO_H */ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) | ||
| // Container-aware library path resolution for uprobe attachment. | ||
| // Resolves SSL library paths inside container mount namespaces | ||
| // via /proc/<pid>/maps and /proc/<pid>/root/. | ||
|
|
||
| #ifndef __CONTAINER_UTILS_H | ||
| #define __CONTAINER_UTILS_H | ||
|
|
||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <unistd.h> | ||
| #include <dirent.h> | ||
| #include <limits.h> | ||
| #include <errno.h> | ||
| #include <stdbool.h> | ||
| #include <sys/types.h> | ||
| #include <bpf/libbpf.h> | ||
|
|
||
| #ifndef PATH_MAX | ||
| #define PATH_MAX 4096 | ||
| #endif | ||
|
|
||
| #define MAX_CONTAINER_LIBS 64 | ||
|
|
||
| /* ---------- data structures ---------- */ | ||
|
|
||
| struct pid_lib_entry { | ||
| pid_t pid; | ||
| char lib_path[PATH_MAX]; /* host-perspective path */ | ||
| }; | ||
|
|
||
| struct dynamic_links { | ||
| struct bpf_link **links; | ||
| int count; | ||
| int capacity; | ||
| }; | ||
|
|
||
| /* ---------- dynamic link management ---------- */ | ||
|
|
||
| static struct dynamic_links g_extra_links = { | ||
| .links = NULL, .count = 0, .capacity = 0 | ||
| }; | ||
|
|
||
| static void add_dynamic_link(struct bpf_link *link) | ||
| { | ||
| if (!link) | ||
| return; | ||
| if (g_extra_links.count >= g_extra_links.capacity) { | ||
| int new_cap = g_extra_links.capacity ? g_extra_links.capacity * 2 : 16; | ||
| struct bpf_link **tmp = realloc(g_extra_links.links, | ||
| new_cap * sizeof(struct bpf_link *)); | ||
| if (!tmp) { | ||
| fprintf(stderr, "realloc dynamic_links failed\n"); | ||
| return; | ||
| } | ||
| g_extra_links.links = tmp; | ||
| g_extra_links.capacity = new_cap; | ||
| } | ||
| g_extra_links.links[g_extra_links.count++] = link; | ||
| } | ||
|
|
||
| static void cleanup_dynamic_links(void) | ||
| { | ||
| for (int i = 0; i < g_extra_links.count; i++) | ||
| bpf_link__destroy(g_extra_links.links[i]); | ||
| free(g_extra_links.links); | ||
| g_extra_links.links = NULL; | ||
| g_extra_links.count = 0; | ||
| g_extra_links.capacity = 0; | ||
| } | ||
|
|
||
| /* ---------- container library path resolution ---------- */ | ||
|
|
||
| /* | ||
| * Find the host-perspective path of a library loaded by a specific PID. | ||
| * | ||
| * For container processes the in-container path (e.g. /usr/lib/libssl.so.3) | ||
| * is translated to /proc/<pid>/root/usr/lib/libssl.so.3 so that | ||
| * bpf_program__attach_uprobe_opts() can locate the correct ELF on the host | ||
| * filesystem. | ||
| */ | ||
| static char *find_library_path_for_pid(pid_t pid, const char *libname, | ||
| bool verbose) | ||
| { | ||
| static char host_path[PATH_MAX]; | ||
| char maps_path[64]; | ||
| char line[4096]; | ||
| FILE *fp; | ||
|
|
||
| snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps", pid); | ||
| fp = fopen(maps_path, "r"); | ||
| if (!fp) { | ||
| if (verbose) | ||
| fprintf(stderr, "Failed to open %s: %s\n", | ||
| maps_path, strerror(errno)); | ||
| return NULL; | ||
| } | ||
|
|
||
| while (fgets(line, sizeof(line), fp)) { | ||
| /* only consider executable/read-only mappings with matching lib name */ | ||
| if (strstr(line, libname) == NULL) | ||
| continue; | ||
| if (strstr(line, "r-xp") == NULL && strstr(line, "r--p") == NULL) | ||
| continue; | ||
|
|
||
| char *path = strchr(line, '/'); | ||
| if (!path) | ||
| continue; | ||
|
|
||
| char *nl = strchr(path, '\n'); | ||
| if (nl) | ||
| *nl = '\0'; | ||
|
|
||
| /* convert to host-perspective path via /proc/<pid>/root */ | ||
| snprintf(host_path, sizeof(host_path), "/proc/%d/root%s", pid, path); | ||
|
|
||
| if (access(host_path, R_OK) == 0) { | ||
| fclose(fp); | ||
| if (verbose) | ||
| fprintf(stderr, | ||
| "Found %s for PID %d: %s -> %s\n", | ||
| libname, pid, path, host_path); | ||
| return host_path; | ||
| } | ||
| } | ||
|
|
||
| fclose(fp); | ||
| return NULL; | ||
| } | ||
|
|
||
| /* | ||
| * Scan /proc for all processes that have loaded `libname`, returning | ||
| * deduplicated (by host path) entries. | ||
| */ | ||
| static int find_pids_with_library(const char *libname, | ||
| struct pid_lib_entry *entries, | ||
| int max_entries, | ||
| bool verbose) | ||
| { | ||
| DIR *proc_dir; | ||
| struct dirent *ent; | ||
| int count = 0; | ||
|
|
||
| /* Heap-allocated dedup array to avoid 256KB stack allocation */ | ||
| char (*seen)[PATH_MAX] = calloc(MAX_CONTAINER_LIBS, PATH_MAX); | ||
| int seen_count = 0; | ||
|
|
||
| if (!seen) { | ||
| fprintf(stderr, "Failed to allocate dedup buffer\n"); | ||
| return 0; | ||
| } | ||
|
|
||
| proc_dir = opendir("/proc"); | ||
| if (!proc_dir) { | ||
| free(seen); | ||
| return 0; | ||
| } | ||
|
|
||
| while ((ent = readdir(proc_dir)) && count < max_entries) { | ||
| pid_t pid = atoi(ent->d_name); | ||
| if (pid <= 0) | ||
| continue; | ||
|
|
||
| char *path = find_library_path_for_pid(pid, libname, false); | ||
| if (!path) | ||
| continue; | ||
|
|
||
| /* dedup */ | ||
| bool dup = false; | ||
| for (int i = 0; i < seen_count; i++) { | ||
| if (strcmp(seen[i], path) == 0) { | ||
| dup = true; | ||
| break; | ||
| } | ||
| } | ||
| if (dup) | ||
| continue; | ||
|
|
||
| if (seen_count < MAX_CONTAINER_LIBS) { | ||
| strncpy(seen[seen_count], path, PATH_MAX - 1); | ||
| seen[seen_count][PATH_MAX - 1] = '\0'; | ||
| seen_count++; | ||
| } | ||
|
|
||
| entries[count].pid = pid; | ||
| strncpy(entries[count].lib_path, path, PATH_MAX - 1); | ||
| entries[count].lib_path[PATH_MAX - 1] = '\0'; | ||
| count++; | ||
|
|
||
| if (verbose) | ||
| fprintf(stderr, "Discovered container SSL lib: %s (via PID %d)\n", | ||
| path, pid); | ||
| } | ||
|
|
||
| closedir(proc_dir); | ||
| free(seen); | ||
| return count; | ||
| } | ||
|
|
||
| #endif /* __CONTAINER_UTILS_H */ |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.