The WAF can detect the geographic location (country and city) of client IPs for use in filtering rules and whitelisting/blacklisting.
Geolocation enables you to:
- Block or allow traffic by country
- Block or allow traffic by city
- Create geo-specific filter rules
- View geographic information in ban lists and logs
- Implement geo-based rate limiting
Geolocation detection is configured separately for country and city:
wafMiddleware:
detectClientCountry:
method: geoip # or 'header'
header: "" # only used if method is 'header'
detectClientCity:
method: geoip # or 'header'
header: "" # only used if method is 'header'Uses local MaxMind GeoLite2 databases to lookup location by IP.
Configuration:
wafMiddleware:
detectClientCountry:
method: geoip
detectClientCity:
method: geoipGeoIP Database Setup:
GeoIP database paths are configured via environment variables or command-line arguments:
export GEOIP_COUNTRY_PATH="./GeoLite2-Country.mmdb"
export GEOIP_CITY_PATH="./GeoLite2-City.mmdb"Or with Docker:
docker run \
-e GEOIP_COUNTRY_PATH=/app/geoip_data/GeoLite2-Country.mmdb \
-e GEOIP_CITY_PATH=/app/geoip_data/GeoLite2-City.mmdb \
-v $(pwd)/geoip_data:/app/geoip_data:ro \
wafAdvantages:
- No dependency on external services
- Fast lookup (local database)
- Works offline
- No privacy concerns (data stays local)
Disadvantages:
- Requires database files (~100MB total)
- Databases need periodic updates for accuracy
- Uses memory to load databases
Uses a value from an HTTP header set by an upstream service (e.g., CDN, proxy).
Country detection from header:
wafMiddleware:
detectClientCountry:
method: header
header: "CloudFront-Viewer-Country" # Example: AWS CloudFrontCity detection from header:
wafMiddleware:
detectClientCity:
method: header
header: "X-City-Name"Advantages:
- No database files needed
- No memory overhead for databases
- CDN may provide more accurate data
Disadvantages:
- Depends on upstream service
- Header values must be trustworthy
- May not work in all environments
Cloudflare sets geo headers automatically:
wafMiddleware:
detectClientCountry:
method: header
header: "CF-IPCountry"Country codes use ISO 3166-1 alpha-2 format (e.g., US, GB, DE).
CloudFront can be configured to add geo headers:
wafMiddleware:
detectClientCountry:
method: header
header: "CloudFront-Viewer-Country"
detectClientCity:
method: header
header: "CloudFront-Viewer-City"Note: Requires enabling CloudFront geolocation headers in distribution settings.
If your proxy sets custom headers:
wafMiddleware:
detectClientCountry:
method: header
header: "X-Country-Code"
detectClientCity:
method: header
header: "X-City-Name"Nginx example:
location / {
proxy_pass http://waf:3000;
# Using GeoIP module
proxy_set_header X-Country-Code $geoip2_data_country_code;
proxy_set_header X-City-Name $geoip2_data_city_name;
}Download from:
- MaxMind (requires free account): https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
- P3TERX Mirror: https://github.com/P3TERX/GeoLite.mmdb
# Download from P3TERX
wget https://github.com/P3TERX/GeoLite.mmdb/releases/latest/download/GeoLite2-Country.mmdb
wget https://github.com/P3TERX/GeoLite.mmdb/releases/latest/download/GeoLite2-City.mmdbRecommendation: Update monthly for best accuracy.
Automated update script (example):
#!/bin/bash
# update-geoip.sh
cd /path/to/waf
wget -q -O GeoLite2-Country.mmdb.tmp https://github.com/P3TERX/GeoLite.mmdb/releases/latest/download/GeoLite2-Country.mmdb
wget -q -O GeoLite2-City.mmdb.tmp https://github.com/P3TERX/GeoLite.mmdb/releases/latest/download/GeoLite2-City.mmdb
# Atomic replace
mv GeoLite2-Country.mmdb.tmp GeoLite2-Country.mmdb
mv GeoLite2-City.mmdb.tmp GeoLite2-City.mmdb
# Restart WAF
systemctl restart wafCron job (runs monthly):
0 2 1 * * /path/to/update-geoip.shCountry detection returns ISO 3166-1 alpha-2 codes (2-letter codes).
Examples:
US- United StatesGB- United KingdomDE- GermanyFR- FranceCN- ChinaRU- RussiaIN- IndiaBR- Brazil
Full list: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
Allow traffic only from specific countries:
wafMiddleware:
whitelist:
ips: []
ipSubnet: []
geoCountry:
- "US"
- "GB"
- "DE"
geoCity: []Block traffic from specific countries:
wafMiddleware:
blacklist:
ips: []
ipSubnet: []
geoCountry:
- "CN"
- "RU"
geoCity: []Block requests from specific countries to sensitive paths:
jailManager:
filterRules:
- name: block-admin-from-foreign-countries
type: flexible
conditions:
- field: "url"
check:
- method: "equals"
values: ["/admin"]
- field: "country"
check:
- method: "equals"
values: ["US", "GB"] # Allow only US/GBDifferent rate limits for different countries:
jailManager:
filterRules:
# Strict limit for risky countries
- name: rate-limit-risky-countries
type: composite
uniqueClientKey: ["ip"]
conditions:
- field: "country"
check:
- method: "equals"
values: ["CN", "RU"]
limit: 10
period: 60
duration: 600
escalationRate: 2.0jailManager:
filterRules:
- name: block-specific-cities
type: flexible
conditions:
- field: "city"
check:
- method: "equals"
values: ["Moscow", "Beijing"]Test geolocation detection:
# Check detection in logs (set log level to debug)
curl http://localhost:3000/
# Check banned users API (includes country/city metadata)
curl -u admin:password http://localhost:3000/waf/jail-manager/baned-usersExample banned user with geo data:
{
"ip": "1.2.3.4",
"unbanTime": 1678886400000,
"escalationCount": 0,
"metadata": {
"ruleId": "rate-limit",
"country": "US",
"city": "New York",
"requestIds": "req-123"
}
}Causes:
- GeoIP database not found or invalid
- IP is a private IP (192.168.x.x, 10.x.x.x, 127.0.0.1)
- IP not in database (rare)
Solution:
- Verify database files exist:
ls -lh *.mmdb - Check environment variables are set correctly
- Update databases (may be outdated)
- For testing, use a public IP (not 127.0.0.1)
Cause: Outdated database.
Solution: Update GeoIP databases.
Cause: Upstream proxy/CDN not setting expected header.
Debug:
wafMiddleware:
mode: audit # Enable audit mode for detailed logsCheck logs for header values received.
- Country lookup: ~0.1-0.5ms per request
- City lookup: ~0.5-2ms per request
Minimal impact on overall request processing time.
- Country database: ~10-20MB in memory
- City database: ~60-80MB in memory
Total: ~100MB additional memory usage.
For high-traffic scenarios, this is acceptable. If memory is extremely limited, consider using the header method instead.
- Use GeoIP method for most scenarios (most reliable)
- Update databases monthly to maintain accuracy
- Combine with IP whitelisting for trusted locations
- Be cautious with strict geo-blocking - VPNs and proxies can cause false positives
- Use geo data in logs to understand traffic patterns
- Test thoroughly before enforcing geo-based rules
- Static Lists - Whitelist/Blacklist by country
- Filter Rules - Using geo data in rules
- Environment Variables - GeoIP database configuration