From 8faf3392321562087ec7ea5edef123bec527c8e5 Mon Sep 17 00:00:00 2001 From: Nik Rozman Date: Fri, 27 Oct 2023 18:32:41 +0200 Subject: [PATCH] Make webhook configurable --- config.ini | 16 ++++++++++++ go.mod | 4 +++ main.go | 61 ++++++++++++++++++++++++++++++++++++-------- src/alert_channel.go | 14 +++++++--- src/event_counter.go | 8 +++--- 5 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 config.ini diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..225dc40 --- /dev/null +++ b/config.ini @@ -0,0 +1,16 @@ +[General] +; Path to the named pipe (FIFO) for sFlow data +PipePath = /tmp/sflow-abuse +; Webhook URL for sending alerts +WebhookURL = "x" + +[Monitoring] +; List of ports to monitor (comma-separated) +MonitoredPorts = 25,587 + +[Thresholds] +; Time threshold for counting events (in seconds) +TimeThreshold = 10 + +; Packet count threshold for sending an alert +PacketCountThreshold = 100 \ No newline at end of file diff --git a/go.mod b/go.mod index ce9ccf6..b312a37 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module sflow-abuse go 1.20 + +require github.com/go-ini/ini v1.67.0 + +require github.com/stretchr/testify v1.8.4 // indirect diff --git a/main.go b/main.go index f243449..a036195 100644 --- a/main.go +++ b/main.go @@ -7,11 +7,13 @@ import ( "net" "os" "sflow-abuse/src" + "strings" "syscall" "time" + + "github.com/go-ini/ini" ) -// Function to check if an IP address is within any of the specified subnets. func isIPInSubnets(ip string, subnets []string, ignored []string) bool { addr := net.ParseIP(ip) if addr == nil { @@ -34,12 +36,36 @@ func isIPInSubnets(ip string, subnets []string, ignored []string) bool { } func main() { - // Define the path for the named pipe (FIFO). - pipePath := "/tmp/sflow-abuse" + cfg, err := ini.Load("config.ini") + if err != nil { + fmt.Printf("Failed to read configuration file: %v\n", err) + return + } + + // Read the values from the sections + generalSection := cfg.Section("General") + pipePath := generalSection.Key("PipePath").String() + + monitoringSection := cfg.Section("Monitoring") + monitoredPorts := monitoringSection.Key("MonitoredPorts").String() + // Split the comma-separated ports into a slice + monitoredPortSlice := strings.Split(monitoredPorts, ",") + + thresholdsSection := cfg.Section("Thresholds") + timeThreshold, err := thresholdsSection.Key("TimeThreshold").Int() + if err != nil { + fmt.Printf("Invalid TimeThreshold: %v\n", err) + return + } + packetCountThreshold, err := thresholdsSection.Key("PacketCountThreshold").Int() + if err != nil { + fmt.Printf("Invalid PacketCountThreshold: %v\n", err) + return + } // Create the named pipe (FIFO) if it doesn't exist. if _, err := os.Stat(pipePath); os.IsNotExist(err) { - if err := syscall.Mkfifo(pipePath, 0666); err != nil { + if err := syscall.Mkfifo(pipePath, 0660); err != nil { fmt.Printf("Error creating named pipe: %v\n", err) return } @@ -77,11 +103,12 @@ func main() { // Print the subnets that will be used for filtering. fmt.Printf("Loaded %d subnets: %v\n", len(subnets), subnets) + fmt.Printf("Loaded %d monitored ports: %v\n", len(monitoredPortSlice), monitoredPortSlice) fmt.Printf("Loaded %d ignored subnets: %v\n", len(ignored), ignored) // Create an instance of EventCounter to track events. - eventCounter := src.NewEventCounter(10*time.Second, src.HandleAlert) - go eventCounter.StartMonitoring() + eventCounter := src.NewEventCounter(time.Duration(timeThreshold)*time.Second, src.HandleAlert) + go eventCounter.StartMonitoring(packetCountThreshold) // Open the named pipe for reading. pipeFile, err := os.OpenFile(pipePath, os.O_RDONLY, os.ModeNamedPipe) @@ -111,12 +138,15 @@ func main() { if len(row) >= 16 { if net.ParseIP(row[9]) != nil { sourceIP := row[9] - // Check if the source IP is within any of the specified subnets. - if isIPInSubnets(sourceIP, subnets, ignored) { - destinationPort := row[15] + destinationPort := row[15] - // Count the event for the source IP and port combination. - eventCounter.CountEvent(sourceIP, destinationPort) + // Check if the destination port is in the list of monitored ports. + if containsPort(monitoredPortSlice, destinationPort) { + // Check if the source IP is within any of the specified subnets. + if isIPInSubnets(sourceIP, subnets, ignored) { + // Count the event for the source IP and port combination. + eventCounter.CountEvent(sourceIP, destinationPort) + } } } } @@ -125,3 +155,12 @@ func main() { // Stop the event monitoring when finished. eventCounter.StopMonitoring() } + +func containsPort(ports []string, port string) bool { + for _, p := range ports { + if p == port { + return true + } + } + return false +} diff --git a/src/alert_channel.go b/src/alert_channel.go index 034c30c..e65432a 100644 --- a/src/alert_channel.go +++ b/src/alert_channel.go @@ -4,12 +4,10 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/go-ini/ini" "net/http" ) -// WebhookURL is the URL of the Discord webhook. -const WebhookURL = "https://discord.com/api/webhooks/1116824798421594233/ARw2KQvPPIt2wLlw4Ssp98o0VWkjr-FdZ2kpFono8zu5UC-N1Uyysy73wbL_DvYJutya" - // AlertData represents the data to be sent in the alert. type AlertData struct { Embeds []Embed `json:"embeds"` @@ -24,6 +22,16 @@ type Embed struct { // SendAlert sends an alert to the specified Discord webhook. func SendAlert(alert string) error { + cfg, err := ini.Load("config.ini") + if err != nil { + fmt.Printf("Failed to read configuration file: %v\n", err) + return nil + } + + // WebhookURL is the URL of the Discord webhook. + generalSection := cfg.Section("General") + WebhookURL := generalSection.Key("WebhookURL").String() + url := WebhookURL alertData := AlertData{ diff --git a/src/event_counter.go b/src/event_counter.go index 7d4fc13..32837cd 100644 --- a/src/event_counter.go +++ b/src/event_counter.go @@ -34,12 +34,12 @@ func (ec *EventCounter) CountEvent(ip, port string) { ec.mutex.Unlock() } -// StartMonitoring starts the event monitoring and alerts. -func (ec *EventCounter) StartMonitoring() { +// StartMonitoring starts the event monitoring and alerts with a specified threshold. +func (ec *EventCounter) StartMonitoring(threshold int) { for range ec.resetTimer.C { ec.mutex.Lock() for key, count := range ec.counts { - if count >= 10 { + if count >= threshold { ip, port := parseKey(key) ec.alertHandler(ip, port, count) } @@ -68,6 +68,6 @@ func (ec *EventCounter) StopMonitoring() { // HandleAlert is a placeholder for alert handling logic. func HandleAlert(ip, port string, count int) { - SendAlert(fmt.Sprintf("Alert: Source IP %s, Port %s exceeded the threshold with a count of %d\n", ip, port, count)) + SendAlert(fmt.Sprintf("Source IP %s, Port %s exceeded the threshold with a count of %d\n", ip, port, count)) fmt.Printf("Alert: Source IP %s, Port %s exceeded the threshold with a count of %d\n", ip, port, count) }