diff --git a/main.go b/main.go index 87ba3b9..e791a33 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "bytes" "context" + "encoding/json" + "fmt" "io" "log" "net" @@ -23,15 +25,16 @@ var ( ) func main() { - // Modus und Master-URL aus ENV blocklistMode = os.Getenv("BLOCKLIST_MODE") hashName = os.Getenv("HASH_NAME") if hashName == "" { hashName = "bl:manual" } + //DEBUG //blocklistMode = "master" //DEBUG + if blocklistMode != "master" && blocklistMode != "slave" { log.Fatal("❌ BLOCKLIST_MODE has to be 'master' or 'slave'") } else { @@ -40,34 +43,30 @@ func main() { if blocklistMode == "slave" { masterURL = os.Getenv("MASTER_URL") - log.Println("✅ MASTER_URL set to:", masterURL) //DEBUG //masterURL = "http://127.0.0.1:8081" //DEBUG if masterURL == "" { log.Fatal("❌ MASTER_URL has to be set in slave mode") } + log.Println("✅ MASTER_URL set to:", masterURL) } if blocklistMode == "master" { - // Redis initialisieren redisAddr := os.Getenv("REDIS_ADDR") if redisAddr == "" { redisAddr = "localhost:6379" } log.Println("✅ Redis Connection set to:", redisAddr) redisClient = redis.NewClient(&redis.Options{ - Addr: redisAddr, - Password: "", - DB: 0, + Addr: redisAddr, + DB: 0, }) if err := redisClient.Ping(ctx).Err(); err != nil { log.Fatalf("❌ No connection to redis-server: %v", err) } } - /* TCP- and UDP-Honeypods */ - portsTCP := []string{"135", "139", "445", "389", "636", "3268", "3269", "88", "3389", "3306", "27017", "5432", "25", "123", "5900"} for _, port := range portsTCP { go startTCPListener(port) @@ -75,14 +74,14 @@ func main() { portsUDP := []string{"135", "137", "138", "389", "3389", "88"} for _, port := range portsUDP { - go startTCPListener(port) + go startUDPListener(port) } - // Endpunkte registrieren http.HandleFunc("/", handleRoot) http.HandleFunc("/add", handleAdd) - - log.Println("✅ Server running in mode:", blocklistMode) + http.HandleFunc("/dashboard", handleDashboard) + http.HandleFunc("/dashboard.json", handleDashboardJSON) + http.HandleFunc("/metrics", handleMetrics) log.Println("🚀 Server listening at :8080") if err := http.ListenAndServe(":8080", nil); err != nil { @@ -90,31 +89,6 @@ func main() { } } -func handleRoot(w http.ResponseWriter, r *http.Request) { - ip := getClientIP(r) - if ip == "" { - http.Error(w, "Wrong IP", http.StatusBadRequest) - return - } - - processIP(ip, w) -} - -func handleAdd(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil || len(body) == 0 { - http.Error(w, "Wrong Body", http.StatusBadRequest) - return - } - ip := strings.TrimSpace(string(body)) - if !isValidIP(ip) { - http.Error(w, "Wrong IP", http.StatusBadRequest) - return - } - - processIP(ip, w) -} - func startTCPListener(port string) { ln, err := net.Listen("tcp", ":"+port) if err != nil { @@ -127,21 +101,12 @@ func startTCPListener(port string) { log.Printf("⚠️ Error accepting TCP connection: %v", err) continue } - go handleTCPConn(conn) + ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String()) + processIP(ip, port, "TCP", nil) + conn.Close() } } -func handleTCPConn(conn net.Conn) { - defer conn.Close() - ip, _, err := net.SplitHostPort(conn.RemoteAddr().String()) - if err != nil { - log.Printf("⚠️ Could not parse remote address: %v", err) - return - } - log.Printf("👀 TCP connection from %s", ip) - processIP(ip, nil) // Anpassung nötig: processIP muss nil w als Option haben -} - func startUDPListener(port string) { addr := net.UDPAddr{ Port: portInt(port), @@ -161,37 +126,48 @@ func startUDPListener(port string) { } if n > 0 { ip := remoteAddr.IP.String() - log.Printf("👀 UDP packet from %s", ip) - processIP(ip, nil) // ebenfalls anpassen + processIP(ip, port, "UDP", nil) } } } -func portInt(port string) int { - p, _ := strconv.Atoi(port) - return p +func handleRoot(w http.ResponseWriter, r *http.Request) { + ip := getClientIP(r) + if ip == "" { + http.Error(w, "Wrong IP", http.StatusBadRequest) + return + } + processIP(ip, "80", "HTTP", w) } -func processIP(ip string, w http.ResponseWriter) { - ipKey := ip + "/32" +func handleAdd(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil || len(body) == 0 { + http.Error(w, "Wrong Body", http.StatusBadRequest) + return + } + parts := strings.Split(string(body), "|") + if len(parts) != 3 { + http.Error(w, "Wrong Body", http.StatusBadRequest) + return + } + ip, port, proto := parts[0], parts[1], parts[2] + processIP(ip, port, proto, w) +} +func processIP(ip, port, proto string, w http.ResponseWriter) { + ipKey := ip + "/32" if blocklistMode == "master" { - err := redisClient.HSet(ctx, hashName, ipKey, 1).Err() - if err != nil { - log.Printf("❌ Error writing to redis: %v", err) - if w != nil { - http.Error(w, "Interner Fehler", http.StatusInternalServerError) - } - return - } - log.Printf("✅ IP %s in saved to redis", ipKey) + _ = redisClient.HSet(ctx, hashName, ipKey, 1).Err() + _ = redisClient.SAdd(ctx, "bl:manual:"+ipKey, port+"/"+proto).Err() + log.Printf("✅ IP %s saved with port %s/%s", ipKey, port, proto) if w != nil { w.WriteHeader(http.StatusServiceUnavailable) w.Write([]byte("Temporary unavailable")) } } else { - // Slave: an Master weiterleiten - resp, err := http.Post(masterURL+"/add", "text/plain", bytes.NewBuffer([]byte(ip))) + payload := ip + "|" + port + "|" + proto + resp, err := http.Post(masterURL+"/add", "text/plain", bytes.NewBuffer([]byte(payload))) if err != nil { log.Printf("❌ Error relaying to Master: %v", err) if w != nil { @@ -207,6 +183,68 @@ func processIP(ip string, w http.ResponseWriter) { } } +func handleMetrics(w http.ResponseWriter, r *http.Request) { + keys, _ := redisClient.HKeys(ctx, hashName).Result() + totalIPs := len(keys) + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte(fmt.Sprintf("honeypod_blocked_ips_total %d\n", totalIPs))) + + for _, ip := range keys { + ports, _ := redisClient.SMembers(ctx, "bl:manual:"+ip).Result() + for _, p := range ports { + parts := strings.Split(p, "/") + if len(parts) == 2 { + port, proto := parts[0], parts[1] + metricName := fmt.Sprintf("honeypod_port_hits{port=\"%s\",protocol=\"%s\"}", port, proto) + w.Write([]byte(metricName + " 1\n")) // hier 1 pro IP/Port-Protokoll + } + } + } +} + +func handleDashboard(w http.ResponseWriter, r *http.Request) { + keys, _ := redisClient.HKeys(ctx, hashName).Result() + + w.Header().Set("Content-Type", "text/html") + w.Write([]byte(` + + + + + Honeypod Dashboard + + + +
+

Honeypod Dashboard

+ + + +`)) + for _, ip := range keys { + ports, _ := redisClient.SMembers(ctx, "bl:manual:"+ip).Result() + w.Write([]byte("")) + } + w.Write([]byte(` + +
IPPorts / Protocols
" + ip + "" + strings.Join(ports, ", ") + "
+
+ + +`)) +} + +func handleDashboardJSON(w http.ResponseWriter, r *http.Request) { + keys, _ := redisClient.HKeys(ctx, hashName).Result() + result := make(map[string][]string) + for _, ip := range keys { + ports, _ := redisClient.SMembers(ctx, "bl:manual:"+ip).Result() + result[ip] = ports + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(result) +} + func getClientIP(r *http.Request) string { xff := r.Header.Get("X-Forwarded-For") if xff != "" { @@ -216,12 +254,10 @@ func getClientIP(r *http.Request) string { return ip } } - xrip := r.Header.Get("X-Real-Ip") if xrip != "" && isValidIP(xrip) { return xrip } - host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return "" @@ -235,3 +271,8 @@ func getClientIP(r *http.Request) string { func isValidIP(ip string) bool { return net.ParseIP(ip) != nil } + +func portInt(port string) int { + p, _ := strconv.Atoi(port) + return p +}