Rotația logului

Daemonul gestionează ambele pattern-uri uzuale de rotație:

  • Rotate-and-rename (access logul este redenumit, un fișier nou îi ia locul). Detectat prin schimbarea inode/device pe calea monitorizată.
  • Copytruncate (conținutul fișierului este copiat în altă parte, apoi originalul este truncat la zero). Detectat prin tell() > stat().

În ambele cazuri, daemonul redeschide fișierul de la început și scrie info reopen after rotate dev=… ino=… (rotate-and-rename) sau info truncate file shrank (copytruncate) pe stderr.

Dacă lipsesc intrări în jurul rotației, verifică configurația uneltei de rotație:

  • Pe OpenBSD, newsyslog are rotație standard rotate-and-rename — fără configurare specială necesară.
  • Pe Linux, logrotate ia mai bine default-ul (rotație + SIGHUP către serverul web) decât modul copytruncate. Ambele funcționează, dar rotate-and-rename este mai ieftin și nu pierde linii.

Scriptul rc.d (OpenBSD)

Variantă minimă /etc/rc.d/abusive_http_watch:

#!/bin/ksh
daemon="/usr/local/sbin/abusive_http_watch"
daemon_flags="/var/www/logs/access.log"
daemon_user="_www"
. /etc/rc.d/rc.subr
rc_bg=YES
rc_reload=NO
rc_cmd $1

Apoi:

doas rcctl enable abusive_http_watch
doas rcctl start abusive_http_watch
doas rcctl check abusive_http_watch

Pe Linux cu systemd, vezi documentația distribuției; o unitate simplă Type=simple care arată către /usr/local/sbin/abusive_http_watch cu aceleași argumente este suficientă.

Evenimente syslog

Daemonul emite o clasă de evenimente — added — când un IP nou depășește pragul de hit-uri. Format:

<syslog-tag>[<pid>]: added ip=<ip> path=<cale-blocklist>

Exemplu:

abusive_http_watch[12345]: added ip=198.51.100.42 path=/var/www/run/abusive_http_hosts

Prin syslog.conf, rutează către un fișier propriu pentru review:

local0.notice                                          /var/log/abusive_http.log

Împreună cu ABUSIVE_SYSLOG_FACILITY=local0, rezultă un audit log curat al fiecărui IP blocat.

Semnale de monitoring

Pentru monitoring automat, doi indicatori sunt utili:

  • Rata de creștere a blocklisteiwc -l /var/www/run/abusive_http_hosts în timp. Pe un server web public, ar trebui să crească constant (de la câteva IP-uri pe zi la multe pe oră, în funcție de expunere). O linie plată de 24h pe un site public sugerează că daemonul nu rulează sau citește logul greșit.
  • Sănătatea procesuluipgrep -f abusive_http_watch ar trebui să returneze mereu exact un PID. Alertă la zero.

Pentru setupuri Prometheus, un mic exporter care doar wc -luiește fișierul de blocklistă și expune numărul ca gauge este suficient. Fără endpoint de metrici în daemonul însuși — by design, pentru a-l ține fără dependențe.

Troubleshooting

Fișierul de blocklistă rămâne gol

Cauze posibile, în ordinea probabilității:

  1. Daemonul citește fișierul greșit. Pornește în foreground cu ABUSIVE_DEBUG=1. Verifică să logheze start following dev=… ino=… pentru calea așteptată. Dacă nu, corectează argumentul.
  2. Nu există trafic real de atac. Improbabil pe un server web public, dar posibil intern. Rulează cu ABUSIVE_DEBUG=1 și caută linii [dbg] ip=… fără un [dbg] token match corespunzător.
  3. Whitelist-ul înghite totul. Verifică /etc/abusive_http_whitelist — o intrare CIDR /0 greșită pune tot internetul pe whitelist. Scoate și restartează.
  4. Pragul de hit-uri prea mare. Coboară ABUSIVE_MIN_HITS de test la 1 și observă comportamentul.

Blocklista crește, dar firewallul nu blochează

Daemonul scrie fișierul. Firewallul trebuie instruit să-l citească. Verifică:

  • Cronul reîncarcă tabelul pf / setul nftables la fiecare câteva minute (daemonul nu cheamă pfctl el însuși).
  • Regula de firewall care referă tabelul este în chain-ul potrivit și quick îndeajuns ca să prindă înainte de regulile pass ulterioare.

Utilizatori legitimi sunt blocați

Adaugă IP-ul lor public sau range-ul CIDR la /etc/abusive_http_whitelist. Daemonul recitește whitelist-ul la fiecare linie — schimbările prind în câteva secunde.

Pentru a scoate un singur IP din blocklista existentă, editează direct fișierul și reîncarcă tabelul de firewall. Daemonul nu are nevoie de restart — va reintroduce IP-ul doar dacă utilizatorul lovește din nou tokenuri.

Daemonul crash-uiește după rotația logului

Nu ar trebui să se întâmple cu gestionarea de rotație descrisă mai sus. Dacă totuși da, rulează în foreground cu ABUSIVE_DEBUG=1, declanșează o rotație și înregistrează stderr. Trimite logul înregistrat prin pagina de Contact — acesta este un bug real pe care vrem să-l reparăm.

Shutdown curat

Daemonul prinde SIGTERM și se termină curat după iterația curentă:

doas rcctl stop abusive_http_watch

Loghează [info] TERM received, exiting, închide handle-ul fișierului de log, face flush la toate scrierile în așteptare și iese cu status 0. Fără corupere de stare, chiar și pe un log foarte folosit.