Der Daemon wird vollständig über Umgebungsvariablen und zwei On-Disk-Dateien konfiguriert. Keine Code-Änderung nötig, um das Verhalten zu justieren.

Umgebungsvariablen

VariableTypDefaultZweck
ABUSIVE_FAMILYv4 / v6 / anyanyMatching auf eine IP-Familie beschränken.
ABUSIVE_MIN_HITSInteger ≥ 13Hits von einer Quelle, bevor die IP in die Blocklist geschrieben wird.
ABUSIVE_SCOPEKomma-Listerequest,uaWelche Log-Felder ausgewertet werden. Gültig: request, ua, referrer.
ABUSIVE_REFERRER_DOMAINSWhitespace/Komma-Liste(leer)Erlaubte Referrer-Hosts. Nur relevant, wenn referrer in ABUSIVE_SCOPE.
ABUSIVE_SYSLOG_TAGStringabusive_http_watchSyslog-Program-Tag.
ABUSIVE_SYSLOG_FACILITYStringdaemonSyslog-Facility. local0local7 für eigene Routings.
ABUSIVE_SYSLOG_PRIOinfo / notice / warn / errnoticeSyslog-Priorität für added-Events.
ABUSIVE_DEBUG0 / 10Verbose-stderr-Trace jeder Zeile. Nur für Scoping sinnvoll.

Anmerkungen

  • ABUSIVE_MIN_HITS — Auf 1 setzen blockt beim ersten Hit. Aggressiver, aber erhöht das False-Positive-Risiko durch One-Shot-Probes von benign Cloud-IPs, die später regulär aufschlagen. Default 3 ist ein guter Kompromiss.
  • ABUSIVE_SCOPEreferrer ist per Default aus, weil ein bösartiger Referrer per Drive-by die IP eines deiner Besucher in die Blocklist befördern könnte. Nur aktivieren, wenn auch ABUSIVE_REFERRER_DOMAINS als strikte Allowlist gesetzt ist.
  • ABUSIVE_FAMILY — Nützlich, wenn man den Daemon über zwei Prozesse spiegeln will: einer für IPv4, einer für IPv6, jeder mit eigener Blocklist-Datei.

Token-Set

Das Erkennungs-Regex-Set ist im Skript fest verdrahtet (@TOKENS) und deckt acht Angriffsklassen ab — PHP-Exploitation, WordPress-Probes, Path-Traversal, sensible Pfade, RCE/CGI-Versuche, SQL-Injection, Login-Loop-Probes und Null-Byte-Injection.

Zum Anpassen des Sets:

  1. Skript öffnen.
  2. Das @TOKENS-Array justieren — Patterns hinzufügen oder entfernen, falls False Positives auftreten.
  3. Daemon neu starten.

Das Set ist absichtlich konservativ. Tokens wie /admin, /login oder phpmyadmin ohne Scoping hinzuzufügen führt zu False Positives auf legitimen Traffic.

Whitelist-Datei

Pfad: /etc/abusive_http_whitelist

Ein Eintrag pro Zeile. Drei Formate werden unterstützt:

# Kommentare erlaubt
203.0.113.42                    # einzelne IPv4
2001:db8::abcd                  # einzelne IPv6 (voll oder verkürzt)
203.0.113.0/24                  # IPv4 CIDR
2001:db8::/32                   # IPv6 CIDR

Whitelisted-IPs werden vollständig übersprungen — sie erhöhen keinen Hit-Counter und landen nie in der Blocklist, selbst wenn ihre Requests jedes Token im Set treffen.

Verwende die Whitelist für:

  • Öffentliche Office- und VPN-IPs (Operatoren, die versehentlich Scanner auslösen).
  • Such-Maschinen-Crawler-IP-Ranges, die dokumentiert legitim sind (Google, Bing). Vorher testen — selten nötig, weil deren Requests keine Angriffs-Token treffen.
  • Monitoring-Dienste, die absichtlich /wp-login.php abfragen.

Blocklist-Datei

Pfad: /var/www/run/abusive_http_hosts

Format: eine IP pro Zeile, keine Kommentare, keine Header, keine Duplikate. Der Daemon stellt Deduplizierung über einen In-Memory-Hash und flock(LOCK_EX) beim Schreiben sicher.

Die Datei ist zur Laufzeit append-only — alte Einträge werden vom Daemon nie entfernt. Zum Aufräumen (z. B. Einträge nach 30 Tagen ablaufen lassen) externe Rotation verwenden:

# In einem Tages-Cron
mv /var/www/run/abusive_http_hosts /var/www/run/abusive_http_hosts.old
touch /var/www/run/abusive_http_hosts
chown _www:_www /var/www/run/abusive_http_hosts
doas rcctl restart abusive_http_watch

Das zwingt den Daemon, seinen In-Memory-State aus einer leeren Datei neu aufzubauen. Die Firewall lädt die neue (leere) Blocklist beim nächsten Cron-Tick.

Firewall-Konsumption

Das Blocklist-Dateiformat ist absichtlich flach, damit jede Schicht es konsumieren kann. Gängige Integrationen:

OpenBSD pf

table <abusive> persist file "/var/www/run/abusive_http_hosts"
block in quick on egress from <abusive>

Tabelle neu laden:

pfctl -t abusive -T replace -f /var/www/run/abusive_http_hosts

Linux nftables

table inet filter {
  set abusive_v4 {
    type ipv4_addr
    elements = { ... }
  }
  chain input {
    ip saddr @abusive_v4 drop
  }
}

Set per Helper aus Datei neu laden:

nft 'flush set inet filter abusive_v4'
awk '/^[0-9]/ {print}' /var/www/run/abusive_http_hosts | \
  xargs -I{} nft 'add element inet filter abusive_v4 { {} }'

Apache

<RequireAll>
  Require all granted
  Require not ip 203.0.113.0/24
  # … ein Require not ip pro Blocklist-Eintrag
</RequireAll>

Per Templater (m4, sed) aus der Blocklist-Datei auf einem Cron generiert. Reagiert langsamer als die Firewall-Integration, funktioniert aber, wenn du die Firewall-Schicht nicht selbst kontrollierst.

nginx

geo $abusive {
  default 0;
  include /var/www/run/abusive_http_nginx.conf;
}
server {
  if ($abusive) { return 444; }
}

abusive_http_nginx.conf wird per Template aus der Blocklist-Datei generiert.