Dziewiąty artykuł z cyklu Konfigurujemy VPS. Do konfiguracji nginx-a jeszcze wrócę, tym razem jednak dwie drobne ale bardzo ułatwiające życie techniki administracyjne:
- monitorowanie logów,
- zarządzanie zawartością
/etc/
Monitorowanie logów
Administrator powinien często przeglądać logi, wyłapując problemy czy zagrożenia - prawda? W praktyce bywa z tym różnie, przy rekonfiguracjach czy wdrażaniu nowych aplikacji się to faktycznie robi, a gdy nadchodzi szara codzienność - niekoniecznie. Ponadto, większość logów jest zaśmiecona mało użytecznymi rutynowymi informacjami.
Narzędzie
Problem w znacznej mierze rozwiązuje logcheck
. Jest to programik, który
regularnie (domyślnie - co dwie godziny) przegląda istotne logi systemowe
i - jeśli znajdzie w nich coś niepokojącego - wysyła maila z powiadomieniem
(cytując znalezione teksty).
Przykład logcheck-owego maila:
This email is sent by logcheck. If you wish to no-longer receive it,
you can either deinstall the logcheck package or modify its
configuration file (/etc/logcheck/logcheck.conf).
System Events
=-=-=-=-=-=-=
2008/12/20 07:09:26 [error] 16599#0: *134847 open() "/var/www/nginx-default/user/soapCaller.bs"
failed (2: No such file or directory), client: 217.20.172.129, server: mekk.waw.pl,
request: "GET /user/soapCaller.bs HTTP/1.1", host: "207.192.73.10"
2008/12/20 07:09:26 [error] 16599#0: *134848 open() "/var/www/nginx-default/trix/soapCaller.bs"
failed (2: No such file or directory), client: 217.20.172.129, server: mekk.waw.pl,
request: "GET /trix/soapCaller.bs HTTP/1.1", host: "207.192.73.10"
2008/12/20 07:09:26 [error] 16599#0: *134849 open() "/var/www/nginx-default/trixbox/soapCaller.bs" failed
(2: No such file or directory), client: 217.20.172.129, server: mekk.waw.pl,
request: "GET /trixbox/soapCaller.bs HTTP/1.1", host: "207.192.73.10"
2008/12/20 07:09:26 [error] 16599#0: *134850 open() "/var/www/nginx-default/soapCaller.bs"
failed (2: No such file or directory), client: 217.20.172.129, server: mekk.waw.pl,
request: "GET /soapCaller.bs HTTP/1.1", host: "207.192.73.10"
2008/12/20 07:09:27 [error] 16599#0: *134851 open() "/var/www/nginx-default/usr/soapCaller.bs"
failed (2: No such file or directory), client: 217.20.172.129, server: mekk.waw.pl,
request: "GET /usr/soapCaller.bs HTTP/1.1", host: "207.192.73.10"
(tu akurat jeden z licznych skanów, znaleziony w logach serwera WWW)
Jak to działa? Logcheck przegląda zdefiniowany zestaw logów (dla każdego pamiętając, do którego miejsca został poprzednio przejrzany), dla każdego wpisu sprawdza, czy nie powinien zostać zignorowany i jeśli nie - dołącza do wysyłanego maila. Reguły ignorowania to po prostu obszerny zbiór wyrażeń regularnych opisujących nieszkodliwe informacje.
Drugi zestaw reguł to szczególnie podejrzane wpisy, sygnalizujące najważniejsze
zagrożenia. Takie informacje logcheck
podkreśla prezentując na początku maila
a także zmieniając mu tytuł.
Instalacja
Jest bardzo prosta:
$ sudo apt-get install logcheck
Skrypt instalacyjny zapyta o kluczowe parametry, tj. adres email na który mają
być wysyłane powiadomienia (powinna być to poczta gdzieś na zewnątrz) oraz
poziom filtrowania (o nim za moment, w większości wypadków sensowną wartością
jest server
). Parametry te można później przejrzeć i poprawić w pliku
/etc/logcheck/logcheck.conf
, także tam można skorygować np. tytuły maili.
Poziom filtrowania po prostu wybiera jeden z zestawów reguł - odpowiednio
/etc/logcheck/ignore.d.paranoid
, /etc/logcheck/ignore.d.server
, albo
/etc/logcheck/ignore.d.workstation
. Są one po kolei łączone, tj. na poziomie
server
obowiązują zarówno reguły z katalogu ignore.d.server
,
jak z ignore.d.paranoid
, a na poziomie workstation
z wszystkich trzech
katalogów.
No i np. ignore.d.server/postfix
zawiera wyrażenia regularne
opisujące wpisy z logów postfiksa, które nie wymagają alarmowania administratora,
a ignore.d.server/maradns
analogicznie traktuje wpisy MaraDNS. Przy czym i ten
podział to tylko konwencja, logcheck
zbiera reguły z wszystkich plików z odpowiedniego
katalogu i analizuje pod ich względem każdy znaleziony wpis.
Logcheck uwzględnia pliki o nazwach złożonych z liter, cyfr, podkreśleń i myślników. Pozostałe (np. zakończone tyldą backupy niektórych edytorów) są pomijane.
Szczególnie alarmowe wpisy definiują zaś reguły z katalogów cracking.d
(maile Security Alert
, sygnalizujące próby włamań) i violations.d
(maile
Security Event
, sygnalizujące poważne zagrożenia).
Definiowanie monitorowanych plików
Listę monitorowanych logów definiuje plik /etc/logcheck/logcheck.logfiles
.
Domyślnie znajdziemy tam tylko syslog
i auth.log
, warto dołożyć logi kluczowych
usług - jeśli nie piszą do sysloga ale do własnych logów. Np. by monitorować
także logi nginx-a, dopisałem tam /var/log/nginx/error.log
. Podobnie można dorzucić
logi bazy danych czy jakiegoś serwera aplikacji.
Logcheck
sensownie obsługuje wyłącznie wierszowe logi (tj. takie, w których
każda informacja jest zapisana w pojedynczym wierszu).
Strojenie reguł
Po początkowej instalacji logcheck
będzie przysyłał trochę niepotrzebnych
informacji - domyślne filtry są raczej ostrożne i wyłączają tylko najbardziej
ewidentnie nieistotne wpisy, do tego czasem nie nadążają za zmianami w programach. No a jeśli
monitorujemy log jakiejś nietypowej aplikacji, reguł dla niej po prostu nie ma.
Rozwiązanie jest proste: gdy dostajemy list od logcheck
-a i zawiera on coś
niepotrzebnego, uzupełniamy reguły tak, by daną informację pomijały. Czyli - tworzymy
nowy plik w /etc/logcheck/ignore.d.server
i dodajemy w nim odpowiednie wyrażenie
lub wyrażenia.
Nawet gdy reguła dotyczy programu, który już swoje reguły ma, doradzam założenie nowego pliku - np. nie uwzględnione standardowo ostrzeżenia postfiksa dotyczące weryfikacji certyfikatów dopisałem sobie w
ignore.d.server/postfix-moje
.Powód jest prosty: przy aktualizacjach dystrybucji standardowe pliki reguł często bardzo głęboko się zmieniają, prościej mieć swoje zapiski na boku i zaakceptować upgrade, niż męczyć się z łączeniem.
Przykład: nginx wpisuje do logów informację o buforowaniu żądania i odpowiedzi
(gdy przekraczają one zadany rozmiar). Nie jest to nic alarmującego, dlatego
załozyłem sobie plik ignore.d.server/nginx
o treści:
^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9:]{8} \[warn\] [0-9\#]+:
[0-9\*]+ an upstream response is buffered
^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9:]{8} \[warn\] [0-9\#]+:
[0-9\*]+ a client request body is buffered
(to są dwie linijki, a po dwukropku powinna być jedna spacja, złamałem dla czytelności)
W wyrażeniach obowiązuje syntaks egrep
-a. Warto zwrócić uwagę
na wiodący ^
(oznacza, że wyrażenie musi zostać dopasowane
do początku wpisu, bez ^
będzie także poszukiwane w środku - co
i kosztuje nieco więcej czasu i grozi przypadkowym wymaskowaniem czegoś istotnego).
Podobnie ewentualny końcowy $
nakazuje dopasować cały napis (powyżej
go nie użyłem).
Ważne: tworzony wpis zostanie uwzględniony przy analizie wszystkiego. Dlatego trzeba pisać dosyć rozbudowane, restrykcyjne wyrażenia - by przypadkiem nie wymaskować istotnych informacji od innych programów.
Pisanie regexpów dla dat i czasów jest uciążliwe, ale niemal zawsze daje się je skopiować z jakiegoś innego pliku regułek.
Tą samą metodą możemy rozszerzyć listę alarmów, dodając nowe
elementy w cracking.d
lub violations.d
.
Testowanie reguł
Rzadko zdarza mi się napisać regexpa bez żadnej pomyłki (ot, parę dni kilkanaście minut zastanawiania kosztował mnie wpis jednego z programów zawierający ... dwie spacje po dwukropku). Dlatego dopisaną regułkę warto od razu przetestować.
Prosta metoda testowania: po dopisaniu regułki uruchamiamy:
$ sudo -u logcheck logcheck -tdo -l /sciezka/testowanego/logu
Znaczenie podanych flag: -t
powoduje wyłączenie aktualizowania znacznika
dokąd przejrzano, -o
wypisanie wyników na standardowe wyjście zamiast
wysyłania ich mailem, a -d
wyświetlenie odrobiny informacji debuggingowych.
Można też próbować ręcznego egrep
-a z treścią reguły jako parametrem,
tu jednak zaczyna grać konieczność escape-owania znaków ze względu na shell.
Zarządzanie zawartością /etc/
Co robi wielu administratorów wprowadzając zmiany w konfiguracji? Ano,
cp httpd.conf httpd.conf.backup
i jazda. Ani to wygodne, ani pewne,
ani eleganckie.
Tymczasem problem pilnowania zmieniających się plików tekstowych
jest świetnie znany i dawno rozwiązany - przez narzędzia zarządzania wersjami.
Wersjonowanie /etc
nie tylko daje komfort zawsze mogę się wycofać z omyłkowych
zmian, nawet po paru dniach czy tygodniach ale także pozwala w dowolnym momencie
stwierdzić kiedy i w jakim celu został umieszczony jakiś wpis (o ile pamiętamy
o sensownym komentowaniu).
Rozpoczęcie
Wykorzystywane narzędzie jest kwestią gustu, ja proponuję Mercuriala
ale równie dobrze może być Git czy Bazaar (Subversion raczej nie, bo niektóre
programy mogą źle reagować na podkatalog .svn
).
Ważne by dany program potrafił wersjonować linki symboliczne i nie modyfikował
uprawnień ani właścicieli plików.
Instalacja Mercuriala:
$ sudo apt-get install mercurial
Rozpoczęcie wersjonowania /etc
:
$ sudo vim /etc/.hgignore
$ sudo -i
# cd /etc
# hg init
# hg add
# hg commit -m "Rozpoczęcie wersjonowania /etc"
Mercurial stworzy katalog /etc/.hg
, który będzie zawierał historię naszego /etc
.
Plik
.hgignore
powinien pomijać rzeczy, których wersjonować nie warto, np. pliki automatycznie generowane albo odpryskiapt-get
. Mój wygląda tak:^localtime$ ^mtab$ ^ld\.so\.cache$ \.lock$ ^aliases\.db$ supervise/(lock|pid|stat|status)$ ~$ \.dpkg-(new|old|bak)$
jeśli ktoś nie lubi regexpów, można napisać na początku
syntax: glob
i pisać rzeczy typu*~
.
Wykorzystanie na codzień
Potem, ilekroć coś zmieniamy, po prostu edytujemy odpowiednie pliki konfiguracyjne, a na koniec robimy np.
# cd /etc
# hg add logcheck
# hg commit -m "Dodanie obserwacji logu nginxa, pomijanie ostrzeżeń o buforowaniu"
hg commit
zatwierdza zarówno dodane pliki, jak zmiany w edytowanych, pełnego wprowadzenia
do Mercuriala tu nie będe umieszczał, zachęcam do przejrzenia dokumentacji.
Pomyliliśmy się? Po prostu
# hg revert nginx.conf
Podobnie hg log
pozwala sprawdzać co się zmieniało, hg annotate
które wiersze danego
pliku skąd się wzięły itd itp.
Zachowanie w trakcie aktualizacji
Niemal każde apt-get upgrade
wprowadza jakieś zmiany w /etc
.
Istnieją narzędzia - na przykład IsiSetup - które automatycznie commitują takie zmiany. Osobiście wolę jednak je najpierw przejrzeć.
Prosta typowa sekwencja:
# apt-get upgrade
...
# cd /etc
# hg status
(widać co się pojawiło, co znikło, co zmieniło). Potem np.
# hg diff logcheck.conf
Jeśli zmiany nie budzą wątpliwości (99% przypadków), kończę robiąc
# hg addremove
# hg commit -m "Automatyczne zmiany apt-get upgrade"
Gdy jakiś plik wymaga poprawienia, zatwierdzam pozostałe, a jego edytuję i zatwierdzam niezależnie.
Inne zastosowania
Mercurial (czy Git, czy Bazaar) nadają się także do wersjonowania innych rodzajów plików. Choćby drzewa dokumentów HTML.
Uboczny efekt takiego mechanizmu to ... możliwość robienia bardzo efektywnego
backupu, który nie tylko daje pełną historię (z możliwością wyjęcia wersji sprzed dwóch
lat włącznie), ale generuje bardzo mały narzut na kopiowanie i zajmuje niewiele miejsca. Pisałem o tym
kiedyś po angielsku, w największym skrócie: wystarczy założyć dla danego katalogu
repozytorium, sklonować je na maszynę backupującą,
włożyć do cron
-a hg addremove
i hg push -F
(albo, z drugiej maszyny hg pull
) i rzucać od czasu
do czasu okiem, czy wszystko działa.