sobota, 21 sierpnia 2010

Zabijanie procesów MySQL wybranego użytkownika

Kilka razy zdarzyło mi się, że klient miał jakiś błąd w skrypcie, który powodował otwieranie mnóstwa procesów w bazie danych MySQL, które nie chciały się zakończyć.

Wyjściem z sytuacji może być restart MySQL'a ale nie zawsze możemy sobie na takie rozwiązanie pozwolić (właściciele, blogów, portali, wortali, sklepów internetowych mogą nas zlinczować).

Pozostaje nam wyłapać procesy określonego użytkownika i je zabić.
Poniżej umieszczam ciąg poleceń, które nam to umożliwią (wiem, że można to zrobić o wiele prościej ale akurat tych poleceń nie mam pod ręką). Wersja hardcore:
mysqladmin processlist -p > dupa.txt && cat dupa.txt | grep root > dupa2.txt && cat dupa2.txt | tr '|' ' ' > dupa3.txt && cat dupa3.txt | awk '{print $1}' > dupa4.txt && for adresss in $(cat dupa4.txt); do mysqladmin kill $adresss -p ; done

Jeżeli chcecie powyższe polecenie stosować na serwerach produkcyjnych to można je znacznie skrócić.
Mysqladmin processlist wyświetla listę wszystkich procesów MySQL'a, przełącznik -p zaznacza, że ma pytać o hasło (w pracy z konta roota mogę zalogować się bez podania hasła - jak ktoś się włamie na root'a to i tak może wszystko), robię zrzut listy procesów do pliku dupa.txt i wyświetlam plik dupa.txt, znajduję wszystkie procesy uruchomione przez użytkownika root (można zmienić na dowolnego) i robię zrzut do pliku dupa2.txt. Wyświetlam zawartość dupa2.txt i za pomocą polecenia tr (od transform) przekształcam wszystkie znaki "|" na "nic" robię zrzut do pliku dupa3.txt. Ponownie wyświetlam zawartość pliku i wynik przekierowuje do programu awk aby wyświetlić tylko pierwszą kolumnę, w której są zawarte numery ID procesów użytkownika root i zrzucam do pliku dupa4.txt. Dla każdego adresu z pliku dupa4.txt wykonuje polecenie mysqladmin kill numer_id_procesu i oczywiście ma spytać o hasło.
Pozostaje tylko wpisać hasło tyle razy, ile mam procesów do ubicia.

I w ten oto niesprytny sposób, ubiliśmy wszystkie procesy MySQL użytkownika root.

Procesy przed wykonaniem polecenia:
+-----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+----+---------+------+-------+------------------+
| 236 | root | localhost | | Sleep | 500 | | |
| 237 | root | localhost | | Sleep | 428 | | |
| 247 | root | localhost | | Query | 0 | | show processlist |
+-----+------+-----------+----+---------+------+-------+------------------+

Procesy po wykonaniu polecenia:
+-----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+----+---------+------+-------+------------------+
| 254 | root | localhost | | Query | 0 | | show processlist |
+-----+------+-----------+----+---------+------+-------+------------------+


Wiem, że można znacznie łatwiej, za pomocą xargs, ale jest późno, komary atakują i oczy tracą ostrość.

W planach mam opisanie w niekoniecznie najbliższej przyszłości: bind ciąg dalszy, postfix czyszczenie kolejki, no i może psychole FTP atakują - czyli jak wyczerpać ilość dostępnych sesji FTP i siać zamęt i zgorszenie.

Czyszczenie kolejki wiadomości w exim'ie

Dość często zdarza się, iż w kolejce mamy setki, a nawet tysiące wiadomości do wysłania. Zdarzyć się tak może z wielu powodów np.:
- jakiś spamer uruchomił skrypt spamerski na naszym serwerze i wysyła na potęgę reklamy viagry lub reklamuje strony z pornografią, lub nawiedzony przedsiębiorca wysyła do wszystkich znanych (i nieznanych) adresatów reklamę swoich produktów / usług,
- ktoś spamuje serwer, bo lubi, bo zarabia na tym pieniądze lub nie lubi kogoś kto ma na naszym serwerze konto pocztowe (jakiś czas temu chciałem zaspamować promotora - straszny burak z niego był),
- ktoś ustawił sobie przekierowanie wiadomości przychodzących na niedziałający adres i do tego włączył autoresponder (wiadomości jest wysyłana na zewnętrzny serwer, zewnętrzny serwer odpowiada nie ma takiego użytkownika wtedy my wysyłamy potwierdzenie że wiadomość otrzymaliśmy i tak w kółko).


Poniżej przedstawiam polecenie, które usunie wszystkie zamrożone wiadomości z kolejki:
exim -bp | grep frozen | awk '{print $3}' | xargs exim -Mrm

Pierwsza część polecenia wyświetla listę wiadomości w kolejce, druga wyłapuje z tej listy wiadomości ze statusem zamrożona, trzecia (awk) wyświetla tylko trzecią kolumnę, w której znajduje się ID wiadomości, czwarta za pomocą xargs usuwa wszystkie wiadomości o podanym ID.

