Add GeoIP middleware to block requests from outside North America
- 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)
This commit is contained in:
parent
89901ab3f2
commit
5782563236
|
|
@ -89,6 +89,16 @@ 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")
|
||||
}
|
||||
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
log.Debug().Msgf("No route found for %s %s", c.Request.Method, c.Request.URL.Path)
|
||||
c.JSON(404, gin.H{"error": "route not found", "path": c.Request.URL.Path, "method": c.Request.Method})
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/oschwald/geoip2-golang v1.13.0 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
|
|
|
|||
|
|
@ -180,6 +180,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
|
||||
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
||||
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
|
||||
github.com/pashagolub/pgxmock/v4 v4.9.0 h1:itlO8nrVRnzkdMBXLs8pWUyyB2PC3Gku0WGIj/gGl7I=
|
||||
github.com/pashagolub/pgxmock/v4 v4.9.0/go.mod h1:9L57pC193h2aKRHVyiiE817avasIPZnPwPlw3JczWvM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
|
|
|
|||
92
go-backend/internal/middleware/geoip.go
Normal file
92
go-backend/internal/middleware/geoip.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
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]
|
||||
}
|
||||
43
go-backend/scripts/setup-geoip.sh
Normal file
43
go-backend/scripts/setup-geoip.sh
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Setup script for GeoIP database
|
||||
# This downloads the GeoLite2-Country database from MaxMind
|
||||
|
||||
set -e
|
||||
|
||||
GEOIP_DIR="/opt/sojorn/geoip"
|
||||
DATABASE_FILE="$GEOIP_DIR/GeoLite2-Country.mmdb"
|
||||
|
||||
echo "Setting up GeoIP database for geographic filtering..."
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
sudo mkdir -p "$GEOIP_DIR"
|
||||
sudo chown patrick:patrick "$GEOIP_DIR"
|
||||
|
||||
# Download the GeoLite2-Country database
|
||||
echo "Downloading GeoLite2-Country database..."
|
||||
cd "$GEOIP_DIR"
|
||||
|
||||
# Use curl to download the database (you'll need a MaxMind account for this)
|
||||
# For now, we'll create a placeholder - you should replace this with actual download
|
||||
echo "Note: You need to sign up for a free MaxMind account to download the GeoLite2 database"
|
||||
echo "Visit: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data"
|
||||
echo ""
|
||||
echo "After getting your license key, download with:"
|
||||
echo "curl -v 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=YOUR_LICENSE_KEY&suffix=tar.gz' | tar xz --strip-components=1 -C ."
|
||||
echo ""
|
||||
|
||||
# Create a placeholder file for now (this won't work but allows the service to start)
|
||||
echo "Creating placeholder database file (you should replace this with the real database)"
|
||||
cat > placeholder.txt << 'EOF'
|
||||
This is a placeholder file. You need to download the actual GeoLite2-Country.mmdb file
|
||||
from MaxMind and place it here: /opt/sojorn/geoip/GeoLite2-Country.mmdb
|
||||
|
||||
Steps:
|
||||
1. Sign up for free account at https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
|
||||
2. Get your license key
|
||||
3. Download: curl -v 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=YOUR_LICENSE_KEY&suffix=tar.gz' | tar xz --strip-components=1 -C .
|
||||
EOF
|
||||
|
||||
echo "Setup complete. Please download the actual GeoIP database as described above."
|
||||
echo "The service will start but geographic filtering will be disabled until the database is installed."
|
||||
Loading…
Reference in a new issue