exectrace

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2024 License: GPL-2.0, MIT, MIT Imports: 21 Imported by: 1

README

exectrace Go Reference

Simple eBPF-based exec snooping on Linux packaged as a Go library.

exectrace loads a pre-compiled eBPF program into the running kernel to receive details about the exec family of syscalls.

Coder

exectrace provides workspace process logging for Coder v1 and Coder v2 (aka. Coder OSS).

Documentation for how to setup workspace process logging for Coder v1 users can be found here.

Documentation for Coder v2 users can be found in enterprise/README.md.

Requirements

exectrace only supports Go 1.16+ and Linux kernel 5.8+ (due to the use of BPF_MAP_TYPE_RINGBUF). Additionally, the kernel config CONFIG_DEBUG_INFO_BTF=y is required.

To validate this config is enabled, run either of the following commands directly on the system:

$ cat /proc/config.gz | gunzip | grep CONFIG_DEBUG_INFO_BTF
$ cat "/boot/config-$(uname -r)" | grep CONFIG_DEBUG_INFO_BTF

Installation

$ go get -u github.com/coder/exectrace

Quickstart

You will need root access, CAP_SYS_ADMIN or CAP_BPF, to run eBPF programs on your system.

Use go run -exec sudo ./cmd/program to compile a program and start it with sudo

$ go install -u github.com/coder/exectrace/cmd/exectrace
$ exectrace --help
...

$ sudo exectrace
2021/12/01 16:42:02 Waiting for events..
[1188921, comm="node", uid=1002, gid=1003, filename=/bin/sh] /bin/sh -c 'which ps'
[1188922, comm="sh", uid=1002, gid=1003, filename=/usr/bin/which] which ps

Usage

exectrace exposes a minimal API surface. Call exectrace.New(nil) and then you can start reading events from the returned Tracer.

It is important that you close the tracer to avoid leaking kernel resources, so we recommend implementing a simple signal handler like the one in this example:

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/coder/exectrace"
)

func main() {
	tracer, err := exectrace.New(nil)
	if err != nil {
		panic(err)
	}
	defer tracer.Close()

	go func() {
		sigs := make(chan os.Signal, 1)
		signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
		<-sigs
		tracer.Close()
	}()

	for {
		event, err := tracer.Read()
		if err != nil {
			panic(err)
		}

		fmt.Printf("%+v\n", event)
	}
}

For a full usage example, refer to this comprehensive program that uses the library.

Development

You will need the following:

  • Docker (the Makefile runs clang within a Docker container for reproducibility)
  • Golang 1.20+
  • golangci-lint
  • prettier
  • shellcheck

Since the eBPF program is packaged using go:embed, you will need to compile the program and include it in the repo.

If you change the files in the bpf directory, run make and ensure that you include the .o files you changed in your commit (CI will verify that you've done this correctly).

Status: stable

This library is ready to use as-is. It has been used in production for years and has received minimal maintenance over that time period.

In April 2024, a system to send logs from the kernel to userspace was added which can make discovering potential issues in production/development much easier.

The API will likely not be further modified as we have no need for additional fields/features. We will continue to maintain the library as needed.

See also

  • canonical/etrace - Go binary that uses ptrace and tracks the processes that a command launches for debugging and analysis
  • shirou/gopsutil - Go library that has methods for listing process details and getting information about the system

Dual licensed under the MIT and GPL 2.0 licenses. See LICENSE.

Code in the enterprise directory has a different license. See LICENSE.enterprise.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var NativeEndian = binary.LittleEndian

The native endian of the processor this program was compiled for.

Functions

func GetPidNS added in v1.0.0

func GetPidNS() (uint32, error)

GetPidNS returns the inum of the PidNS used by the current process.

Types

type Event

type Event struct {
	Filename string `json:"filename"`
	// Argv contains the raw argv supplied to the process, including argv[0]
	// (which is equal to `filepath.Base(e.Filename)` in most circumstances).
	Argv []string `json:"argv"`
	// Truncated is true if we were unable to read all process arguments into
	// Argv because there were more than 32 arguments, or if one of the
	// arguments was greater than or equal to 1023 bytes in length.
	//
	// It may indicate that the user or process is trying to hide arguments from
	// the tracer.
	Truncated bool `json:"truncated"`

	// These values are of the new process. Keep in mind that the exec call may
	// fail and the PID will be released in such a case.
	PID uint32 `json:"pid"`
	UID uint32 `json:"uid"`
	GID uint32 `json:"gid"`

	// Comm is the "name" of the parent process, usually the filename of the
	// executable (but not always).
	Comm string `json:"comm"`
}

Event contains data about each exec event with many fields for easy filtering and logging.

type Tracer

type Tracer interface {
	io.Closer

	// Read blocks until an exec event is available, then returns it.
	Read() (*Event, error)

	// FD returns the FD of the loaded eBPF program. This is useful for
	// benchmarking.
	FD() int
}

Tracer allows consumers to read exec events from the kernel via an eBPF program. `execve()` syscalls are traced in the kernel, and details about the event are sent back to this Go interface.

func New

func New(opts *TracerOpts) (Tracer, error)

New instantiates all of the BPF objects into the running kernel, starts tracing, and returns the created Tracer. After calling this successfully, the caller should immediately attach a for loop running `h.Read()`.

The returned Tracer MUST be closed to avoid leaking kernel resources.

type TracerOpts

type TracerOpts struct {
	// PidNS filters all processes that are in the given PID namespace or in the
	// child namespace tree of this given namespace. This is very useful for
	// Docker containers, as you can read all processes in a container (or in
	// child containers).
	//
	// You can read the PID namespace ID for a given process by running
	// `readlink /proc/x/ns/pid`.
	//
	// This filter runs in the kernel for high performance.
	PidNS uint32

	// LogFn is called for each log line that is read from the kernel. All logs
	// are considered error logs unless running a debug version of the eBPF
	// program.
	//
	// If unspecified, a default log function is used that logs to stderr.
	LogFn func(uid, gid, pid uint32, logLine string)
}

TracerOpts contains all of the configuration options for the tracer. All are optional.

Directories

Path Synopsis
cmd
enterprise module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL