diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c4a7f9..5d87035 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,13 +12,15 @@ repos: - id: check-json #- id: check-docstring-first # - id: check-xml - # - repo: https://github.com/dnephin/pre-commit-golang - # rev: v0.5.1 - # hooks: - # - id: go-fmt - # - id: go-imports - # - id: golangci-lint - # args: [--fix] + - repo: https://github.com/dnephin/pre-commit-golang + rev: v0.5.1 + hooks: + - id: go-fmt + # - id: go-imports + # - id: go-critic + - id: go-mod-tidy + - id: golangci-lint + args: [--fix] - repo: https://github.com/containerscrew/mtoc rev: v0.4.1 hooks: diff --git a/go.mod b/go.mod index 29c1a06..fb6c8c5 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,22 @@ require ( github.com/cilium/ebpf v0.16.0 github.com/containerscrew/devstdout v0.3.0 github.com/pelletier/go-toml v1.9.5 + github.com/prometheus/client_golang v1.20.4 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect + google.golang.org/protobuf v1.35.1 // indirect ) require ( diff --git a/go.sum b/go.sum index 812c07c..be277f2 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,13 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/containerscrew/devstdout v0.3.0 h1:WvL3ezp8Ul4B+BRRAP0+txcZWfzqlZZxq6gNiVrmNCo= github.com/containerscrew/devstdout v0.3.0/go.mod h1:qivX4vQCCaDLhLXXma2qzQMFAvF6xJQOqa4//2IDSSo= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 h1:ygs9POGDQpQGLJPlq4+0LBUmMBNox1N4JSpw+OETcvI= @@ -18,10 +24,14 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -31,21 +41,39 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/core/core.go b/internal/core/core.go index 516c9ce..124167f 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -4,10 +4,17 @@ import ( "context" "github.com/cilium/ebpf/rlimit" + "github.com/containerscrew/kernelsnoop/internal/dto" ) +// Retrieve context data (log and config) from the context +func GetContextData(ctx context.Context) *dto.ContextData { + contextData, _ := ctx.Value(dto.ContextDataKey).(*dto.ContextData) + return contextData +} + // RemoveMemLock removes the memory lock limit for the current process. -func RemoveMemLock(ctx context.Context) error { +func RemoveMemLock() error { if err := rlimit.RemoveMemlock(); err != nil { return err } diff --git a/internal/dto/config.go b/internal/dto/config.go index 0f46eb7..943f299 100644 --- a/internal/dto/config.go +++ b/internal/dto/config.go @@ -13,10 +13,10 @@ type Config struct { Type string `toml:"type"` } Networking struct { - Enable_udp_tracing bool `toml:"enable_udp_tracing"` - Enable_tcp_tracing bool `toml:"enable_tcp_tracing"` - Udp_filter_ports []string `toml:"udp_filter_ports"` - Tcp_filter_ports []string `toml:"tcp_filter_ports"` + EnableUDPTracing bool `toml:"enable_udp_tracing"` + EnableTCPTracing bool `toml:"enable_tcp_tracing"` + UDPFilterPorts []string `toml:"udp_filter_ports"` + TCPFilterPorts []string `toml:"tcp_filter_ports"` } } diff --git a/internal/dto/context_data.go b/internal/dto/context_data.go index 947f876..e8aaf3d 100644 --- a/internal/dto/context_data.go +++ b/internal/dto/context_data.go @@ -3,6 +3,11 @@ package dto import devstdout "github.com/containerscrew/devstdout/pkg" type ContextData struct { - Log *devstdout.CustomLogger - Config *Config + Log *devstdout.CustomLogger + Config *Config } + +// Define a custom key type to avoid string-based key issues +type contextKey string + +const ContextDataKey = contextKey("contextData") diff --git a/internal/ipchecker/ip_checker.go b/internal/ipchecker/ip_checker.go index e64991f..c66d591 100644 --- a/internal/ipchecker/ip_checker.go +++ b/internal/ipchecker/ip_checker.go @@ -1,4 +1,3 @@ -// Get geo location of an IP address using custom API https://github.com/containerscrew/iproxy package ipchecker import ( @@ -6,8 +5,10 @@ import ( "fmt" "net" "net/http" + "time" ) +// GeoLocation represents the response structure for the IP info API type GeoLocation struct { Status string `json:"status"` City string `json:"city"` @@ -25,28 +26,41 @@ type GeoLocation struct { Query string `json:"query"` } -// Get ip info from custom API +// GetIPInfo retrieves geo-location information for a given IP address from a custom API func GetIPInfo(ip string) (GeoLocation, error) { url := fmt.Sprintf("http://iproxy:8000/api/v1/%s", ip) - resp, err := http.Get(url) + + // Create a custom HTTP client with a timeout + client := &http.Client{ + Timeout: 10 * time.Second, + } + + // Perform the GET request + resp, err := client.Get(url) if err != nil { - return GeoLocation{}, err + return GeoLocation{}, fmt.Errorf("failed to make request: %v", err) } defer resp.Body.Close() + // Check for non-OK status if resp.StatusCode != http.StatusOK { return GeoLocation{}, fmt.Errorf("API request failed with status: %s", resp.Status) } + // Parse the response body into the GeoLocation struct var info GeoLocation if err := json.NewDecoder(resp.Body).Decode(&info); err != nil { - return GeoLocation{}, err + return GeoLocation{}, fmt.Errorf("failed to decode response: %v", err) } return info, nil } +// PrivateIPCheck checks if the given IP is private (non-routable) func PrivateIPCheck(ip string) bool { - ipAddress := net.ParseIP(ip) - return ipAddress.IsPrivate() + ipAddress := net.ParseIP(ip) + if ipAddress == nil { + return false // Invalid IP + } + return ipAddress.IsPrivate() } diff --git a/internal/monitoring/prometheus.go b/internal/monitoring/prometheus.go new file mode 100644 index 0000000..3ed846a --- /dev/null +++ b/internal/monitoring/prometheus.go @@ -0,0 +1,52 @@ +package monitoring + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// Define the Prometheus metrics for TCP and UDP connections +var ( + TCPConnections = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "tcp_connections_total", + Help: "Total number of TCP connections.", + }, + []string{"src_addr", "dst_addr", "src_port", "dst_port", "comm"}, + ) + + UDPConnections = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "udp_connections_total", + Help: "Total number of UDP connections.", + }, + []string{"src_addr", "dst_addr", "src_port", "dst_port", "comm"}, + ) +) + +// InitPrometheus registers the Prometheus metrics +func InitPrometheus() { + prometheus.MustRegister(TCPConnections) + prometheus.MustRegister(UDPConnections) +} + +// TrackTCPEvent updates the Prometheus metrics for a TCP event +func TrackTCPEvent(srcAddr, dstAddr, srcPort, dstPort, comm string) { + TCPConnections.With(prometheus.Labels{ + "src_addr": srcAddr, + "dst_addr": dstAddr, + "src_port": srcPort, + "dst_port": dstPort, + "comm": comm, + }).Inc() +} + +// TrackUDPEvent updates the Prometheus metrics for a UDP event +func TrackUDPEvent(srcAddr, dstAddr, srcPort, dstPort, comm string) { + UDPConnections.With(prometheus.Labels{ + "src_addr": srcAddr, + "dst_addr": dstAddr, + "src_port": srcPort, + "dst_port": dstPort, + "comm": comm, + }).Inc() +} diff --git a/internal/trackers/net_track/bpf_bpfeb.go b/internal/trackers/net_track/bpf_bpfeb.go index 692fe56..a7e58e0 100644 --- a/internal/trackers/net_track/bpf_bpfeb.go +++ b/internal/trackers/net_track/bpf_bpfeb.go @@ -1,7 +1,7 @@ // Code generated by bpf2go; DO NOT EDIT. //go:build mips || mips64 || ppc64 || s390x -package net_track +package nettrack import ( "bytes" diff --git a/internal/trackers/net_track/bpf_bpfel.go b/internal/trackers/net_track/bpf_bpfel.go index eec1a9a..03c1755 100644 --- a/internal/trackers/net_track/bpf_bpfel.go +++ b/internal/trackers/net_track/bpf_bpfel.go @@ -1,7 +1,7 @@ // Code generated by bpf2go; DO NOT EDIT. //go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 -package net_track +package nettrack import ( "bytes" diff --git a/internal/trackers/net_track/net_track.go b/internal/trackers/net_track/net_track.go index 5a4309f..e50fe35 100644 --- a/internal/trackers/net_track/net_track.go +++ b/internal/trackers/net_track/net_track.go @@ -1,4 +1,4 @@ -package net_track +package nettrack import ( "bytes" @@ -9,56 +9,46 @@ import ( "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/ringbuf" - devstdout "github.com/containerscrew/devstdout/pkg" + "github.com/containerscrew/kernelsnoop/internal/core" "github.com/containerscrew/kernelsnoop/internal/dto" + "github.com/containerscrew/kernelsnoop/internal/monitoring" ) //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -type event bpf ./net_track.bpf.c -- -I../../headers func NetworkTrack(ctx context.Context) { // Retrieve the context data (log and config) from the context - contextData, _ := ctx.Value("contextData").(*dto.ContextData) - log := contextData.Log - config := contextData.Config + contextData := core.GetContextData(ctx) // Load pre-compiled eBPF programs and maps into the kernel - objs := bpfObjects{} - if err := loadBpfObjects(&objs, nil); err != nil { - log.Error(fmt.Sprintf("Error loading eBPF objects: %v", err)) + objs, err := loadEBPFObjects(contextData) + if err != nil { return } defer objs.Close() // Conditionally attach TCP tracing if enabled in config.toml - if config.Networking.Enable_tcp_tracing { - linkTCP, err := link.AttachTracing(link.TracingOptions{ - Program: objs.bpfPrograms.TcpConnect, - }) - if err != nil { - log.Error(fmt.Sprintf("Failed to attach TCP tracing link: %v", err)) - } else { - defer linkTCP.Close() - log.Info("tcp tracing enabled") - } + linkTCP, err := attachTCPTracing(contextData, objs) + if err != nil { + return } - // Conditionally attach UDP tracing if enabled in config.toml - if config.Networking.Enable_udp_tracing { - linkUDP, err := link.AttachTracing(link.TracingOptions{ - Program: objs.bpfPrograms.UdpSendmsg, - }) - if err != nil { - log.Error(fmt.Sprintf("Failed to attach UDP tracing link: %v", err)) - } else { - defer linkUDP.Close() - log.Info("udp tracing enabled") - } + if linkTCP != nil { + defer linkTCP.Close() } - // Create a ring buffer reader to receive events - ringBufferReader, err := ringbuf.NewReader(objs.bpfMaps.Events) + linkUDP, err := attachUDPTracing(contextData, objs) + + if err != nil { + return + } + + if linkUDP != nil { + defer linkUDP.Close() + } + + ringBufferReader, err := createRingBufferReader(contextData, objs) if err != nil { - log.Error(fmt.Sprintf("Failed to create ring buffer reader: %v", err)) return } defer ringBufferReader.Close() @@ -69,39 +59,107 @@ func NetworkTrack(ctx context.Context) { // Read an event from the ring buffer record, err := ringBufferReader.Read() if err != nil { - log.Warning(fmt.Sprintf("Error reading from ring buffer: %v", err)) + contextData.Log.Warning(fmt.Sprintf("Error reading from ring buffer: %v", err)) continue } // Parse the raw event data into the bpfEvent struct if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.BigEndian, &event); err != nil { - log.Warning(fmt.Sprintf("Error parsing ring buffer event: %v", err)) + contextData.Log.Warning(fmt.Sprintf("Error parsing ring buffer event: %v", err)) continue } - // Apply port filters for TCP/UDP based on the config - if event.V4.Protocol == 6 && config.Networking.Enable_tcp_tracing { // TCP - if shouldTrackPort(config.Networking.Tcp_filter_ports, event.V4.Dport) { - log.Info("New TCP connection", - devstdout.Argument("comm", string(event.V4.Comm[:bytes.IndexByte(event.V4.Comm[:], 0)])), - devstdout.Argument("src_addr", intToIP(event.V4.Saddr)), - devstdout.Argument("src_port", event.V4.Sport), - devstdout.Argument("dst_addr", intToIP(event.V4.Daddr)), - devstdout.Argument("dst_port", event.V4.Dport), - ) - } - } else if event.V4.Protocol == 17 && config.Networking.Enable_udp_tracing { // UDP - if shouldTrackPort(config.Networking.Udp_filter_ports, event.V4.Dport) { - log.Info("New UDP connection", - devstdout.Argument("comm", string(event.V4.Comm[:bytes.IndexByte(event.V4.Comm[:], 0)])), - devstdout.Argument("src_addr", intToIP(event.V4.Saddr)), - devstdout.Argument("src_port", event.V4.Sport), - devstdout.Argument("dst_addr", intToIP(event.V4.Daddr)), - devstdout.Argument("dst_port", event.V4.Dport), - ) - } + // Handle the event + handleEvent(contextData, &event) + } +} + +// handleEvent processes a single event and updates Prometheus metrics +func handleEvent(contextData *dto.ContextData, event *bpfEvent) { + if event.V4.Protocol == 6 && contextData.Config.Networking.EnableTCPTracing { // TCP + if shouldTrackPort(contextData.Config.Networking.TCPFilterPorts, event.V4.Dport) { + monitoring.TrackTCPEvent( + intToIP(event.V4.Saddr).String(), + intToIP(event.V4.Daddr).String(), + fmt.Sprintf("%d", event.V4.Sport), + fmt.Sprintf("%d", event.V4.Dport), + string(event.V4.Comm[:bytes.IndexByte(event.V4.Comm[:], 0)]), + ) + } + } else if event.V4.Protocol == 17 && contextData.Config.Networking.EnableUDPTracing { // UDP + if shouldTrackPort(contextData.Config.Networking.UDPFilterPorts, event.V4.Dport) { + monitoring.TrackUDPEvent( + intToIP(event.V4.Saddr).String(), + intToIP(event.V4.Daddr).String(), + fmt.Sprintf("%d", event.V4.Sport), + fmt.Sprintf("%d", event.V4.Dport), + string(event.V4.Comm[:bytes.IndexByte(event.V4.Comm[:], 0)]), + ) + } + } +} + +// logEvent logs the details of a TCP/UDP connection event +// func logEvent(contextData *dto.ContextData, protocol string, event *bpfEvent) { +// contextData.Log.Info(fmt.Sprintf("New %s connection", protocol), +// devstdout.Argument("comm", string(event.V4.Comm[:bytes.IndexByte(event.V4.Comm[:], 0)])), +// devstdout.Argument("src_addr", intToIP(event.V4.Saddr)), +// devstdout.Argument("src_port", event.V4.Sport), +// devstdout.Argument("dst_addr", intToIP(event.V4.Daddr)), +// devstdout.Argument("dst_port", event.V4.Dport), +// ) +// } + +// Create a ring buffer reader +func createRingBufferReader(contextData *dto.ContextData, objs *bpfObjects) (*ringbuf.Reader, error) { + ringBufferReader, err := ringbuf.NewReader(objs.bpfMaps.Events) + if err != nil { + contextData.Log.Error(fmt.Sprintf("Failed to create ring buffer reader: %v", err)) + return nil, err + } + return ringBufferReader, nil +} + +// Attach TCP tracing if enabled +func attachTCPTracing(contextData *dto.ContextData, objs *bpfObjects) (link.Link, error) { + if contextData.Config.Networking.EnableTCPTracing { + linkTCP, err := link.AttachTracing(link.TracingOptions{ + Program: objs.bpfPrograms.TcpConnect, + }) + if err != nil { + contextData.Log.Error(fmt.Sprintf("Failed to attach TCP tracing link: %v", err)) + return nil, err + } + contextData.Log.Info("tcp tracing enabled") + return linkTCP, nil + } + return nil, nil +} + +// Attach UDP tracing if enabled +func attachUDPTracing(contextData *dto.ContextData, objs *bpfObjects) (link.Link, error) { + if contextData.Config.Networking.EnableUDPTracing { + linkUDP, err := link.AttachTracing(link.TracingOptions{ + Program: objs.bpfPrograms.UdpSendmsg, + }) + if err != nil { + contextData.Log.Error(fmt.Sprintf("Failed to attach UDP tracing link: %v", err)) + return nil, err } + contextData.Log.Info("udp tracing enabled") + return linkUDP, nil + } + return nil, nil +} + +// Load eBPF objects into the kernel +func loadEBPFObjects(contextData *dto.ContextData) (*bpfObjects, error) { + objs := &bpfObjects{} + if err := loadBpfObjects(objs, nil); err != nil { + contextData.Log.Error(fmt.Sprintf("Error loading eBPF objects: %v", err)) + return nil, err } + return objs, nil } // Check if the event port is in the filter list diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 5a507f4..aeed5b0 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -29,7 +29,7 @@ func GetUsername(uid uint32) string { } // IpResolver return the FQDN of an IP address -func IpLookup(ip string) (string, error) { +func IPLookup(ip string) (string, error) { fqdn, err := net.LookupAddr(ip) if err != nil { return "Unknown", err @@ -42,14 +42,14 @@ func IpLookup(ip string) (string, error) { // IpInfo represents the response from the ip-api.com API type IPInfo struct { - IP string `json:"ip"` - Network Network `json:"network"` + IP string `json:"ip"` + Network Network `json:"network"` Location Location `json:"location"` } type Network struct { - CIDR string `json:"cidr"` - Hosts Hosts `json:"hosts"` + CIDR string `json:"cidr"` + Hosts Hosts `json:"hosts"` AutonomousSystem AutonomousSystem `json:"autonomous_system"` } @@ -110,7 +110,6 @@ func GetIPInfo(ip string) (IPInfo, error) { return info, nil } - // virustotal check for the IP address // VirusTotalResponse represents the response from the VirusTotal API type VirusTotalResponse struct { diff --git a/internal/core/_old_core.go b/learningStuff/_old_core.go similarity index 99% rename from internal/core/_old_core.go rename to learningStuff/_old_core.go index b29d4e0..b4e8947 100644 --- a/internal/core/_old_core.go +++ b/learningStuff/_old_core.go @@ -2,7 +2,7 @@ // This was an intention to separate the code to avoid code duplication, but it was not used in the initial version. -// package core +package oldcore // import ( // "fmt" diff --git a/main.go b/main.go index dd5870b..ad8b11e 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,14 @@ package main import ( "context" "fmt" + "net/http" devstdout "github.com/containerscrew/devstdout/pkg" "github.com/containerscrew/kernelsnoop/internal/core" "github.com/containerscrew/kernelsnoop/internal/dto" - "github.com/containerscrew/kernelsnoop/internal/trackers/net_track" + "github.com/containerscrew/kernelsnoop/internal/monitoring" + nettrack "github.com/containerscrew/kernelsnoop/internal/trackers/net_track" + "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { @@ -23,20 +26,31 @@ func main() { log.Info("Starting kernelsnoop") - // Create a struct to hold both log and config + // Create a struct to hold both log and config contextData := &dto.ContextData{ Log: log, Config: &config, } - // Add the contextData struct to the context - ctx := context.WithValue(context.Background(), "contextData", contextData) + // Add the contextData struct to the context using the custom key + ctx := context.WithValue(context.Background(), dto.ContextDataKey, contextData) // Reset memory lock limit - if err := core.RemoveMemLock(ctx); err != nil { + if err := core.RemoveMemLock(); err != nil { log.Error(fmt.Sprintf("failed to remove memlock rlimit: %v. Consider using sudo or give necessary capabilities to the program", err)) } - net_track.NetworkTrack(ctx) - //ksnoop_file_permissions.FilePermissions(ctx) + // Initialize Prometheus metrics (register the metrics) + monitoring.InitPrometheus() + + // Start the HTTP server for Prometheus to scrape metrics + go func() { + http.Handle("/metrics", promhttp.Handler()) + log.Info("Serving Prometheus metrics on :2112/metrics") + if err := http.ListenAndServe(":2112", nil); err != nil { + log.Error(fmt.Sprintf("Error starting Prometheus metrics server: %v", err)) + } + }() + + nettrack.NetworkTrack(ctx) }