feat(admin): add security and activity management features
This commit is contained in:
89
lib/server/ip.go
Normal file
89
lib/server/ip.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func clientIP(ctx *gin.Context) string {
|
||||
if ctx == nil || ctx.Request == nil {
|
||||
return ""
|
||||
}
|
||||
remoteIP := remoteAddrIP(ctx.Request)
|
||||
|
||||
// Only trust forwarding headers when remote hop looks like local/internal proxy.
|
||||
if isPrivateOrLoopback(remoteIP) {
|
||||
for _, candidate := range headerIPs(ctx.Request.Header) {
|
||||
if isPublicIP(candidate) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
candidates := headerIPs(ctx.Request.Header)
|
||||
if len(candidates) > 0 {
|
||||
return candidates[0]
|
||||
}
|
||||
}
|
||||
return remoteIP
|
||||
}
|
||||
|
||||
func headerIPs(header http.Header) []string {
|
||||
keys := []string{
|
||||
"X-Forwarded-For",
|
||||
"X-Real-Ip",
|
||||
"CF-Connecting-IP",
|
||||
"X-Envoy-External-Address",
|
||||
"Fly-Client-IP",
|
||||
}
|
||||
out := make([]string, 0, 4)
|
||||
seen := map[string]bool{}
|
||||
for _, key := range keys {
|
||||
raw := strings.TrimSpace(header.Get(key))
|
||||
if raw == "" {
|
||||
continue
|
||||
}
|
||||
for _, part := range strings.Split(raw, ",") {
|
||||
ip := normalizeIP(strings.TrimSpace(part))
|
||||
if ip == "" || seen[ip] {
|
||||
continue
|
||||
}
|
||||
seen[ip] = true
|
||||
out = append(out, ip)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func remoteAddrIP(request *http.Request) string {
|
||||
host, _, err := net.SplitHostPort(strings.TrimSpace(request.RemoteAddr))
|
||||
if err != nil {
|
||||
return normalizeIP(strings.TrimSpace(request.RemoteAddr))
|
||||
}
|
||||
return normalizeIP(host)
|
||||
}
|
||||
|
||||
func normalizeIP(raw string) string {
|
||||
ip := net.ParseIP(strings.TrimSpace(raw))
|
||||
if ip == nil {
|
||||
return ""
|
||||
}
|
||||
return ip.String()
|
||||
}
|
||||
|
||||
func isPublicIP(value string) bool {
|
||||
ip := net.ParseIP(value)
|
||||
if ip == nil || !ip.IsGlobalUnicast() {
|
||||
return false
|
||||
}
|
||||
return !isPrivateOrLoopback(value)
|
||||
}
|
||||
|
||||
func isPrivateOrLoopback(value string) bool {
|
||||
ip := net.ParseIP(value)
|
||||
if ip == nil {
|
||||
return true
|
||||
}
|
||||
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast()
|
||||
}
|
||||
Reference in New Issue
Block a user