Repo .git was damaged (refs/ missing, index truncated, object store near-empty). Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak, then full worktree committed as a rollback baseline.
88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|