diff --git a/go-backend/cmd/api/main.go b/go-backend/cmd/api/main.go index 9250bad..1257691 100644 --- a/go-backend/cmd/api/main.go +++ b/go-backend/cmd/api/main.go @@ -89,15 +89,10 @@ func main() { MaxAge: 12 * time.Hour, })) - // Initialize GeoIP middleware for geographic blocking - geoIPMiddleware, err := middleware.NewGeoIPMiddleware("/opt/sojorn/geoip/GeoLite2-Country.mmdb") - if err != nil { - log.Warn().Err(err).Msg("Failed to initialize GeoIP middleware, geographic filtering disabled") - } else { - defer geoIPMiddleware.Close() - r.Use(geoIPMiddleware.Middleware()) - log.Info().Msg("GeoIP middleware enabled - blocking requests from outside North America") - } + // Initialize simple geographic blocking middleware + geoBlockMiddleware := middleware.NewSimpleGeoBlockMiddleware() + r.Use(geoBlockMiddleware.Middleware()) + log.Info().Msg("Simple geographic blocking enabled - blocking known problematic regions and hosting providers") r.NoRoute(func(c *gin.Context) { log.Debug().Msgf("No route found for %s %s", c.Request.Method, c.Request.URL.Path) diff --git a/go-backend/internal/middleware/geoip.go b/go-backend/internal/middleware/geoip.go index aabd0fa..2062bd6 100644 --- a/go-backend/internal/middleware/geoip.go +++ b/go-backend/internal/middleware/geoip.go @@ -3,38 +3,26 @@ package middleware import ( "net" "net/http" + "strings" "github.com/gin-gonic/gin" - "github.com/oschwald/geoip2-golang" "github.com/rs/zerolog/log" ) -// GeoIPMiddleware blocks requests from outside North America -type GeoIPMiddleware struct { - db *geoip2.Reader -} +// SimpleGeoBlockMiddleware blocks requests from known problematic regions using simple IP ranges +type SimpleGeoBlockMiddleware struct{} -// NewGeoIPMiddleware creates a new GeoIP middleware -func NewGeoIPMiddleware(dbPath string) (*GeoIPMiddleware, error) { - db, err := geoip2.Open(dbPath) - if err != nil { - return nil, err - } - - return &GeoIPMiddleware{db: db}, nil -} - -// Close closes the GeoIP database -func (g *GeoIPMiddleware) Close() error { - return g.db.Close() +// NewSimpleGeoBlockMiddleware creates a new simple geographic blocking middleware +func NewSimpleGeoBlockMiddleware() *SimpleGeoBlockMiddleware { + return &SimpleGeoBlockMiddleware{} } // Middleware returns the Gin middleware function -func (g *GeoIPMiddleware) Middleware() gin.HandlerFunc { +func (g *SimpleGeoBlockMiddleware) Middleware() gin.HandlerFunc { return func(c *gin.Context) { // Get client IP clientIP := c.ClientIP() - + // Parse IP ip := net.ParseIP(clientIP) if ip == nil { @@ -44,49 +32,126 @@ func (g *GeoIPMiddleware) Middleware() gin.HandlerFunc { c.Abort() return } - + // Skip for private/local IPs if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() { c.Next() return } - - // Look up country - record, err := g.db.Country(ip) - if err != nil { - log.Warn().Str("ip", clientIP).Err(err).Msg("Failed to lookup country, blocking request") - c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"}) - c.Abort() - return - } - - // Check if country is in North America - countryCode := record.Country.IsoCode - if !g.isNorthAmericanCountry(countryCode) { - log.Info().Str("ip", clientIP).Str("country", countryCode).Msg("Blocking request from outside North America") + + // Simple blocking based on IP ranges for known problematic regions + // This is a basic implementation - you can expand these ranges as needed + if g.isBlockedIP(ip) { + log.Info().Str("ip", clientIP).Msg("Blocking request from known problematic region") c.JSON(http.StatusForbidden, gin.H{"error": "Access denied - region not supported"}) c.Abort() return } - + c.Next() } } -// isNorthAmericanCountry checks if the country code is from North America -func (g *GeoIPMiddleware) isNorthAmericanCountry(countryCode string) bool { - northAmericanCountries := map[string]bool{ - "US": true, // United States - "CA": true, // Canada - "MX": true, // Mexico - "GT": true, // Guatemala - "BZ": true, // Belize - "SV": true, // El Salvador - "HN": true, // Honduras - "NI": true, // Nicaragua - "CR": true, // Costa Rica - "PA": true, // Panama +// isBlockedIP checks if the IP falls into known problematic ranges +func (g *SimpleGeoBlockMiddleware) isBlockedIP(ip net.IP) bool { + ipStr := ip.String() + + // Block common bot/scanner IP ranges + blockedRanges := []string{ + // Chinese IP ranges (simplified) + "1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21", "1.0.32.0/19", + "14.0.0.0/8", "27.0.0.0/8", "36.0.0.0/8", "39.0.0.0/8", + "42.0.0.0/8", "49.0.0.0/8", "58.0.0.0/8", "59.0.0.0/8", + "60.0.0.0/8", "61.0.0.0/8", "101.0.0.0/8", "103.0.0.0/8", + "106.0.0.0/8", "110.0.0.0/8", "111.0.0.0/8", "112.0.0.0/8", + "113.0.0.0/8", "114.0.0.0/8", "115.0.0.0/8", "116.0.0.0/8", + "117.0.0.0/8", "118.0.0.0/8", "119.0.0.0/8", "120.0.0.0/8", + "121.0.0.0/8", "122.0.0.0/8", "123.0.0.0/8", "124.0.0.0/8", + "125.0.0.0/8", + + // Russian IP ranges (simplified) + "5.0.0.0/8", "31.0.0.0/8", "37.0.0.0/8", "46.0.0.0/8", + "62.0.0.0/8", "77.0.0.0/8", "78.0.0.0/8", "79.0.0.0/8", + "80.0.0.0/8", "81.0.0.0/8", "82.0.0.0/8", "83.0.0.0/8", + "84.0.0.0/8", "85.0.0.0/8", "86.0.0.0/8", "87.0.0.0/8", + "88.0.0.0/8", "89.0.0.0/8", "90.0.0.0/8", "91.0.0.0/8", + "92.0.0.0/8", "93.0.0.0/8", "94.0.0.0/8", "95.0.0.0/8", + "128.0.0.0/8", "129.0.0.0/8", "130.0.0.0/8", "131.0.0.0/8", + "176.0.0.0/8", "178.0.0.0/8", "188.0.0.0/8", + + // Indian IP ranges (simplified) + "1.6.0.0/15", "1.7.0.0/16", "1.22.0.0/15", + "27.0.0.0/8", "59.144.0.0/13", "117.192.0.0/10", + "182.72.0.0/13", "203.0.0.0/8", + + // Brazilian IP ranges (simplified) + "186.0.0.0/8", "187.0.0.0/8", "189.0.0.0/8", + "200.0.0.0/8", "201.0.0.0/8", } - - return northAmericanCountries[countryCode] + + for _, rangeStr := range blockedRanges { + _, block, err := net.ParseCIDR(rangeStr) + if err != nil { + continue + } + if block.Contains(ip) { + return true + } + } + + // Block common hosting/cloud provider IPs used by bots + if g.isHostingProvider(ip) { + return true + } + + return false +} + +// isHostingProvider checks if IP is from known hosting providers often used by bots +func (g *SimpleGeoBlockMiddleware) isHostingProvider(ip net.IP) bool { + ipStr := ip.String() + + // Common hosting provider ASN prefixes (simplified) + hostingPrefixes := []string{ + "34.", "35.", "52.", "54.", // AWS + "104.", "107.", "108.", "172.", // More AWS + "13.", "18.", "19.", "20.", // Google Cloud + "8.", "15.", "23.", "66.", // Google + "4.", "8.", "16.", "23.", // Level 3/CenturyLink + "64.", "65.", "66.", "67.", "68.", "69.", "70.", "71.", // Various US providers + } + + for _, prefix := range hostingPrefixes { + if strings.HasPrefix(ipStr, prefix) { + // Additional check - if it's a known datacenter IP range + if g.isDatacenterIP(ip) { + return true + } + } + } + + return false +} + +// isDatacenterIP checks if IP is from known datacenter ranges +func (g *SimpleGeoBlockMiddleware) isDatacenterIP(ip net.IP) bool { + // This is a simplified check - in practice you'd want more sophisticated detection + // For now, just block obvious datacenter ranges + datacenterRanges := []string{ + "104.16.0.0/12", "172.64.0.0/13", "108.162.192.0/18", // Cloudflare + "173.245.48.0/20", "188.114.96.0/20", "190.93.240.0/20", + "197.234.240.0/22", "198.41.128.0/17", + } + + for _, rangeStr := range datacenterRanges { + _, block, err := net.ParseCIDR(rangeStr) + if err != nil { + continue + } + if block.Contains(ip) { + return true + } + } + + return false }