Rotacja logów

Daemon obsługuje oba popularne wzorce rotacji:

  • Rotate-and-Rename (access-log zostaje przemianowany, nowy plik zajmuje jego miejsce). Wykrywane przez zmianę inode/device na obserwowanej ścieżce.
  • Copytruncate (zawartość pliku jest kopiowana gdzie indziej, potem oryginał obcięty do rozmiaru zero). Wykrywane przez tell() > stat().

W obu przypadkach daemon otwiera plik na nowo od początku i zapisuje info reopen after rotate dev=… ino=… (Rotate-and-Rename) lub info truncate file shrank (Copytruncate) na stderr.

Jeśli wokół rotacji brakuje wpisów, sprawdź konfigurację narzędzia rotacji:

  • Na OpenBSD newsyslog standardową rotacją jest Rotate-and-Rename — żadna specjalna konfiguracja nie jest potrzebna.
  • Na Linuksie logrotate lepiej brać default (rotacja + SIGHUP do serwera web) zamiast trybu copytruncate. Oba działają, ale Rotate-and-Rename jest tańszy i nie traci linii.

Skrypt rc.d (OpenBSD)

Minimalny wariant /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

Potem:

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

Na Linuksie z systemd patrz dokumentacja dystrybucji; prosta jednostka Type=simple wskazująca na /usr/local/sbin/abusive_http_watch z tymi samymi argumentami wystarcza.

Eventy syslog

Daemon emituje jedną klasę eventów — added — gdy nowe IP przekracza próg trafień. Format:

<syslog-tag>[<pid>]: added ip=<ip> path=<ścieżka-blocklisty>

Przykład:

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

Przez syslog.conf zrutować do osobnego pliku do przeglądu:

local0.notice                                          /var/log/abusive_http.log

W połączeniu z ABUSIVE_SYSLOG_FACILITY=local0 daje to czyste audit-log każdego zablokowanego IP.

Sygnały monitoringu

Dla zautomatyzowanego monitoringu dwa wskaźniki są użyteczne:

  • Tempo wzrostu blocklistywc -l /var/www/run/abusive_http_hosts w czasie. Na publicznym serwerze web to powinno stale rosnąć (od kilku IP dziennie do wielu na godzinę, zależnie od ekspozycji). Płaska linia przez 24 h na publicznej stronie wskazuje, że daemon nie działa lub czyta zły log.
  • Health procesupgrep -f abusive_http_watch powinno zawsze zwracać dokładnie jedną PID. Alert przy zero.

Dla setupów Prometheus wystarczy maleńki eksporter, który po prostu robi wc -l na pliku blocklisty i eksponuje liczbę jako gauge. Bez metrics-endpointu w samym daemonie — by design, aby utrzymać go bez zależności.

Troubleshooting

Plik blocklisty pozostaje pusty

Możliwe przyczyny, według prawdopodobieństwa:

  1. Daemon czyta zły plik. Uruchomić na pierwszym planie z ABUSIVE_DEBUG=1. Sprawdzić, czy loguje start following dev=… ino=… dla oczekiwanej ścieżki. Jeśli nie, poprawić argument.
  2. Brak prawdziwego ruchu ataków. Mało prawdopodobne na publicznym serwerze web, ale możliwe wewnętrznie. Uruchomić z ABUSIVE_DEBUG=1 i szukać linii [dbg] ip=… bez odpowiadającego [dbg] token match.
  3. Whitelist pochłania wszystko. Sprawdzić /etc/abusive_http_whitelist — błędny wpis CIDR /0 whitelistuje cały internet. Usunąć i zrestartować.
  4. Próg trafień zbyt wysoki. Testowo obniżyć ABUSIVE_MIN_HITS do 1 i obserwować zachowanie.

Blocklista rośnie, ale firewall nie blokuje

Daemon pisze plik. Firewall musi zostać poinstruowany, aby go czytał. Sprawdzić:

  • Cron przeładowuje tabelę pf / set nftables co kilka minut (daemon nie wywołuje pfctl sam).
  • Reguła firewalla, która wskazuje na tabelę, jest w odpowiednim chain i quick na tyle, aby chwytać przed późniejszymi regułami pass.

Legalni użytkownicy są blokowani

Dodać ich publiczny IP lub CIDR-range do /etc/abusive_http_whitelist. Daemon czyta whitelistę przy każdej linii — zmiany działają w ciągu sekund.

Aby usunąć pojedyncze IP z istniejącej blocklisty, edytować plik bezpośrednio i przeładować tabelę firewalla. Daemon nie potrzebuje restartu — wpisze IP ponownie tylko wtedy, gdy użytkownik znowu trafi w tokeny.

Daemon crashuje po rotacji logów

Nie powinno się wydarzyć z opisaną wyżej obsługą rotacji. Jeśli jednak tak, uruchomić na pierwszym planie z ABUSIVE_DEBUG=1, wywołać rotację i zapisać stderr. Zapisany log wysłać przez stronę Kontakt — to prawdziwy bug, który chcemy naprawić.

Czysty shutdown

Daemon łapie SIGTERM i kończy czysto po aktualnej iteracji:

doas rcctl stop abusive_http_watch

Loguje [info] TERM received, exiting, zamyka handle pliku logu, flushuje wszystkie oczekujące write’y i kończy ze statusem 0. Bez korupcji stanu, nawet przy mocno używanym logu.