Skip to content

23 - IP Security

Request Boundary

SecurityMiddleware checks the resolved client IP against logs.db before Flask handles the request. An active ban returns plain-text HTTP 403 and removes the scoped logs session on both blocked and allowed paths.

Client-IP resolution is deliberately gated by TRUST_PROXY_HEADERS:

  • Default FALSE: use only the immediate peer (REMOTE_ADDR / request.remote_addr).
  • TRUE: accept CF-Connecting-IP, True-Client-IP, X-Real-IP, the first X-Forwarded-For value, then X-Client-IP.

Enable forwarded headers only when a controlled reverse proxy is the sole route to Gunicorn/Flask. Otherwise a direct client can spoof those headers and evade per-IP controls.

Stored Security State

database/traffic_db.py stores three IP-related domains in logs.db:

TablePurpose
ip_bansActive temporary or permanent bans, reason, count, creator
error_404_trackerPer-IP 404 count and up to 50 distinct attempted paths in a 24-hour window
invalid_api_key_trackerPer-IP invalid-key count and up to 20 key hashes in a 24-hour window

Raw API keys are not stored by the invalid-key tracker.

Ban lookups use a short TTL cache. Manual ban/unban and automatic changes invalidate the affected cache entry.

Automatic Bans

Automatic bans are controlled by the persisted Security settings, not environment variables. Current defaults are:

SettingDefault
Automatic banningOff
404 threshold in 24 hours100
404 ban duration0 hours (permanent)
Invalid API-key threshold in 24 hours100
Invalid API-key ban duration0 hours (permanent)
Repeat-offender limit2 bans

The authenticated Security dashboard can change the 404 threshold from 1 to 1,000, the invalid API-key threshold from 1 to 100, durations from 0 to 8,760 hours, and repeat limit from 1 to 10. A duration of zero means permanent.

When automatic banning is enabled and a tracker reaches its threshold, localhost addresses are never banned. The tracker resets after its 24-hour window. Existing ban records increment ban_count; reaching the configured repeat limit makes the ban permanent. Durations are not automatically doubled.

Dashboard And Routes

The React Security page is /logs/security. Session-authenticated routes are registered under /security:

RoutePurpose
GET /security/Older server-side dashboard template, still registered
POST /security/banBan one validated IPv4/IPv6 address
POST /security/unbanRemove one ban
POST /security/ban-hostResolve recent traffic for a validated host and ban matching IPs
POST /security/clear-404Clear one 404 tracker
GET /security/api/dataRead ban/tracker/settings data
GET /security/statsRead security totals
POST /security/settingsUpdate automatic-ban settings
GET /security/api/login-activityRead login audit data
POST /security/api/login-activity/clearClear login audit data
GET /security/api/active-sessionsRead active application sessions

There is no CIDR whitelist feature in the current middleware. Rate limiting and IP bans are separate controls.

Key Files

FileResponsibility
utils/security_middleware.pyPre-Flask ban enforcement
utils/ip_helper.pyTrusted client-IP resolution
database/traffic_db.pyBan and attempt persistence
database/settings_db.pyPersisted thresholds and defaults
blueprints/security.pyAuthenticated controls and data routes
frontend/src/pages/monitoring/SecurityDashboard.tsxCurrent dashboard