- Add GeoIP middleware that checks country codes - Block all countries except US, CA, MX, and Central American countries - Add setup script for GeoIP database - Gracefully handle missing database (logs warning but continues)
93 lines
2.3 KiB
Go
93 lines
2.3 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
|
|
"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
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
|
|
// Middleware returns the Gin middleware function
|
|
func (g *GeoIPMiddleware) Middleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Get client IP
|
|
clientIP := c.ClientIP()
|
|
|
|
// Parse IP
|
|
ip := net.ParseIP(clientIP)
|
|
if ip == nil {
|
|
// Invalid IP, block it
|
|
log.Warn().Str("ip", clientIP).Msg("Invalid IP address, blocking request")
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
|
|
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")
|
|
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
|
|
}
|
|
|
|
return northAmericanCountries[countryCode]
|
|
}
|