Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA. Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb, Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming, Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office (Collabora/OnlyOffice). Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort) sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"aether/pkg/config"
|
|
)
|
|
|
|
type contextKey int
|
|
|
|
const (
|
|
keyRealIP contextKey = iota
|
|
keyIsHTTPS contextKey = iota
|
|
)
|
|
|
|
// RealIP returns the real client IP stored in context by TrustedProxy middleware.
|
|
// Falls back to r.RemoteAddr if the middleware was not applied.
|
|
func RealIP(r *http.Request) string {
|
|
if ip, ok := r.Context().Value(keyRealIP).(string); ok && ip != "" {
|
|
return ip
|
|
}
|
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
return r.RemoteAddr
|
|
}
|
|
return host
|
|
}
|
|
|
|
// IsHTTPS reports whether the original client connection was HTTPS,
|
|
// either directly or via a trusted proxy's X-Forwarded-Proto header.
|
|
func IsHTTPS(r *http.Request) bool {
|
|
if v, ok := r.Context().Value(keyIsHTTPS).(bool); ok {
|
|
return v
|
|
}
|
|
return r.TLS != nil
|
|
}
|
|
|
|
// TrustedProxy extracts the real client IP and protocol from X-Forwarded-*
|
|
// headers, but only when the immediate peer is in the trusted proxy list.
|
|
// Untrusted peers never override RemoteAddr.
|
|
func TrustedProxy(cfg *config.Config) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
remoteHost, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
remoteHost = r.RemoteAddr
|
|
}
|
|
peerIP := net.ParseIP(remoteHost)
|
|
|
|
realIP := remoteHost
|
|
isHTTPS := r.TLS != nil
|
|
|
|
if peerIP != nil && cfg.IsTrustedProxy(peerIP) {
|
|
if xfp := r.Header.Get("X-Forwarded-Proto"); xfp != "" {
|
|
isHTTPS = strings.EqualFold(strings.TrimSpace(xfp), "https")
|
|
}
|
|
// X-Forwarded-For may be a comma-separated chain; the leftmost
|
|
// entry is the original client (set by the first proxy).
|
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
|
parts := strings.SplitN(xff, ",", 2)
|
|
if candidate := strings.TrimSpace(parts[0]); candidate != "" {
|
|
realIP = candidate
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx := context.WithValue(r.Context(), keyRealIP, realIP)
|
|
ctx = context.WithValue(ctx, keyIsHTTPS, isHTTPS)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
}
|