package main import ( "context" "log" "net" "net/http" "os" ) 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() { // LookupEnv statt envOr: envOr würde bei leer GESETZTER Variable den // Default liefern -- dann ließe sich die Sperre nie abschalten. Nur eine // UNgesetzte Variable fällt auf den Default zurück; KVER_TOR_NET= (leer) // deaktiviert die Sperre. cidr, ok := os.LookupEnv("KVER_TOR_NET") if !ok { cidr = "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. var blockTOR = guard(func(_ http.ResponseWriter, r *http.Request) error { if isTOR(r) { return errTORBlocked } return nil }) var errTORBlocked = Forbidden("tor.blocked", "Beiträge über TOR sind derzeit nicht möglich.") // isTOR erkennt Anfragen aus dem TOR-Netz. func isTOR(r *http.Request) bool { if torNet == nil { return false } ip := clientIP(r.RemoteAddr) return ip != nil && torNet.Contains(ip) } var errAuthRequired = Unauth("auth.required", "Nicht angemeldet") // requireAuth lehnt Anfragen ohne gültige Session ab und legt die uid in den // Context. Nicht über guard, weil hier nicht nur geprüft, sondern auch der // Request angereichert wird. func requireAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s, ok := getSession(r) if !ok { writeAPIError(w, errAuthRequired) 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 }