diff --git a/.gitea/workflows/registry.yml b/.gitea/workflows/registry.yml new file mode 100644 index 0000000..20912ac --- /dev/null +++ b/.gitea/workflows/registry.yml @@ -0,0 +1,51 @@ +name: release-tag +on: + push: + branches: + - 'main' +jobs: + release-image: + runs-on: ubuntu-fast + env: + DOCKER_ORG: ${{ vars.DOCKER_ORG }} + DOCKER_LATEST: latest + RUNNER_TOOL_CACHE: /toolcache + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker BuildX + uses: docker/setup-buildx-action@v2 + with: # replace it with your local IP + config-inline: | + [registry."${{ vars.DOCKER_REGISTRY }}"] + http = true + insecure = true + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + registry: ${{ vars.DOCKER_REGISTRY }} # replace it with your local IP + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Get Meta + id: meta + run: | + echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT + echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT + + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + file: ./Dockerfile + platforms: | + linux/amd64 + push: true + tags: | # replace it with your local IP and tags + ${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }} + ${{ vars.DOCKER_REGISTRY }}/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cdf5565 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +# -------- Dockerfile (Multi-Stage Build) -------- +# 1. Builder-Stage +FROM golang:1.24-alpine AS builder + +WORKDIR /app +COPY go.* ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /bin/ipblock-api + +# 2. Runtime-Stage +FROM alpine:3.20 + +# HTTPS-Callouts in Alpine brauchen ca-certificates +RUN apk add --no-cache ca-certificates + +COPY --from=builder /bin/ipblock-api /bin/ipblock-api + +# Default listens on :8080 – siehe main.go +EXPOSE 8080 + +# Environment defaults; können per compose überschrieben werden +ENV REDIS_ADDR=redis:6379 \ + BLOCKLIST_MODE=master \ + HASH_NAME=bl:manual \ + MASTER_URL=http://127.0.0.1:8080 + +ENTRYPOINT ["/bin/ipblock-api"] \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..337f9bc --- /dev/null +++ b/compose.yml @@ -0,0 +1,25 @@ +services: + api: + image: git.send.nrw/sendnrw/flod-pod:latest + container_name: ipblock-slave + networks: + - flod_nw + environment: + # Beispiel – mehrere Listen in einer Kategorie „spam“ + BLOCKLIST_MODE: slave + MASTER_URL: https://flod-proxy.send.nrw + #ports: + #- "8080:8080" # : + restart: unless-stopped + newt: + image: fosrl/newt + container_name: newt + networks: + - flod_nw + restart: unless-stopped + environment: + - PANGOLIN_ENDPOINT= + - NEWT_ID= + - NEWT_SECRET= +networks: + flod_nw: \ No newline at end of file diff --git a/main.go b/main.go index b77f32e..cb07880 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "fmt" "io" "log" "net" @@ -16,7 +17,7 @@ import ( var ( ctx = context.Background() redisClient *redis.Client - hashName = "bl:cat" + hashName = "bl:manual" blocklistMode string masterURL string ) @@ -24,14 +25,27 @@ 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 muss 'master' oder 'slave' sein") + 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 muss gesetzt sein im slave Modus") + log.Fatal("❌ MASTER_URL has to be set in slave mode") } } @@ -41,13 +55,14 @@ func main() { 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("Keine Verbindung zu Redis: %v", err) + log.Fatalf("❌ No connection to redis-server: %v", err) } } @@ -55,17 +70,18 @@ func main() { http.HandleFunc("/", handleRoot) http.HandleFunc("/add", handleAdd) - log.Println("Server läuft im Modus:", blocklistMode) - log.Println("Server startet auf :8080") + 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) + 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) + http.Error(w, "Wrong IP", http.StatusBadRequest) return } @@ -75,12 +91,12 @@ func handleRoot(w http.ResponseWriter, r *http.Request) { 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) + http.Error(w, "Wrong Body", http.StatusBadRequest) return } ip := strings.TrimSpace(string(body)) if !isValidIP(ip) { - http.Error(w, "Ungültige IP", http.StatusBadRequest) + http.Error(w, "Wrong IP", http.StatusBadRequest) return } @@ -93,23 +109,24 @@ func processIP(ip string, w http.ResponseWriter) { if blocklistMode == "master" { err := redisClient.HSet(ctx, hashName, ipKey, 1).Err() if err != nil { - log.Printf("Fehler beim Schreiben in Redis: %v", err) + log.Printf("❌ Error writing to 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")) + 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("Fehler beim Weiterleiten an Master: %v", err) - http.Error(w, "Fehler beim Weiterleiten", http.StatusBadGateway) + 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) } }