add-bpf-capability (#1)
1) add-bpf-capability 2) Not so clean but for now it's okay to start with Co-authored-by: Harshavardhan Musanalli <harshavmb@gmail.com> Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
387
ebpf_simple_manager.go
Normal file
387
ebpf_simple_manager.go
Normal file
@@ -0,0 +1,387 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EBPFEvent represents an event captured by eBPF programs
|
||||
type EBPFEvent struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
EventType string `json:"event_type"`
|
||||
ProcessID int `json:"process_id"`
|
||||
ProcessName string `json:"process_name"`
|
||||
UserID int `json:"user_id"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// EBPFTrace represents a collection of eBPF events for a specific investigation
|
||||
type EBPFTrace struct {
|
||||
TraceID string `json:"trace_id"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Capability string `json:"capability"`
|
||||
Events []EBPFEvent `json:"events"`
|
||||
Summary string `json:"summary"`
|
||||
EventCount int `json:"event_count"`
|
||||
ProcessList []string `json:"process_list"`
|
||||
}
|
||||
|
||||
// EBPFRequest represents a request to run eBPF monitoring
|
||||
type EBPFRequest struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"` // "tracepoint", "kprobe", "kretprobe"
|
||||
Target string `json:"target"` // tracepoint path or function name
|
||||
Duration int `json:"duration"` // seconds
|
||||
Filters map[string]string `json:"filters,omitempty"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// EBPFManagerInterface defines the interface for eBPF managers
|
||||
type EBPFManagerInterface interface {
|
||||
GetCapabilities() map[string]bool
|
||||
GetSummary() map[string]interface{}
|
||||
StartEBPFProgram(req EBPFRequest) (string, error)
|
||||
GetProgramResults(programID string) (*EBPFTrace, error)
|
||||
StopProgram(programID string) error
|
||||
ListActivePrograms() []string
|
||||
}
|
||||
|
||||
// SimpleEBPFManager implements basic eBPF functionality using bpftrace
|
||||
type SimpleEBPFManager struct {
|
||||
programs map[string]*RunningProgram
|
||||
programsLock sync.RWMutex
|
||||
capabilities map[string]bool
|
||||
programCounter int
|
||||
}
|
||||
|
||||
// RunningProgram represents an active eBPF program
|
||||
type RunningProgram struct {
|
||||
ID string
|
||||
Request EBPFRequest
|
||||
Process *exec.Cmd
|
||||
Events []EBPFEvent
|
||||
StartTime time.Time
|
||||
Cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// NewSimpleEBPFManager creates a new simple eBPF manager
|
||||
func NewSimpleEBPFManager() *SimpleEBPFManager {
|
||||
manager := &SimpleEBPFManager{
|
||||
programs: make(map[string]*RunningProgram),
|
||||
capabilities: make(map[string]bool),
|
||||
}
|
||||
|
||||
// Test capabilities
|
||||
manager.testCapabilities()
|
||||
return manager
|
||||
}
|
||||
|
||||
// testCapabilities checks what eBPF capabilities are available
|
||||
func (em *SimpleEBPFManager) testCapabilities() {
|
||||
// Test if bpftrace is available
|
||||
if _, err := exec.LookPath("bpftrace"); err == nil {
|
||||
em.capabilities["bpftrace"] = true
|
||||
}
|
||||
|
||||
// Test root privileges (required for eBPF)
|
||||
em.capabilities["root_access"] = os.Geteuid() == 0
|
||||
|
||||
// Test kernel version (simplified check)
|
||||
cmd := exec.Command("uname", "-r")
|
||||
output, err := cmd.Output()
|
||||
if err == nil {
|
||||
version := strings.TrimSpace(string(output))
|
||||
em.capabilities["kernel_ebpf"] = strings.Contains(version, "4.") || strings.Contains(version, "5.") || strings.Contains(version, "6.")
|
||||
} else {
|
||||
em.capabilities["kernel_ebpf"] = false
|
||||
}
|
||||
|
||||
log.Printf("eBPF capabilities: %+v", em.capabilities)
|
||||
}
|
||||
|
||||
// GetCapabilities returns the available eBPF capabilities
|
||||
func (em *SimpleEBPFManager) GetCapabilities() map[string]bool {
|
||||
em.programsLock.RLock()
|
||||
defer em.programsLock.RUnlock()
|
||||
|
||||
caps := make(map[string]bool)
|
||||
for k, v := range em.capabilities {
|
||||
caps[k] = v
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
||||
// GetSummary returns a summary of the eBPF manager state
|
||||
func (em *SimpleEBPFManager) GetSummary() map[string]interface{} {
|
||||
em.programsLock.RLock()
|
||||
defer em.programsLock.RUnlock()
|
||||
|
||||
return map[string]interface{}{
|
||||
"capabilities": em.capabilities,
|
||||
"active_programs": len(em.programs),
|
||||
"program_ids": em.ListActivePrograms(),
|
||||
}
|
||||
}
|
||||
|
||||
// StartEBPFProgram starts a new eBPF monitoring program
|
||||
func (em *SimpleEBPFManager) StartEBPFProgram(req EBPFRequest) (string, error) {
|
||||
if !em.capabilities["bpftrace"] {
|
||||
return "", fmt.Errorf("bpftrace not available")
|
||||
}
|
||||
|
||||
if !em.capabilities["root_access"] {
|
||||
return "", fmt.Errorf("root access required for eBPF programs")
|
||||
}
|
||||
|
||||
em.programsLock.Lock()
|
||||
defer em.programsLock.Unlock()
|
||||
|
||||
// Generate program ID
|
||||
em.programCounter++
|
||||
programID := fmt.Sprintf("prog_%d", em.programCounter)
|
||||
|
||||
// Create bpftrace script
|
||||
script, err := em.generateBpftraceScript(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate script: %w", err)
|
||||
}
|
||||
|
||||
// Start bpftrace process
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(req.Duration)*time.Second)
|
||||
cmd := exec.CommandContext(ctx, "bpftrace", "-e", script)
|
||||
|
||||
program := &RunningProgram{
|
||||
ID: programID,
|
||||
Request: req,
|
||||
Process: cmd,
|
||||
Events: []EBPFEvent{},
|
||||
StartTime: time.Now(),
|
||||
Cancel: cancel,
|
||||
}
|
||||
|
||||
// Start the program
|
||||
if err := cmd.Start(); err != nil {
|
||||
cancel()
|
||||
return "", fmt.Errorf("failed to start bpftrace: %w", err)
|
||||
}
|
||||
|
||||
em.programs[programID] = program
|
||||
|
||||
// Monitor the program in a goroutine
|
||||
go em.monitorProgram(programID)
|
||||
|
||||
log.Printf("Started eBPF program %s for %s", programID, req.Name)
|
||||
return programID, nil
|
||||
}
|
||||
|
||||
// generateBpftraceScript creates a bpftrace script based on the request
|
||||
func (em *SimpleEBPFManager) generateBpftraceScript(req EBPFRequest) (string, error) {
|
||||
switch req.Type {
|
||||
case "network":
|
||||
return `
|
||||
BEGIN {
|
||||
printf("Starting network monitoring...\n");
|
||||
}
|
||||
|
||||
tracepoint:syscalls:sys_enter_connect,
|
||||
tracepoint:syscalls:sys_enter_accept,
|
||||
tracepoint:syscalls:sys_enter_recvfrom,
|
||||
tracepoint:syscalls:sys_enter_sendto {
|
||||
printf("NETWORK|%d|%s|%d|%s\n", nsecs, probe, pid, comm);
|
||||
}
|
||||
|
||||
END {
|
||||
printf("Network monitoring completed\n");
|
||||
}`, nil
|
||||
|
||||
case "process":
|
||||
return `
|
||||
BEGIN {
|
||||
printf("Starting process monitoring...\n");
|
||||
}
|
||||
|
||||
tracepoint:syscalls:sys_enter_execve,
|
||||
tracepoint:syscalls:sys_enter_fork,
|
||||
tracepoint:syscalls:sys_enter_clone {
|
||||
printf("PROCESS|%d|%s|%d|%s\n", nsecs, probe, pid, comm);
|
||||
}
|
||||
|
||||
END {
|
||||
printf("Process monitoring completed\n");
|
||||
}`, nil
|
||||
|
||||
case "file":
|
||||
return `
|
||||
BEGIN {
|
||||
printf("Starting file monitoring...\n");
|
||||
}
|
||||
|
||||
tracepoint:syscalls:sys_enter_open,
|
||||
tracepoint:syscalls:sys_enter_openat,
|
||||
tracepoint:syscalls:sys_enter_read,
|
||||
tracepoint:syscalls:sys_enter_write {
|
||||
printf("FILE|%d|%s|%d|%s\n", nsecs, probe, pid, comm);
|
||||
}
|
||||
|
||||
END {
|
||||
printf("File monitoring completed\n");
|
||||
}`, nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported eBPF program type: %s", req.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// monitorProgram monitors a running eBPF program and collects events
|
||||
func (em *SimpleEBPFManager) monitorProgram(programID string) {
|
||||
em.programsLock.Lock()
|
||||
program, exists := em.programs[programID]
|
||||
if !exists {
|
||||
em.programsLock.Unlock()
|
||||
return
|
||||
}
|
||||
em.programsLock.Unlock()
|
||||
|
||||
// Wait for the program to complete
|
||||
err := program.Process.Wait()
|
||||
|
||||
// Clean up
|
||||
program.Cancel()
|
||||
|
||||
em.programsLock.Lock()
|
||||
if err != nil {
|
||||
log.Printf("eBPF program %s completed with error: %v", programID, err)
|
||||
} else {
|
||||
log.Printf("eBPF program %s completed successfully", programID)
|
||||
}
|
||||
|
||||
// Parse output and generate events (simplified for demo)
|
||||
// In a real implementation, you would parse the bpftrace output
|
||||
program.Events = []EBPFEvent{
|
||||
{
|
||||
Timestamp: time.Now().Unix(),
|
||||
EventType: program.Request.Type,
|
||||
ProcessID: 0,
|
||||
ProcessName: "example",
|
||||
UserID: 0,
|
||||
Data: map[string]interface{}{
|
||||
"description": "Sample eBPF event",
|
||||
"program_id": programID,
|
||||
},
|
||||
},
|
||||
}
|
||||
em.programsLock.Unlock()
|
||||
|
||||
log.Printf("Generated %d events for program %s", len(program.Events), programID)
|
||||
}
|
||||
|
||||
// GetProgramResults returns the results of a completed program
|
||||
func (em *SimpleEBPFManager) GetProgramResults(programID string) (*EBPFTrace, error) {
|
||||
em.programsLock.RLock()
|
||||
defer em.programsLock.RUnlock()
|
||||
|
||||
program, exists := em.programs[programID]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("program %s not found", programID)
|
||||
}
|
||||
|
||||
// Check if program is still running
|
||||
if program.Process.ProcessState == nil {
|
||||
return nil, fmt.Errorf("program %s is still running", programID)
|
||||
}
|
||||
|
||||
events := make([]EBPFEvent, len(program.Events))
|
||||
copy(events, program.Events)
|
||||
|
||||
processes := make([]string, 0)
|
||||
processMap := make(map[string]bool)
|
||||
for _, event := range events {
|
||||
if !processMap[event.ProcessName] {
|
||||
processes = append(processes, event.ProcessName)
|
||||
processMap[event.ProcessName] = true
|
||||
}
|
||||
}
|
||||
|
||||
trace := &EBPFTrace{
|
||||
TraceID: programID,
|
||||
StartTime: program.StartTime,
|
||||
EndTime: time.Now(),
|
||||
Capability: program.Request.Type,
|
||||
Events: events,
|
||||
EventCount: len(events),
|
||||
ProcessList: processes,
|
||||
Summary: fmt.Sprintf("Collected %d events for %s monitoring", len(events), program.Request.Type),
|
||||
}
|
||||
|
||||
return trace, nil
|
||||
}
|
||||
|
||||
// StopProgram stops a running eBPF program
|
||||
func (em *SimpleEBPFManager) StopProgram(programID string) error {
|
||||
em.programsLock.Lock()
|
||||
defer em.programsLock.Unlock()
|
||||
|
||||
program, exists := em.programs[programID]
|
||||
if !exists {
|
||||
return fmt.Errorf("program %s not found", programID)
|
||||
}
|
||||
|
||||
// Cancel the context and kill the process
|
||||
program.Cancel()
|
||||
if program.Process.Process != nil {
|
||||
program.Process.Process.Kill()
|
||||
}
|
||||
|
||||
delete(em.programs, programID)
|
||||
log.Printf("Stopped eBPF program %s", programID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListActivePrograms returns a list of active program IDs
|
||||
func (em *SimpleEBPFManager) ListActivePrograms() []string {
|
||||
em.programsLock.RLock()
|
||||
defer em.programsLock.RUnlock()
|
||||
|
||||
programs := make([]string, 0, len(em.programs))
|
||||
for id := range em.programs {
|
||||
programs = append(programs, id)
|
||||
}
|
||||
return programs
|
||||
}
|
||||
|
||||
// GetCommonEBPFRequests returns predefined eBPF programs for common use cases
|
||||
func (em *SimpleEBPFManager) GetCommonEBPFRequests() []EBPFRequest {
|
||||
return []EBPFRequest{
|
||||
{
|
||||
Name: "network_activity",
|
||||
Type: "network",
|
||||
Target: "syscalls:sys_enter_connect,sys_enter_accept,sys_enter_recvfrom,sys_enter_sendto",
|
||||
Duration: 30,
|
||||
Description: "Monitor network connections and data transfers",
|
||||
},
|
||||
{
|
||||
Name: "process_activity",
|
||||
Type: "process",
|
||||
Target: "syscalls:sys_enter_execve,sys_enter_fork,sys_enter_clone",
|
||||
Duration: 30,
|
||||
Description: "Monitor process creation and execution",
|
||||
},
|
||||
{
|
||||
Name: "file_access",
|
||||
Type: "file",
|
||||
Target: "syscalls:sys_enter_open,sys_enter_openat,sys_enter_read,sys_enter_write",
|
||||
Duration: 30,
|
||||
Description: "Monitor file system access and I/O operations",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions - using system_info.go functions
|
||||
// isRoot and checkKernelVersion are available from system_info.go
|
||||
Reference in New Issue
Block a user