file opened
Debugowanie fail2ban, PikaStatus i lokalny model
Od momentu, gdy dorzuciłem fail2ban na serwer, który służy jako “compute” oraz gateway do systemu, miałem problem z ciągłym “samoblokowaniem się” systemu. Faktycznie, niektóre systemy realizują komunikację przez domenę zewnętrzną, mimo, że powinny i mogłyby od wenątrz, co jest na mojej liście “Do refaktoru” w ramach architektury. Jednak miałem też świadomość, że mimo to nie powinien być to ruch tak duży, który by triggerował fail2bana, by banował swój własny IP. Oczywiście, konsekwencje tego problemu były takie, że przy zmianach IP publicznego u mojego ISP dochodziło do chwilowych downtime’ów usług. Było to irytujące za równo dla mnie jak i dla kilku osób, które używają częścij lub rzadziej moich usług.
Debugowanie problemu
Problem zdebugowałem poniekąd przypadkowo, rozwiązując inny problem w całkowicie innym miejscu niż zakładałem, że ten występuje. Sesji debugowania wspomnianego problemu z samoblokowaniem się miałem co najmniej 2 lub 3. Po trzeciej wyglądało, że działa, po jakimś czasie okazało się, że jednak coś nadal jest nie tak, ale chwilowo to “olałem”. W międzyczasie zacząłem z całkowicie innej “mańki” kombinować jak by tu sobie odpalać w sensowym tempie i z sensownymi wynikami modele lokalne. Wcześniej działało to średnio, agenci niekoniecznie chcieli się mnie słuchać i tak to leżało. Aż siadłem i pokonfigurowałem nie tylko tak, że mogłem używać lokalnie, ale i zdalnie po zabezpieczionych tunelach poprzez swój serwer. Jak wspomniałem w innym wpisie, zostałem na ten moment przy OpenCode z Ollama i gemma4:27b. Musiałem rozszerzyć mu output length i context na 64k z domyślnych 4k, a wtedy OpenCode zaczął faktyczni dobrze instruować i “gadać” z Ollamą jak i samym modelem. Nie mierzyłem wydajności, ale nie jest to demon prędkości, ale jest wystarczająco dokładny, by pomagać mi w moich prywatnych projektach. Pod zastosowanie enterprise (zlecenia m.in.) nadal wykorzystuję rozwiązanie płatne (GH Copilot). W związku z tym wnioskiem, który wysnułem z mojej obserwacji sposobu i jakości działania lokalnego modelu, uznałem, że należy to natychmiast przetestować w praktyce. Stąd też odpaliłem w neovimie PikaStatus, czyli mój tzw. cloudfront. Ten landing page jest napisany w Blazorze i posiada aktywną sekcję, czyli pozostałość odrębnego systemu “PikaStatus”, czyli podstrona /status. Na biegu znalazłem parę kwestii, które chciałem skorygować, a nie były zbyt trudne.
Cel: Korekta wydajnościowa podstrony statusu
Ogólnie rzecz ujmując problemem podstrony statusu systemu było to, że nie była ona wydajna. Pojedyncze wejście (przez użytkownika za równo zalogowanego jak i niezalogowanego) potrafiło “wywalić” fail2bana “off-rails”, który natychmiast zaczynał agresywnie ścinać ruch. Wstępnie przed banem, albo ruch capował fail2ban, albo robił to evasive na Apache2, bo wszystko przy drugim odświeżeniu strony po prostu muliło. Pojedyncze wejście powodowało też, że pozostałe systemy łapał “freeze”. Poza tym wpadły mi do worka drobne korekty UI również i sposobu renderowania wudoku systemów (poprawiona obsługa błędów). Ostatecznie, problemy renderowania model rozwiązał szybko i od razu poprawnie. Natomiast, jeśli chodzi o problem wzruszania fail2ban/evasive nie byłem pewny czy model pomoże rozwiązać mi ten problem. Aby skrócić oczekiwanie na rozwiązanie, zastosowałem flow standardowy dla siebie: przeanalizowałem kod, wyłapałem co może być nie tak (dodatkowo miałem w głowie pewną obserwację zrobioną w tzw. międzyczasie) i w ramach prompta zawarłem dokładny opis, który w skrócie koncentrował się na tym, że należy ukrócić zbyt częste generowanie się requestów wynikające z pętli w ramach Lifecycle komponentu Blazora. Wzbudzenia re-renderowania w Blazorze wystepuje często znacznie częściej niż np. w Reactcie z racji konstrukcji samego Blazora (każdy komponent ma podpiętą komunikację poprzez tzw. Circuit, który odbierając nowe wiadomości może wymuszać re-render). Moje podejrzenie było takie, że to był dokładny powód, ale zostawiłem samą implementację techniczną modelowi. Model zrealizował to tak, że wyciągnął logikę do odpowiedników “hooków” w Vue lub dodał odpowiednie warunki blokujące uruchamianie logiki, jeśli stan komponentu się odpowiednio zmieniał. Jednocześnie, używając IDistributedMemory (czyli domyślny provider pamięci współdzielonej w .NET) zrealizował cache, aczkolwiek nie była to inwencja własna modelu - kazałem mu zaimplementować cache w oparciu o in-memory store dosłownie. Rozwiązanie było bardzo dobre i po 2 bądź 3 iteracjach (z niewielką korektą manualną z mojej strony) okazało się działać. Ładowanie statusu systemów nie freezowała ani samego landingu, ani reszty systemów, a IP nie dostawało bana nawet, jeśli nie było dodane do ignorowanych. To było moje doświadczenie po lokalnych testach i wdrożeniu na “produkcję”, ale teraz po ok. 3 tygodniach ciągłego użytkowania przeze mnie i pozostałe osoby, nadal nie obserwuję powyższych problemów, które występowały wcześniej.
Tylko czemu fail2ban dawał autobana?
Mój wniosek jest taki, że fail2ban wycinał ruch, który triggerował Pika Status odczytując statusy inaczej system floodował resztę systemów odpytując je po domenach. Pozostaje tylko pytanie - ok, to się działo jak ktoś wszedł, a czemu się to działo w losowych momentach? I tu odpowiedzią są scrappery i inne boty m.in. te należące do OpenAI i Claude. Znały tą subdomenę, wyłapywały link do podstrony statusu i scrapowały ją co triggerowało duży ruch. Pojedyncze wejście powodowało throttling ruchu, ale jeśli 2-3 boty równolegle w odstępie kilku sekund weszły, dochodziło do banów IP botów, a także IP samego systemu. Podsumowując, było to poniekad security issue, które na szczęście udało mi się zaadresować, a jednocześnie było PoC użytkowania lokalnego LLM w praktyce. Ostatecznie, mogę to nazwać podwójnym sukcesem.