Skip to main content

Overview

The GeoIP filtering system blocks or allows requests based on the geographic location of the client IP address using MaxMind GeoLite2 database.

Functions

loadGeoIP()

Loads the MaxMind GeoIP2 database from the specified file path.
main.go:264-271
func loadGeoIP(path string) {
    var err error
    geoDB, err = geoip2.Open(path)
    if err != nil {
        log.Fatalf("GeoIP DB error: %v", err)
    }
    log.Println("GeoIP database loaded")
}
path
string
required
File path to GeoLite2-Country.mmdb database
Fatal error if database file cannot be loaded. Ensure the database file exists at the specified path.

Usage Example

From main.go:370-373:
geoBlockEnabled = getEnvBool("GEO_BLOCK_ENABLED", false)
if geoBlockEnabled {
    loadGeoIP("/app/GeoLite2-Country.mmdb")
}

getCountryCode()

Returns the ISO country code for a given IP address.
main.go:273-283
func getCountryCode(ipStr string) (string, error) {
    ip := net.ParseIP(ipStr)
    if ip == nil {
        return "", fmt.Errorf("invalid IP")
    }
    record, err := geoDB.Country(ip)
    if err != nil {
        return "", err
    }
    return record.Country.IsoCode, nil
}
ipStr
string
required
IP address as string (e.g., "192.168.1.100" or "2001:db8::1")
countryCode
string
Two-letter ISO country code (e.g., "US", "CN", "DE")
error
error
Error if IP is invalid or lookup fails

Example

code, err := getCountryCode("8.8.8.8")
if err != nil {
    log.Printf("Lookup failed: %v", err)
} else {
    fmt.Printf("Country: %s\n", code) // Output: Country: US
}

geoFilter()

Filters requests based on geographic allow/block lists.
main.go:296-315
func geoFilter(r *http.Request, allow, block map[string]struct{}) (bool, string) {
    ip, _ := splitHostPort(r.RemoteAddr)

    country, err := getCountryCode(ip)
    if err != nil {
        return false, "geo lookup failed"
    }

    if _, blocked := block[country]; blocked {
        return false, "geo blocked"
    }

    if len(allow) > 0 {
        if _, ok := allow[country]; !ok {
            return false, "geo not allowed"
        }
    }

    return true, country
}
r
*http.Request
required
HTTP request containing RemoteAddr
allow
map[string]struct{}
required
Set of allowed country codes (empty map = allow all)
block
map[string]struct{}
required
Set of blocked country codes
allowed
bool
true if request should be allowed, false if blocked
reason
string
Country code if allowed, or reason string if blocked

Filtering Logic

  1. Lookup fails: Return false, "geo lookup failed"
  2. Country in blocklist: Return false, "geo blocked"
  3. Allowlist non-empty AND country not in allowlist: Return false, "geo not allowed"
  4. Otherwise: Return true, countryCode
Blocklist takes precedence over allowlist. If both are specified, blocked countries are rejected even if in the allowlist.

Usage Example

From main.go:432-439:
if geoBlockEnabled {
    allowed, country := geoFilter(r, geoAllow, geoBlock)
    if !allowed {
        log.Printf("[GEO] blocked %s from %s", r.RemoteAddr, country)
        http.Error(w, "Access denied by GeoIP policy", http.StatusForbidden)
        return
    }
}

parseCSVSet()

Parses a comma-separated string of country codes into a set.
main.go:285-294
func parseCSVSet(env string) map[string]struct{} {
    out := map[string]struct{}{}
    for _, c := range strings.Split(env, ",") {
        c = strings.TrimSpace(strings.ToUpper(c))
        if c != "" {
            out[c] = struct{}{}
        }
    }
    return out
}
env
string
required
Comma-separated country codes (e.g., "US,GB,DE")
return
map[string]struct{}
Set of uppercase country codes

Features

  • Automatically converts to uppercase
  • Trims whitespace
  • Skips empty values
  • Returns empty map for empty input

Usage Example

From main.go:417-420:
if geoBlockEnabled {
    geoAllow = parseCSVSet(os.Getenv("GEO_ALLOW_COUNTRIES"))
    geoBlock = parseCSVSet(os.Getenv("GEO_BLOCK_COUNTRIES"))
}

Global Variables

main.go:47-53
var geoDB *geoip2.Reader

var geoBlockEnabled bool
var blockBots bool
var verifyIPReputation bool
var geoAllow map[string]struct{}
var geoBlock map[string]struct{}
geoDB
*geoip2.Reader
MaxMind GeoIP2 database reader instance
geoBlockEnabled
bool
Whether GeoIP filtering is enabled
geoAllow
map[string]struct{}
Set of allowed country codes
geoBlock
map[string]struct{}
Set of blocked country codes

Environment Variables

See Environment Variables for configuration:
  • GEO_BLOCK_ENABLED - Enable/disable GeoIP filtering
  • GEO_ALLOW_COUNTRIES - Comma-separated allowlist
  • GEO_BLOCK_COUNTRIES - Comma-separated blocklist

Complete Example

Configuration

export GEO_BLOCK_ENABLED=true
export GEO_ALLOW_COUNTRIES="US,CA,GB,DE,FR"
export GEO_BLOCK_COUNTRIES="CN,RU,KP"

Behavior

IP LocationResultReason
United States (US)AllowedIn allowlist
Canada (CA)AllowedIn allowlist
China (CN)BlockedIn blocklist (takes precedence)
Russia (RU)BlockedIn blocklist
Japan (JP)BlockedNot in allowlist
Germany (DE)AllowedIn allowlist

Code Flow

// 1. Initialization
geoBlockEnabled = getEnvBool("GEO_BLOCK_ENABLED", false)
if geoBlockEnabled {
    loadGeoIP("/app/GeoLite2-Country.mmdb")
    geoAllow = parseCSVSet(os.Getenv("GEO_ALLOW_COUNTRIES"))
    geoBlock = parseCSVSet(os.Getenv("GEO_BLOCK_COUNTRIES"))
}

// 2. Per-request filtering
if geoBlockEnabled {
    allowed, country := geoFilter(r, geoAllow, geoBlock)
    if !allowed {
        log.Printf("[GEO] blocked %s from %s", r.RemoteAddr, country)
        http.Error(w, "Access denied by GeoIP policy", http.StatusForbidden)
        return
    }
}

Database Requirements

Requires MaxMind GeoLite2 or GeoIP2 Country database in .mmdb format. Download from MaxMind.
Default path: /app/GeoLite2-Country.mmdb

Build docs developers (and LLMs) love