package main import ( "context" "log" "net" "net/http" ) type ctxKey string const uidKey ctxKey = "uid" // torNet ist das interne Netz, über das TOR-Anfragen beim Backend ankommen: // der Onion-Hidden-Service reicht über einen lokalen Proxy weiter, eine echte // Client-IP gibt es dabei nicht -> die Anfragen erscheinen mit einer IP aus // diesem Netz (siehe Impression-Log: 10.89.0.0). Beiträge von dort werden // vorerst komplett abgewiesen; eine Moderations-/Freigabeseite kommt später. // Per KVER_TOR_NET (CIDR) konfigurierbar; leer schaltet die Sperre ab. var torNet *net.IPNet func init() { cidr := envOr("KVER_TOR_NET", "10.89.0.0/16") if cidr == "" { return } if _, n, err := net.ParseCIDR(cidr); err == nil { torNet = n } else { log.Printf("KVER_TOR_NET ignoriert (ungültiges CIDR %q): %v", cidr, err) } } // blockTOR weist Anfragen aus dem TOR-Netz ab (siehe torNet). Die volle IP wird // hier nur für diese Zugriffsentscheidung geprüft, nicht gespeichert oder // geloggt. Ist kein Netz konfiguriert, ist die Middleware ein No-op. func blockTOR(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if torNet != nil { if ip := clientIP(r.RemoteAddr); ip != nil && torNet.Contains(ip) { writeError(w, http.StatusForbidden, "Beiträge über TOR sind derzeit nicht möglich.") return } } next.ServeHTTP(w, r) }) } // requireAuth lehnt Anfragen ohne gültige Session ab und legt die uid in den Context. func requireAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s, ok := getSession(r) if !ok { writeError(w, http.StatusUnauthorized, "Nicht angemeldet") return } ctx := context.WithValue(r.Context(), uidKey, s.UID) next.ServeHTTP(w, r.WithContext(ctx)) }) } func uidFromContext(ctx context.Context) int64 { uid, _ := ctx.Value(uidKey).(int64) return uid }