This application includes comprehensive security features to protect against malicious traffic, abuse, and unauthorized access:
- IP Banning: Persistent ban list with TTL support
- Rate Limiting: Sliding window rate limiter (per-minute and per-second limits)
- Suspicious Request Detection: Pattern-based detection of malicious requests
- Geographic Blocking: Country/region-based access control using GeoIP
- Request Whitelisting: Allow legitimate static file requests
- Admin API: Secure endpoints for security management
# Generate a secure random API key
python -c "import secrets; print(secrets.token_urlsafe(32))"Copy .env.example to .env and update the values:
cp .env.example .env
nano .env # Edit and paste your API keyImportant: Change ADMIN_API_KEY to the generated secure key!
poetry install# Development mode
poetry run python main.py
# Or using uvicorn
poetry run uvicorn main:app --host 0.0.0.0 --port 8000 --reload
# Production with Docker
make build
make serveRequests are processed in this order:
- IP Ban Check - Block banned IPs immediately
- Geographic Check - Apply country/region restrictions
- Whitelist Check - Allow legitimate requests (static files, main endpoints)
- Suspicious Pattern Detection - Block malicious request patterns
- Rate Limiting - Prevent abuse through request frequency limits
Default values in .env:
RATE_LIMIT_REQUESTS_PER_MINUTE=60 # Max requests per minute per IP
RATE_LIMIT_REQUESTS_PER_SECOND=10 # Burst protectionBAN_DURATION_RATE_LIMIT=3600 # 1 hour for rate limit violations
BAN_DURATION_SUSPICIOUS=86400 # 24 hours for suspicious requestsAutomatically detected patterns (in main.py):
.envfiles.php,.asp,.aspxscripts.json,.xml,.sqlfiles.bak,.log,.conf,.inifiles/admin,/wp-*paths/.git/,/cgi-bin/paths- Hidden files (dotfiles)
Allowed requests:
/static/*.(css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2)- Static assets/- Root endpoint (own IP info)/[domain-or-ip]- Main feature (domain/IP lookup)
All admin endpoints require the api-key header with your ADMIN_API_KEY.
# Set your API key
export API_KEY="your-api-key-from-env-file"curl -H "api-key: $API_KEY" http://localhost:8000/admin/bans# Ban for default duration (24 hours)
curl -X POST -H "api-key: $API_KEY" \
http://localhost:8000/admin/ban/192.168.1.100
# Ban for custom duration (in seconds)
curl -X POST -H "api-key: $API_KEY" \
"http://localhost:8000/admin/ban/192.168.1.100?duration=7200"curl -X DELETE -H "api-key: $API_KEY" \
http://localhost:8000/admin/ban/192.168.1.100curl -H "api-key: $API_KEY" http://localhost:8000/admin/geo/rules# Enable blocklist mode and block specific countries
curl -X PUT -H "api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "blocklist",
"blocked_countries": ["CN", "RU", "KP"],
"block_unknown": false
}' \
http://localhost:8000/admin/geo/rulescurl -X POST -H "api-key: $API_KEY" \
http://localhost:8000/admin/geo/block/country/CNcurl -X DELETE -H "api-key: $API_KEY" \
http://localhost:8000/admin/geo/block/country/CNcurl -X POST -H "api-key: $API_KEY" \
http://localhost:8000/admin/geo/allow/country/UScurl -X DELETE -H "api-key: $API_KEY" \
http://localhost:8000/admin/geo/allow/country/UScurl -H "api-key: $API_KEY" \
http://localhost:8000/admin/geo/lookup/8.8.8.8curl -H "api-key: $API_KEY" \
http://localhost:8000/admin/geo/countriescurl -H "api-key: $API_KEY" http://localhost:8000/admin/statsReturns:
- Number of banned IPs
- Number of rate-limited IPs being tracked
- Current geo-blocking mode
- Number of blocked/allowed countries
{
"mode": "disabled"
}No geographic restrictions applied.
{
"mode": "blocklist",
"blocked_countries": ["CN", "RU", "KP", "IR"],
"blocked_regions": ["US-TX"],
"block_unknown": false
}Block specific countries/regions, allow all others.
Use case: Public service that wants to block high-risk countries.
{
"mode": "allowlist",
"allowed_countries": ["US", "CA", "GB", "DE", "JP"],
"allowed_regions": ["US-CA", "US-NY"],
"block_unknown": true
}Only allow specific countries/regions, block all others.
Use case: Internal service for specific geographic markets.
File: data/banned_ips.json
{
"192.168.1.100": {
"banned_at": "2025-11-05T10:30:00+00:00",
"expires_at": "2025-11-06T10:30:00+00:00",
"reason": "rate_limit",
"request_path": "/api/data",
"country": "CN"
}
}File: data/geo_rules.json
{
"mode": "blocklist",
"blocked_countries": ["CN", "RU", "KP"],
"blocked_regions": [],
"allowed_countries": [],
"allowed_regions": [],
"block_unknown": false,
"bypass_ips": ["8.8.8.8"]
}Automated maintenance tasks run via APScheduler:
- GeoIP Database Update - Every 3 days
- Ban Cleanup - Every 5 minutes (removes expired bans)
- Rate Limit Cleanup - Every 1 minute (prevents memory leaks)
- Use
secrets.token_urlsafe(32)to generate strong keys - Never commit
.envto version control - Rotate keys periodically
- Use different keys for dev/staging/production
Adjust based on your service needs:
# Conservative (low-traffic site)
RATE_LIMIT_REQUESTS_PER_MINUTE=30
RATE_LIMIT_REQUESTS_PER_SECOND=5
# Moderate (default)
RATE_LIMIT_REQUESTS_PER_MINUTE=60
RATE_LIMIT_REQUESTS_PER_SECOND=10
# Permissive (high-traffic site)
RATE_LIMIT_REQUESTS_PER_MINUTE=120
RATE_LIMIT_REQUESTS_PER_SECOND=20Watch for security events:
# Watch live logs
tail -f service.log | grep SECURITY
# Count security events
grep SECURITY service.log | wc -lRecommended for public services:
{
"mode": "blocklist",
"blocked_countries": ["CN", "RU", "KP", "IR"],
"block_unknown": false
}For internal/regional services:
{
"mode": "allowlist",
"allowed_countries": ["US", "CA", "GB"],
"block_unknown": true
}Add trusted IPs to bypass geo-blocking:
{
"bypass_ips": ["8.8.8.8", "1.1.1.1", "your-monitoring-ip"]
}Error: "Not Found"
Reason: For security purposes, admin endpoints return 404 instead of authentication errors to hide their existence from unauthorized users.
Possible causes:
-
ADMIN_API_KEY not set in
.env- Generate API key:
python -c "import secrets; print(secrets.token_urlsafe(32))" - Update
.envfile with generated key - Restart service
- Generate API key:
-
Invalid API key
- Check you're using the correct header:
api-key: YOUR_KEY - Verify the key matches your
.envfile exactly - Ensure no extra spaces in the key
- API key is case-sensitive
- Check you're using the correct header:
-
Endpoint doesn't exist
- Verify the URL is correct
- Check available endpoints in this documentation
Check:
- Review whitelist patterns in
main.py(WhitelistManager) - Check if IP is in ban list:
GET /admin/bans - Verify geo-blocking rules:
GET /admin/geo/rules - Check rate limits in
.env
Solution:
- Update suspicious patterns in
main.py(SuspiciousPatternDetector) - Add exception to whitelist patterns
- Restart service after changes
Region format: {COUNTRY_CODE}-{SUBDIVISION_CODE}
Examples:
US-CA- California, United StatesUS-NY- New York, United StatesUS-TX- Texas, United StatesCA-ON- Ontario, CanadaCA-QC- Quebec, CanadaGB-ENG- England, United Kingdom
Use /admin/geo/lookup/{ip} to find region codes for specific IPs.
200- Success403- Forbidden (banned IP, geo-blocked, suspicious request)404- Not Found (endpoint doesn't exist, or invalid/missing admin API key - returns this for security to hide admin endpoints)429- Too Many Requests (rate limit exceeded)
All security events are logged with this format:
2025-11-05 10:30:00 - main.py:805 - security_middleware - SECURITY: Banned 192.168.1.100 (CN) for suspicious request: /.env
Log levels:
WARNING- Security events (bans, blocks)INFO- Configuration changes, cleanup eventsERROR- System errors
The security features work seamlessly with Docker:
# .env is loaded automatically
# Persistent storage in data/ directory
docker run -d \
-p 8000:8000 \
-v $(pwd)/data:/app/data \
--env-file .env \
whatismyipOr using docker-compose:
version: '3.8'
services:
whatismyip:
build: .
ports:
- "8000:8000"
volumes:
- ./data:/app/data
env_file:
- .env
restart: unless-stopped# Send 70 requests rapidly (should trigger rate limit)
for i in {1..70}; do curl http://localhost:8000/ & done
# Check if banned
curl -H "api-key: $API_KEY" http://localhost:8000/admin/bans# Should return 403 and ban IP
curl http://localhost:8000/.env
# Verify ban
curl -H "api-key: $API_KEY" http://localhost:8000/admin/bans# Block China
curl -X POST -H "api-key: $API_KEY" \
http://localhost:8000/admin/geo/block/country/CN
# Update mode to blocklist
curl -X PUT -H "api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode": "blocklist"}' \
http://localhost:8000/admin/geo/rules# These should all succeed
curl http://localhost:8000/static/style.css
curl http://localhost:8000/
curl http://localhost:8000/google.comFor issues or questions:
- Check logs:
tail -f service.log - Review configuration:
.envanddata/geo_rules.json - Verify admin API:
curl -H "api-key: $API_KEY" http://localhost:8000/admin/stats