Make webhook configurable
This commit is contained in:
16
config.ini
Normal file
16
config.ini
Normal 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
4
go.mod
@@ -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
|
||||||
|
|||||||
61
main.go
61
main.go
@@ -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,12 +138,15 @@ 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.
|
destinationPort := row[15]
|
||||||
if isIPInSubnets(sourceIP, subnets, ignored) {
|
|
||||||
destinationPort := row[15]
|
|
||||||
|
|
||||||
// Count the event for the source IP and port combination.
|
// Check if the destination port is in the list of monitored ports.
|
||||||
eventCounter.CountEvent(sourceIP, destinationPort)
|
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.
|
// 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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{
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user