Czasami czyszczenie zamrożonych wiadomości nie wystarczy, wtedy wyszukuję wiadomości, które wiszą w kolejce przez określony czas np.:
exim -bp | grep [234567890][d]| less
exim -bp | grep [234567890][h]| less
exim -bp | grep [234567890][m]| less

Pierwsze polecenie wyłapuje dni, drugi godziny, a trzecie minuty. Do tego wszystkiego dołożyłem less bo czasami dobrze sprawdzić czy ważnych wiadomości nie usuwamy. Jeżeli po sprawdzeniu okaże się, że kolejce wiszą same śmieci można less zastąpić awk '{print $3}' | xargs exim -Mrm.
Powyższa metoda nie jest niestety doskonała, ponieważ czasami w id wiadomości znajduje się cyfra i litera d,h,m - wówczas możemy usunąć wiadomość z kolejki, która nie koniecznie miała być usunięta. Dlatego zawsze, ale to zawsze trzeba być czujnym jak ważka :).

Dobra, wszystko ładnie i pięknie ale możecie powiedzieć skąd mam wiedzieć czy to jest spam, czy normalna wiadomości.
Najpierw znajdujemy wiadomość, która wisi, exim -bp | grep frozen.
Gdy już mamy jej id możemy przejrzeć jej:
body (czyli całą treść wiadomości) exim -Mvb id_wiadomości
headers (czyli nagłówki) exim -Mvh id_wiadomości
logi (skąd dokąd i kiedy) exim -Mvl id_wiadomości

Gdy się okaże że wiadomość zawiera spam możemy od razu ją usunąć np. exim -Mrm id_wiadomości

Jak już wspomniałem o spamerach, to mogą oni wysyłać spam na dwa główne sposoby za pomocą apacha i sendmaila, za pomocą exima (czyli połączenie za pomocą klienta poczty).
Gdy spamują za pomocą WWW, wystarczy sprawdzić access_logs Apacha np.: grep post -i access_logs.
Gdy spamują za pomocą skrzynki pocztowej możemy przejrzeć logi exima na żywo: tail -f /var/log/exim_mainlog, następnie przegrepować po loginie: exigrep adres@email_spamera.pl /var/log/exim_mainlog.
Exigrep jest o tyle wygodny, że pokazuje cały log dotyczący wybranej wiadomości, samo polecenie grep pokaże nam tylko fragment logów.
Można też grepować logi po sendmailu.
Czasami spamerzy są sprytni, tzn. umieszczają skrypt spamerski, robią wysyłkę, usuwają skrypt spamerski, czyszczą access_logs (jeżeli mają do nich dostęp). W takich sytuacjach dobrze jest przeszukać logi serwera FTP i sprawdzić czy przypadkiem jakieś pliki o podejrzanych nazwach nie były wgrywane na serwer.

Oczywiście gdy już złapiemy spamera i zablokujemy możliwość wysyłania spamu, należy pamiętać o wyczyszczeniu wiadomości znajdujących się w kolejce.

poniedziałek, 2 sierpnia 2010

Jak mieć wszystkie komunikatory w poważaniu.

Wystarczy zainstalować Linux'a założyć, koledze/koleżance konto i umożliwić im logowanie za pomocą SSH.

Zakładam, że ja mam login root, a znajomy ma login pawel.

Obaj jesteśmy zalogowani przez ssh. Najpierw wysyłam komunikat do wszystkich:
wall [wciskam enter]
wprowadzam jakikolwiek komunikat [wciskam enter]
zatwierdzam wysłanie wiadomości [wciskam ctrl + d]


W ten oto prosty sposób wysłałem komunikat do wszystkich:

zbiorowy komunikat od użytkownika pawel@pawel-laptop
(/dev/pts/3) at 22:23 ...

alo



Istnieje możliwość zablokowania otrzymywania komunikatów, oraz odblokowania mesg y [włączenie], mesg n [wyłączenie].


Kolejną interesującą opcją jest pisanie do wybranego użytkownika za pomocą write login.
Najpierw sprawdzamy, na którym terminalu się znajdujemy
root@pawel-laptop:~# tty
/dev/pts/2

pawel@pawel-laptop:~$ tty
/dev/pts/3


Teraz jako root wpisujemy write pawel pts/3 i możemy wpisać dowolny tekst - no prawie, bez polskich znaków oczywiście.

Gdy Paweł będzie chciał odpisać do root'a najpierw sprawdza, na którym terminalu jest on zalogowany:
pawel@pawel-laptop:~$ finger
Login Name Tty Idle Login Time Office Office Phone
pawel pawel tty7 Aug 2 21:29 (:0)
pawel pawel pts/0 4 Aug 2 21:32 (:0.0)
pawel pawel pts/3 Aug 2 22:01 (:0.0)
root root pts/2 4 Aug 2 21:59 (localhost)

następnie wpisuje write root pst/2 lub po prostu write root (root jest tylko zalogowany na jednym terminalu). Istnieje również program talk, ale stwierdzam, że program write w zupełności wystarcza do rozmowy.