From 3d51aa455006903345f554a2dd90034993796114 Mon Sep 17 00:00:00 2001 From: sergei Date: Tue, 14 Apr 2026 06:23:55 +0400 Subject: vpnem: VPN infrastructure with load-balanced multi-protocol nodes - Multi-protocol VPS nodes (VLESS-REALITY + Hysteria2 + SOCKS5) - Smart load balancing via recommendation API - Windows/Linux client (Go + Wails + sing-box) - Server API with RealIP detection and connection tracking - Auto-deployment via vpnui control plane - Silent Windows installer with UAC elevation - Load-based server recommendation (no sticky sessions) - Best Server one-click connection workflow --- internal/api/middleware.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 internal/api/middleware.go (limited to 'internal/api/middleware.go') diff --git a/internal/api/middleware.go b/internal/api/middleware.go new file mode 100644 index 0000000..76885ac --- /dev/null +++ b/internal/api/middleware.go @@ -0,0 +1,70 @@ +package api + +import ( + "context" + "net" + "net/http" + "strings" +) + +// contextKey for real IP. +type contextKey string + +const ctxRealIP contextKey = "real_ip" + +// RealIP middleware extracts the client's real public IP. +// Priority: X-Forwarded-For (from Traefik) > X-Real-IP > RemoteAddr. +func RealIP(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ip := extractRealIP(r) + if ip != "" { + r = r.WithContext(context.WithValue(r.Context(), ctxRealIP, ip)) + } + next(w, r) + } +} + +// GetRealIP returns the client IP from context. +func GetRealIP(r *http.Request) string { + if ip, ok := r.Context().Value(ctxRealIP).(string); ok { + return ip + } + return "" +} + +func extractRealIP(r *http.Request) string { + // 1. X-Forwarded-For (Traefik, nginx, etc.) + if xff := r.Header.Get("X-Forwarded-For"); xff != "" { + // Can contain multiple IPs: client, proxy1, proxy2 + // First one is the original client + parts := strings.Split(xff, ",") + if len(parts) > 0 { + ip := strings.TrimSpace(parts[0]) + if isValidIP(ip) { + return ip + } + } + } + + // 2. X-Real-IP (some proxies use this) + if xri := r.Header.Get("X-Real-IP"); xri != "" { + ip := strings.TrimSpace(xri) + if isValidIP(ip) { + return ip + } + } + + // 3. RemoteAddr fallback (direct connection) + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err == nil && isValidIP(host) { + return host + } + + return "" +} + +func isValidIP(ip string) bool { + // Accept both IPv4 and IPv6 + parsed := net.ParseIP(ip) + return parsed != nil +} -- cgit v1.2.3