Skip to content

Commit

Permalink
feat(capture): Enrich capture output with user information (#230)
Browse files Browse the repository at this point in the history
* feat(capture): Enrich capture output with user information

* fill user info for the running process and fix case for uid == 0

* fix read from pcapng
  • Loading branch information
mozillazg authored Jan 18, 2025
1 parent c2bdce9 commit 3f9ca04
Show file tree
Hide file tree
Showing 25 changed files with 150 additions and 6 deletions.
2 changes: 2 additions & 0 deletions bpf/bpf_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified bpf/bpf_arm64_bpfel.o
Binary file not shown.
Binary file modified bpf/bpf_legacy_arm64_bpfel.o
Binary file not shown.
Binary file modified bpf/bpf_legacy_x86_bpfel.o
Binary file not shown.
Binary file modified bpf/bpf_no_tracing_arm64_bpfel.o
Binary file not shown.
Binary file modified bpf/bpf_no_tracing_x86_bpfel.o
Binary file not shown.
2 changes: 2 additions & 0 deletions bpf/bpf_x86_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified bpf/bpf_x86_bpfel.o
Binary file not shown.
12 changes: 12 additions & 0 deletions bpf/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ struct process_meta_t {
u32 mntns_id;
u32 netns_id;
u32 tid;
u32 uid;
u32 gid;
char tname[TASK_COMM_LEN];
char cgroup_name[MAX_CGROUP_NAME_LEN];
};
Expand Down Expand Up @@ -251,6 +253,14 @@ static __always_inline void fill_process_meta(struct task_struct *task, struct p
BPF_CORE_READ_INTO(&meta->pid, task, tgid);
BPF_CORE_READ_INTO(&meta->ppid, task, real_parent, tgid);

// u64 uid_gid = bpf_get_current_uid_gid();
// meta->uid = uid_gid & 0xFFFFFFFF;
// meta->gid = uid_gid >> 32;
BPF_CORE_READ_INTO(&meta->uid, task, cred, uid);
BPF_CORE_READ_INTO(&meta->gid, task, cred, gid);

// debug_log("uid %lld, gid %lld\n", meta->uid, meta->gid);

const char *cname = BPF_CORE_READ(task, cgroups, subsys[0], cgroup, kn, name);
int size = bpf_core_read_str(&meta->cgroup_name, sizeof(meta->cgroup_name), cname);
if (size < MIN_CGROUP_NAME_LEN) {
Expand Down Expand Up @@ -368,6 +378,8 @@ static __always_inline void clone_process_meta(struct process_meta_t *origin, st
target->mntns_id = origin->mntns_id;
target->netns_id = origin->netns_id;
target->pidns_id = origin->pidns_id;
target->uid = origin->uid;
target->gid = origin->gid;
__builtin_memcpy(&target->cgroup_name, &origin->cgroup_name, sizeof(origin->cgroup_name));
}

Expand Down
6 changes: 5 additions & 1 deletion cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
extPcap = ".pcap"
extPcapNG = ".pcapng"

contextUser = "user"
contextProcess = "process"
contextThread = "thread"
contextParentProc = "parentproc"
Expand Down Expand Up @@ -155,14 +156,17 @@ func prepareOptions(opts *Options, rawArgs []string, args []string) {

if len(opts.enhancedContexts) == 0 {
opts.enhancedContext = types.EnhancedContextProcess | types.EnhancedContextParentProc |
types.EnhancedContextContainer | types.EnhancedContextPod | types.EnhancedContextThread
types.EnhancedContextContainer | types.EnhancedContextPod |
types.EnhancedContextThread | types.EnhancedContextUser
}
for _, c := range opts.enhancedContexts {
switch c {
case contextProcess:
opts.enhancedContext |= types.EnhancedContextProcess
case contextThread:
opts.enhancedContext |= types.EnhancedContextThread
case contextUser:
opts.enhancedContext |= types.EnhancedContextUser
case contextParentProc:
opts.enhancedContext |= types.EnhancedContextParentProc
case contextContainer:
Expand Down
15 changes: 15 additions & 0 deletions cmd/read_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ func TestFormat(t *testing.T) {
},
expectedOutFile: "../testdata/format/curl-thread.pcapng.-v.out.txt",
},
{
name: "user",
opts: &Options{
readFilePath: "../testdata/format/curl-user.pcapng",
},
expectedOutFile: "../testdata/format/curl-user.pcapng.out.txt",
},
{
name: "user -v",
opts: &Options{
readFilePath: "../testdata/format/curl-user.pcapng",
verbose: 1,
},
expectedOutFile: "../testdata/format/curl-user.pcapng.-v.out.txt",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ func init() {
rootCmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false,
"Quiet output. Print less protocol information so output lines are shorter")
rootCmd.Flags().StringSliceVar(&opts.enhancedContexts, "context",
[]string{contextProcess, contextThread, contextParentProc, contextContainer, contextPod},
[]string{contextProcess, contextThread, contextParentProc, contextUser,
contextContainer, contextPod},
"Specify which context information to include in the output")
rootCmd.Flags().StringVar(&opts.backend, "backend", string(types.NetHookBackendTc),
"Specify the backend to use for capturing packets. "+
Expand Down
8 changes: 6 additions & 2 deletions internal/event/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Packet struct {
Device types.Device
Pid int
Tid int
Uid int
Gid int
TName string
MntNs int
NetNs int
Expand All @@ -53,14 +55,16 @@ func ParsePacketEvent(deviceCache *metadata.DeviceCache, event bpf.BpfPacketEven
}
p.Pid = int(event.Meta.Process.Pid)
p.Tid = int(event.Meta.Process.Tid)
p.Uid = int(event.Meta.Process.Uid)
p.Gid = int(event.Meta.Process.Gid)
p.TName = utils.GoString(event.Meta.Process.Tname[:])
p.MntNs = int(event.Meta.Process.MntnsId)
p.NetNs = int(event.Meta.Process.NetnsId)
p.CgroupName = utils.GoString(event.Meta.Process.CgroupName[:])
p.Device, _ = deviceCache.GetByIfindex(int(event.Meta.Ifindex), event.Meta.Process.NetnsId)

log.Infof("new packet event, thread: %s.%d, pid: %d mntns: %d, netns: %d, cgroupName: %s",
p.TName, p.Tid, p.Pid, p.MntNs, p.NetNs, p.CgroupName)
log.Infof("new packet event, thread: %s.%d, pid: %d, uid: %d, gid: %d, mntns: %d, netns: %d, cgroupName: %s",
p.TName, p.Tid, p.Pid, p.Uid, p.Gid, p.MntNs, p.NetNs, p.CgroupName)

p.L3Protocol = event.Meta.L3Protocol
p.FirstLayer = firstLayerType(event.Meta.FirstLayer)
Expand Down
11 changes: 10 additions & 1 deletion internal/event/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,21 @@ func ParseProcessExecEvent(event bpf.BpfExecEventT) (*types.ProcessExec, error)

func FromPacketOptions(opts pcapgo.NgPacketOptions) (types.ProcessExec, types.PacketContext) {
p := &types.ProcessExec{}
pctx := &types.PacketContext{}
pctx := &types.PacketContext{
Process: types.Process{
ProcessBase: types.ProcessBase{
UserId: -1,
GroupId: -1,
},
},
}

pctx.FromPacketComments(opts.Comments)
p.PPid = pctx.Parent.Pid
p.Pid = pctx.Pid
p.Tid = pctx.Tid
p.Uid = pctx.UserId
p.Gid = pctx.GroupId
p.TName = pctx.TName
p.Filename = pctx.Cmd
p.FilenameTruncated = pctx.CmdTruncated
Expand Down
21 changes: 21 additions & 0 deletions internal/metadata/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (c *ProcessCache) fillRunningProcesses(ctx context.Context) error {
<-pool
wg.Done()
}()

ppid := 0
if parent, err := p.ParentWithContext(ctx); err == nil {
ppid = int(parent.Pid)
Expand All @@ -91,9 +92,20 @@ func (c *ProcessCache) fillRunningProcesses(ctx context.Context) error {
filename, _ = p.Name()
}
args, _ := p.CmdlineSlice()
uid := -1
gid := -1
if uids, _ := p.Uids(); len(uids) > 0 {
uid = int(uids[0])
}
if gids, _ := p.Gids(); len(gids) > 0 {
gid = int(gids[0])
}

e := types.ProcessExec{
PPid: ppid,
Pid: int(p.Pid),
Uid: uid,
Gid: gid,
Filename: filename,
FilenameTruncated: false,
Args: args,
Expand Down Expand Up @@ -187,6 +199,11 @@ func (c *ProcessCache) AddItemWithContext(exec types.ProcessExec, rawCtx types.P
ProcessBase: types.ProcessBase{
Pid: exec.Pid,
Cmd: exec.FilenameStr(),
CmdTruncated: false,
Tid: 0,
TName: "",
UserId: exec.Uid,
GroupId: exec.Gid,
Args: exec.Args,
ArgsTruncated: exec.ArgsTruncated,
},
Expand Down Expand Up @@ -235,6 +252,10 @@ func (c *ProcessCache) getProcessBase(pid int) types.ProcessBase {
Pid: pid,
Cmd: cmd,
CmdTruncated: false,
Tid: 0,
TName: "",
UserId: -1,
GroupId: -1,
Args: args,
ArgsTruncated: false,
}
Expand Down
2 changes: 2 additions & 0 deletions internal/parser/pcapng.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ func (p *PcapNGParser) Parse() (*event.Packet, error) {
exec, ctx := event.FromPacketOptions(opts)
e.Pid = exec.Pid
e.Tid = exec.Tid
e.Uid = exec.Uid
e.Gid = exec.Gid
e.TName = exec.TName
p.pcache.AddItemWithContext(exec, ctx)

Expand Down
4 changes: 4 additions & 0 deletions internal/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func (c *PacketContext) FromPacketComments(comments []string) {
c.Tid, _ = strconv.Atoi(value)
case "ThreadName":
c.TName = value
case "UserId":
c.UserId, _ = strconv.Atoi(value)
case "GroupId":
c.GroupId, _ = strconv.Atoi(value)
case "ParentPID":
c.Parent.Pid, _ = strconv.Atoi(value)
case "Command", "Cmd", "ParentCommand", "ParentCmd":
Expand Down
5 changes: 5 additions & 0 deletions internal/types/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ const (
EnhancedContextContainer
EnhancedContextPod
EnhancedContextThread
EnhancedContextUser
)

func (c EnhancedContext) ProcessContext() bool {
return c == 0 || c&EnhancedContextProcess != 0
}

func (c EnhancedContext) UserContext() bool {
return c == 0 || c&EnhancedContextUser != 0
}

func (c EnhancedContext) ThreadContext() bool {
return c == 0 || c&EnhancedContextThread != 0
}
Expand Down
5 changes: 5 additions & 0 deletions internal/types/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type ProcessBase struct {
CmdTruncated bool
Tid int
TName string
UserId int
GroupId int

Args []string
ArgsTruncated bool
Expand All @@ -36,6 +38,9 @@ type ProcessExec struct {
Tid int
TName string

Uid int
Gid int

Filename string
FilenameTruncated bool

Expand Down
12 changes: 12 additions & 0 deletions internal/writer/pcapng.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ func (w *PcapNGWriter) Write(e *event.Packet) error {
p := w.pcache.Get(e.Pid, e.MntNs, e.NetNs, e.CgroupName)
p.Tid = e.Tid
p.TName = e.TName
if p.UserId == 0 && e.Uid != 0 {
p.UserId = e.Uid
}
if p.GroupId == 0 && e.Gid != 0 {
p.GroupId = e.Gid
}

opts := pcapgo.NgPacketOptions{}
if w.enhancedContext.ProcessContext() && p.Pid > 0 {
Expand All @@ -62,6 +68,12 @@ func (w *PcapNGWriter) Write(e *event.Packet) error {
p.Tid, p.TName),
)
}
if w.enhancedContext.UserContext() && p.UserId >= 0 {
opts.Comments = append(opts.Comments,
fmt.Sprintf("UserId: %d\nGroupId: %d",
p.UserId, p.GroupId),
)
}
if w.enhancedContext.ParentProcContext() && p.Parent.Pid > 0 {
opts.Comments = append(opts.Comments,
fmt.Sprintf("ParentPID: %d\nParentCmd: %s\nParentArgs: %s",
Expand Down
16 changes: 16 additions & 0 deletions internal/writer/stdout.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,16 @@ func (w *StdoutWriter) Write(e *event.Packet) error {
p := w.pcache.Get(e.Pid, e.MntNs, e.NetNs, e.CgroupName)
p.Tid = e.Tid
p.TName = e.TName
if p.UserId == 0 && e.Uid != 0 {
p.UserId = e.Uid
}
if p.GroupId == 0 && e.Gid != 0 {
p.GroupId = e.Gid
}

processInfo := ""
threadInfo := ""
userInfo := ""
parentProcInfo := ""
containerInfo := ""
PodInfo := ""
Expand All @@ -77,6 +84,9 @@ func (w *StdoutWriter) Write(e *event.Packet) error {
if w.enhancedContext.ProcessContext() && p.Tid > 0 {
threadInfo = fmt.Sprintf("Thread (tid %d, name %s)", p.Tid, p.TName)
}
if w.enhancedContext.UserContext() && p.UserId >= 0 {
userInfo = fmt.Sprintf("User (uid %d, gid %d)", p.UserId, p.GroupId)
}
if w.enhancedContext.ParentProcContext() && p.Parent.Pid > 0 {
parentProcInfo = fmt.Sprintf("ParentProc (pid %d, cmd %s, args %s)",
p.Parent.Pid, p.Parent.Cmd, p.Parent.FormatArgs())
Expand Down Expand Up @@ -151,6 +161,9 @@ func (w *StdoutWriter) Write(e *event.Packet) error {
if threadInfo != "" {
builder.WriteString(fmt.Sprintf(" %s\n", threadInfo))
}
if userInfo != "" {
builder.WriteString(fmt.Sprintf(" %s\n", userInfo))
}
if parentProcInfo != "" {
builder.WriteString(fmt.Sprintf(" %s\n", parentProcInfo))
}
Expand All @@ -167,6 +180,9 @@ func (w *StdoutWriter) Write(e *event.Packet) error {
if threadInfo != "" {
builder.WriteString(fmt.Sprintf(", %s", threadInfo))
}
if userInfo != "" {
builder.WriteString(fmt.Sprintf(", %s", userInfo))
}
if parentProcInfo != "" {
builder.WriteString(fmt.Sprintf(", %s", parentProcInfo))
}
Expand Down
Binary file added testdata/format/curl-user.pcapng
Binary file not shown.
25 changes: 25 additions & 0 deletions testdata/format/curl-user.pcapng.-v.out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
14:40:20.276349 ens33 Out IP (tos 0x0, ttl 64, id 54926, offset 0, flags [DF], proto TCP (6), length 60)
10.0.2.15.43950 > 139.178.84.217.443: Flags [S], cksum 0xecc8, seq 2716706921, win 64240, options [mss 1460,sackOK,TS val 3379641114 ecr 0,nop,wscale 7], length 0
Process (pid 23592, cmd /usr/bin/curl, args curl https://kernel.org)
User (uid 1000, gid 1000)
ParentProc (pid 6007, cmd /usr/bin/bash, args -bash)
14:40:20.780518 ens33 In IP (tos 0x0, ttl 128, id 7878, offset 0, flags [none], proto TCP (6), length 44)
139.178.84.217.443 > 10.0.2.15.43950: Flags [S.], cksum 0x65a7, seq 676670157, ack 2716706922, win 64240, options [mss 1460], length 0
Process (pid 23592, cmd /usr/bin/curl, args curl https://kernel.org)
User (uid 1000, gid 1000)
ParentProc (pid 6007, cmd /usr/bin/bash, args -bash)
14:40:20.780771 ens33 Out IP (tos 0x0, ttl 64, id 54927, offset 0, flags [DF], proto TCP (6), length 40)
10.0.2.15.43950 > 139.178.84.217.443: Flags [.], cksum 0xecb4, seq 2716706922, ack 676670158, win 64240, length 0
Process (pid 23592, cmd /usr/bin/curl, args curl https://kernel.org)
User (uid 1000, gid 1000)
ParentProc (pid 6007, cmd /usr/bin/bash, args -bash)
14:40:20.790480 ens33 In IP (tos 0x0, ttl 128, id 7880, offset 0, flags [none], proto TCP (6), length 40)
139.178.84.217.443 > 10.0.2.15.43950: Flags [.], cksum 0x7b5f, seq 676670158, ack 2716707439, win 64240, length 0
Process (pid 23592, cmd /usr/bin/curl, args curl https://kernel.org)
User (uid 1000, gid 1000)
ParentProc (pid 6007, cmd /usr/bin/bash, args -bash)
14:40:20.790422 ens33 Out IP (tos 0x0, ttl 64, id 54928, offset 0, flags [DF], proto TCP (6), length 557)
10.0.2.15.43950 > 139.178.84.217.443: Flags [P.], cksum 0xeeb9, seq 2716706922:2716707439, ack 676670158, win 64240, length 517
Process (pid 23592, cmd /usr/bin/curl, args curl https://kernel.org)
User (uid 1000, gid 1000)
ParentProc (pid 6007, cmd /usr/bin/bash, args -bash)
5 changes: 5 additions & 0 deletions testdata/format/curl-user.pcapng.out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
14:40:20.276349 ens33 curl.23592 Out IP 10.0.2.15.43950 > 139.178.84.217.443: Flags [S], seq 2716706921, win 64240, options [mss 1460,sackOK,TS val 3379641114 ecr 0,nop,wscale 7], length 0, ParentProc [bash.6007]
14:40:20.780518 ens33 curl.23592 In IP 139.178.84.217.443 > 10.0.2.15.43950: Flags [S.], seq 676670157, ack 2716706922, win 64240, options [mss 1460], length 0, ParentProc [bash.6007]
14:40:20.780771 ens33 curl.23592 Out IP 10.0.2.15.43950 > 139.178.84.217.443: Flags [.], seq 2716706922, ack 676670158, win 64240, length 0, ParentProc [bash.6007]
14:40:20.790480 ens33 curl.23592 In IP 139.178.84.217.443 > 10.0.2.15.43950: Flags [.], seq 676670158, ack 2716707439, win 64240, length 0, ParentProc [bash.6007]
14:40:20.790422 ens33 curl.23592 Out IP 10.0.2.15.43950 > 139.178.84.217.443: Flags [P.], seq 2716706922:2716707439, ack 676670158, win 64240, length 517, ParentProc [bash.6007]
2 changes: 1 addition & 1 deletion testdata/test_run_with_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export TMP="/tmp/"
function test_ptcpdump() {
timeout 60s ${CMD} -c 1 -v -i any --print -w "${FNAME}" \
'dst host 1.1.1.1 and tcp[tcpflags] = tcp-syn' | tee "${LNAME}" &
sleep 10
sleep 30
curl -m 10 1.1.1.1 &>/dev/null || true
wait

Expand Down

0 comments on commit 3f9ca04

Please sign in to comment.