package main import ( "bytes" "context" "fmt" "io" "log" "net" "net/http" "os" "strings" "github.com/redis/go-redis/v9" ) var ( ctx = context.Background() redisClient *redis.Client hashName = "bl:manual" blocklistMode string masterURL string ) 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 { log.Println("✅ BLOCKLIST_MODE set to:", blocklistMode) } 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") } } 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, }) if err := redisClient.Ping(ctx).Err(); err != nil { log.Fatalf("❌ No connection to redis-server: %v", err) } } // Endpunkte registrieren http.HandleFunc("/", handleRoot) http.HandleFunc("/add", handleAdd) log.Println("✅ Server running in mode:", blocklistMode) log.Println("🚀 Server listening at :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, "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 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("❌ Error writing to redis: %v", err) http.Error(w, "Interner Fehler", http.StatusInternalServerError) return } log.Printf("✅ IP %s in saved to redis", ipKey) 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))) if err != nil { log.Printf("❌ Error relaying to Master: %v", err) http.Error(w, "Error relaying", http.StatusBadGateway) return } defer resp.Body.Close() w.WriteHeader(resp.StatusCode) fmt.Println(resp.Body) 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 }