- Add security preset system with curated threat intelligence - High-Risk Countries preset (RU, CN, KP, IR, etc.) ~800M IPs - Expanded Threat List preset ~1.2B IPs - Cloud Scanner IPs preset (Shodan, Censys) ~3K IPs - Tor Exit Nodes preset ~1.2K IPs (changes daily) - Add tooltips linking to data sources (SANS ISC, Spamhaus, Tor Project) - Add 'Get My IP' button to quickly add current IP to allowlist - Add IP range calculator showing total IPs covered by rules - Emphasize block lists over allow lists in UI - Renamed UI labels to show 'Recommended' for block lists - Added info box explaining why block lists are safer - Add /system/my-ip API endpoint to fetch user's public IP - Handles X-Forwarded-For, X-Real-IP, CF-Connecting-IP headers - Returns IP and source (direct, proxy, Cloudflare, etc.) - Add ARIA attributes to ProxyHosts checkboxes for accessibility Block lists prevent lockouts while maintaining security by blocking known threats instead of requiring explicit allow lists that can inadvertently block legitimate users (especially CGNAT/mobile users). Note: Bulk delete tests need refinement (event simulation) - tracked separately.
75 lines
1.6 KiB
Go
75 lines
1.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type SystemHandler struct{}
|
|
|
|
func NewSystemHandler() *SystemHandler {
|
|
return &SystemHandler{}
|
|
}
|
|
|
|
type MyIPResponse struct {
|
|
IP string `json:"ip"`
|
|
Source string `json:"source"`
|
|
}
|
|
|
|
// GetMyIP returns the client's public IP address
|
|
func (h *SystemHandler) GetMyIP(c *gin.Context) {
|
|
// Try to get the real IP from various headers (in order of preference)
|
|
// This handles proxies, load balancers, and CDNs
|
|
ip := getClientIP(c.Request)
|
|
|
|
source := "direct"
|
|
if c.GetHeader("X-Forwarded-For") != "" {
|
|
source = "X-Forwarded-For"
|
|
} else if c.GetHeader("X-Real-IP") != "" {
|
|
source = "X-Real-IP"
|
|
} else if c.GetHeader("CF-Connecting-IP") != "" {
|
|
source = "Cloudflare"
|
|
}
|
|
|
|
c.JSON(http.StatusOK, MyIPResponse{
|
|
IP: ip,
|
|
Source: source,
|
|
})
|
|
}
|
|
|
|
// getClientIP extracts the real client IP from the request
|
|
// Checks headers in order of trust/reliability
|
|
func getClientIP(r *http.Request) string {
|
|
// Cloudflare
|
|
if ip := r.Header.Get("CF-Connecting-IP"); ip != "" {
|
|
return ip
|
|
}
|
|
|
|
// Other CDNs/proxies
|
|
if ip := r.Header.Get("X-Real-IP"); ip != "" {
|
|
return ip
|
|
}
|
|
|
|
// Standard proxy header (can be a comma-separated list)
|
|
if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
|
|
// Take the first IP in the list (client IP)
|
|
ips := strings.Split(forwarded, ",")
|
|
if len(ips) > 0 {
|
|
return strings.TrimSpace(ips[0])
|
|
}
|
|
}
|
|
|
|
// Fallback to RemoteAddr (format: "IP:port")
|
|
if ip := r.RemoteAddr; ip != "" {
|
|
// Remove port if present
|
|
if idx := strings.LastIndex(ip, ":"); idx != -1 {
|
|
return ip[:idx]
|
|
}
|
|
return ip
|
|
}
|
|
|
|
return "unknown"
|
|
}
|