package main import ( "bytes" "context" "io" "log" "net" "net/http" "os" "strings" "github.com/redis/go-redis/v9" ) var ( ctx = context.Background() redisClient *redis.Client hashName = "bl:cat" blocklistMode string masterURL string ) func main() { // Modus und Master-URL aus ENV blocklistMode = os.Getenv("BLOCKLIST_MODE") if blocklistMode != "master" && blocklistMode != "slave" { log.Fatal("BLOCKLIST_MODE muss 'master' oder 'slave' sein") } if blocklistMode == "slave" { masterURL = os.Getenv("MASTER_URL") if masterURL == "" { log.Fatal("MASTER_URL muss gesetzt sein im slave Modus") } } if blocklistMode == "master" { // Redis initialisieren redisAddr := os.Getenv("REDIS_ADDR") if redisAddr == "" { redisAddr = "localhost:6379" } redisClient = redis.NewClient(&redis.Options{ Addr: redisAddr, Password: "", DB: 0, }) if err := redisClient.Ping(ctx).Err(); err != nil { log.Fatalf("Keine Verbindung zu Redis: %v", err) } } // Endpunkte registrieren http.HandleFunc("/", handleRoot) http.HandleFunc("/add", handleAdd) log.Println("Server läuft im Modus:", blocklistMode) log.Println("Server startet auf :8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal("ListenAndServe: ", err) } } func handleRoot(w http.ResponseWriter, r *http.Request) { ip := getClientIP(r) if ip == "" { http.Error(w, "Ungültige 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, "Ungültiger Body", http.StatusBadRequest) return } ip := strings.TrimSpace(string(body)) if !isValidIP(ip) { http.Error(w, "Ungültige IP", http.StatusBadRequest) return } processIP(ip, w) } func processIP(ip string, w http.ResponseWriter) { ipKey := ip + "/32" if blocklistMode == "master" { err := redisClient.HSet(ctx, hashName, ipKey, 1).Err() if err != nil { log.Printf("Fehler beim Schreiben in Redis: %v", err) http.Error(w, "Interner Fehler", http.StatusInternalServerError) return } log.Printf("IP %s in Redis gespeichert", ipKey) w.WriteHeader(http.StatusOK) w.Write([]byte("IP gespeichert: " + ipKey + "\n")) } else { // Slave: an Master weiterleiten resp, err := http.Post(masterURL+"/add", "text/plain", bytes.NewBuffer([]byte(ip))) if err != nil { log.Printf("Fehler beim Weiterleiten an Master: %v", err) http.Error(w, "Fehler beim Weiterleiten", http.StatusBadGateway) return } defer resp.Body.Close() w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) } } func getClientIP(r *http.Request) string { xff := r.Header.Get("X-Forwarded-For") if xff != "" { parts := strings.Split(xff, ",") ip := strings.TrimSpace(parts[0]) if isValidIP(ip) { 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 "" } if isValidIP(host) { return host } return "" } func isValidIP(ip string) bool { return net.ParseIP(ip) != nil }