0904
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/flamego/flamego"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/tamakyi/TamaBox/internal/conf"
|
||||
"github.com/tamakyi/TamaBox/internal/shared"
|
||||
)
|
||||
|
||||
// ipBlacklistMiddleware returns a Flamego handler that checks the client's IP
|
||||
// against a configurable blacklist. When a blacklisted IP is detected, the
|
||||
// request is redirected (HTTP 302) to the configured redirect URL.
|
||||
//
|
||||
// Supports exact IP matching and CIDR notation (e.g. "192.168.1.0/24").
|
||||
// The client IP is resolved by checking X-Real-IP, X-Forwarded-For, then
|
||||
// falling back to the raw RemoteAddr.
|
||||
func ipBlacklistMiddleware() flamego.Handler {
|
||||
raw := strings.TrimSpace(conf.Security.IPBlacklist)
|
||||
if raw == "" {
|
||||
return func(ctx flamego.Context) {}
|
||||
}
|
||||
|
||||
// Pre-parse all entries into exact IPs and CIDR networks.
|
||||
type cidrEntry struct{ ipNet *net.IPNet }
|
||||
exact := make(map[string]bool)
|
||||
cidrs := make([]cidrEntry, 0)
|
||||
|
||||
for _, entry := range strings.Split(raw, ",") {
|
||||
entry = strings.TrimSpace(entry)
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
// Try CIDR first.
|
||||
if _, ipNet, err := net.ParseCIDR(entry); err == nil {
|
||||
cidrs = append(cidrs, cidrEntry{ipNet: ipNet})
|
||||
logrus.Infof("IP blacklist: CIDR %s added", entry)
|
||||
continue
|
||||
}
|
||||
// Fall back to exact IP.
|
||||
if ip := net.ParseIP(entry); ip != nil {
|
||||
exact[ip.String()] = true
|
||||
logrus.Infof("IP blacklist: exact IP %s added", ip.String())
|
||||
continue
|
||||
}
|
||||
logrus.Warnf("IP blacklist: skipping invalid entry %q", entry)
|
||||
}
|
||||
|
||||
if len(exact) == 0 && len(cidrs) == 0 {
|
||||
return func(ctx flamego.Context) {}
|
||||
}
|
||||
|
||||
redirectURL := strings.TrimSpace(conf.Security.IPBlacklistRedirectURL)
|
||||
if redirectURL == "" {
|
||||
logrus.Warn("IP blacklist is configured but ip_blacklist_redirect_url is empty")
|
||||
}
|
||||
|
||||
return func(ctx flamego.Context) {
|
||||
clientIP := shared.ResolveClientIP(ctx.Request().Request)
|
||||
if clientIP == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Exact match first (O(1)).
|
||||
if exact[clientIP] {
|
||||
logrus.WithField("ip", clientIP).Info("IP blacklist: exact match, redirecting")
|
||||
ctx.Redirect(redirectURL, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
// CIDR match (O(n), n is typically small).
|
||||
parsed := net.ParseIP(clientIP)
|
||||
if parsed != nil {
|
||||
for _, e := range cidrs {
|
||||
if e.ipNet.Contains(parsed) {
|
||||
logrus.WithField("ip", clientIP).Infof("IP blacklist: CIDR %s match, redirecting", e.ipNet)
|
||||
ctx.Redirect(redirectURL, http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user