Make webhook configurable

This commit is contained in:
Nik Rozman
2023-10-27 18:32:41 +02:00
parent 2f9c2c1969
commit 8faf339232
5 changed files with 85 additions and 18 deletions

16
config.ini Normal file
View File

@@ -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

4
go.mod
View File

@@ -1,3 +1,7 @@
module sflow-abuse module sflow-abuse
go 1.20 go 1.20
require github.com/go-ini/ini v1.67.0
require github.com/stretchr/testify v1.8.4 // indirect

55
main.go
View File

@@ -7,11 +7,13 @@ import (
"net" "net"
"os" "os"
"sflow-abuse/src" "sflow-abuse/src"
"strings"
"syscall" "syscall"
"time" "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 { func isIPInSubnets(ip string, subnets []string, ignored []string) bool {
addr := net.ParseIP(ip) addr := net.ParseIP(ip)
if addr == nil { if addr == nil {
@@ -34,12 +36,36 @@ func isIPInSubnets(ip string, subnets []string, ignored []string) bool {
} }
func main() { func main() {
// Define the path for the named pipe (FIFO). cfg, err := ini.Load("config.ini")
pipePath := "/tmp/sflow-abuse" 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. // Create the named pipe (FIFO) if it doesn't exist.
if _, err := os.Stat(pipePath); os.IsNotExist(err) { 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) fmt.Printf("Error creating named pipe: %v\n", err)
return return
} }
@@ -77,11 +103,12 @@ func main() {
// Print the subnets that will be used for filtering. // Print the subnets that will be used for filtering.
fmt.Printf("Loaded %d subnets: %v\n", len(subnets), subnets) 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) fmt.Printf("Loaded %d ignored subnets: %v\n", len(ignored), ignored)
// Create an instance of EventCounter to track events. // Create an instance of EventCounter to track events.
eventCounter := src.NewEventCounter(10*time.Second, src.HandleAlert) eventCounter := src.NewEventCounter(time.Duration(timeThreshold)*time.Second, src.HandleAlert)
go eventCounter.StartMonitoring() go eventCounter.StartMonitoring(packetCountThreshold)
// Open the named pipe for reading. // Open the named pipe for reading.
pipeFile, err := os.OpenFile(pipePath, os.O_RDONLY, os.ModeNamedPipe) pipeFile, err := os.OpenFile(pipePath, os.O_RDONLY, os.ModeNamedPipe)
@@ -111,17 +138,29 @@ func main() {
if len(row) >= 16 { if len(row) >= 16 {
if net.ParseIP(row[9]) != nil { if net.ParseIP(row[9]) != nil {
sourceIP := row[9] 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]
// 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. // Count the event for the source IP and port combination.
eventCounter.CountEvent(sourceIP, destinationPort) eventCounter.CountEvent(sourceIP, destinationPort)
} }
} }
} }
} }
}
// Stop the event monitoring when finished. // Stop the event monitoring when finished.
eventCounter.StopMonitoring() eventCounter.StopMonitoring()
} }
func containsPort(ports []string, port string) bool {
for _, p := range ports {
if p == port {
return true
}
}
return false
}

View File

@@ -4,12 +4,10 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/go-ini/ini"
"net/http" "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. // AlertData represents the data to be sent in the alert.
type AlertData struct { type AlertData struct {
Embeds []Embed `json:"embeds"` Embeds []Embed `json:"embeds"`
@@ -24,6 +22,16 @@ type Embed struct {
// SendAlert sends an alert to the specified Discord webhook. // SendAlert sends an alert to the specified Discord webhook.
func SendAlert(alert string) error { 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 url := WebhookURL
alertData := AlertData{ alertData := AlertData{

View File

@@ -34,12 +34,12 @@ func (ec *EventCounter) CountEvent(ip, port string) {
ec.mutex.Unlock() ec.mutex.Unlock()
} }
// StartMonitoring starts the event monitoring and alerts. // StartMonitoring starts the event monitoring and alerts with a specified threshold.
func (ec *EventCounter) StartMonitoring() { func (ec *EventCounter) StartMonitoring(threshold int) {
for range ec.resetTimer.C { for range ec.resetTimer.C {
ec.mutex.Lock() ec.mutex.Lock()
for key, count := range ec.counts { for key, count := range ec.counts {
if count >= 10 { if count >= threshold {
ip, port := parseKey(key) ip, port := parseKey(key)
ec.alertHandler(ip, port, count) ec.alertHandler(ip, port, count)
} }
@@ -68,6 +68,6 @@ func (ec *EventCounter) StopMonitoring() {
// HandleAlert is a placeholder for alert handling logic. // HandleAlert is a placeholder for alert handling logic.
func HandleAlert(ip, port string, count int) { 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) fmt.Printf("Alert: Source IP %s, Port %s exceeded the threshold with a count of %d\n", ip, port, count)
} }