Wybierz region
pl
  • PL
  • EN
Wydrukuj

#AdminCases – czyli gotowa solucja: Permanentna inwigilacja, czyli jak w linuksie logować polecenia użytkowników

Czy zdarzyło Wam się szukać delikwenta, który popsuł coś w systemie czy na środowisku, a Wy jako admini musieliście zastanawiać się, co autor zamieszania nawyprawiał, że takiego piwa nawarzył? Teraz już nie będziecie musieli się zastanawiać.

Pokażę Wam, w jaki sposób logować polecenia użytkowników w systemie Linux wykorzystując do tego Rsyslog wraz z Logrotate, no i oczywiście to co jest najważniejsze, czyli wiedzę na temat działania systemu operacyjnego. Zaimplementujemy to wszystko za pomocą Ansible. 

Nie wiem jak Wy, ale ja już wielokrotnie szukałem co się stało, że w przeciwieństwie do Standardowej Odpowiedzi Administratora numer jeden, czyli: “U mnie działa”, musiałem odpowiedzieć: “Ale kto i co tam namieszał, bo przecież wszystko działało” lub też: “A co zostało usunięte, że działać przestało?”. Nie chodzi tutaj, żeby kogoś piętnować (chociaż warto mieć świadomość kto wykonuje nerwowe ruchy i może coś popsuć chociażby ze względu na możliwość obniżenia takiej personie uprawnień), ale trzeba też wiedzieć co się zadziało, bo jednak jak się wie, to łatwiej można odkręcić daną sytuację lub też – jeżeli chodzi o część z usuwaniem danych - mieć wiedzę co i skąd zostało to usunięte. Ręczę Wam słowem, że takie informacje znacznie przyspieszają przywracanie funkcjonalności, co skutecznie zmniejsza stan nerwowości oraz ilości wyrwanych włosów z głowy.

Jak zdobyć taką wiedzę? Trzeba wiedzieć, co użytkownicy robią w systemie, czyli logować polecenia, które na nim wykonują. Pokażę jeden ze sposobów, w jaki można to osiągnąć, a że (jak zawsze powtarzam) administrator to leniwa bestia, to wdrażanie tego rozwiązania oprzemy na Ansible, żeby nie trzeba było wchodzić na każdy z serwerów, których jesteśmy administratorami i wykonywać tych samych czynności. Chociaż sama automatyzacja nie jest tematem tego artykułu, a jedynie formą pokazania: co zrobić żeby się nie narobić, a zrobić ;)

Teraz może garść założeń zanim przejdziemy do wiedzy tajemnej. Logując się do systemu Linux użytkownicy korzystają z powłoki i zakładamy, że powłoką, którą używają jest bash. Wszystkie polecenia wykonywane w shellu mają zostać zapisane. Nasz przykład będzie dotyczył systemów z menedżerem serwisów i usług jakim jest systemd, czyli większość hostów, które obecnie wykorzystujemy, jeżeli chodzi o serwery z linuxem na pokładzie. Systemy operacyjne, które będą służyły do zobrazowania solucji pochodzą z dwóch rodzin: Debian i Red Hat. Jeżeli chodzi o założenia funkcjonalne to mamy zapisywać polecenia wszystkich użytkowników logujących się do systemu operacyjnego. Powstałe pliki logów mają być rotowane dziennie za pomocą logrotate i funkcjonalności, która już jest dostępna na naszych linuxach. Całość ma zostać zaimplementowana w systemach za pomocą Ansible.

Wygląda na to, że wiemy już wszystko co mamy zrobić zatem zacznijmy czarować, a przy okazji czarów poznamy kilka fajnych rzeczy, które egzystują w interesujących nas systemach operacyjnych.

Na początek skorzystamy z dobrodziejstw Rsyslog, czyli mówiąc wprost – bez używania formułek książkowych – serwisu do tworzenia, kolekcjonowania czy przechowywania informacji o systemie i usługach na nim działających, innymi słowy logów. Mógłbym się rozpisywać o tym jak działa, jednak jeśli ktoś potrzebuje dokładnych informacji to zapraszam do dokumentacji. Wiem, że jest słabą prozą napisana i nie zawsze wciąga, jednak tego czego potrzebować będziemy – zaprawdę powiadam Wam – się z niej dowiecie. Wróćmy zatem do realizacji naszego tematu. Co musimy wiedzieć.

Rsyslog jest konfigurowalny. W katalogu /etc mamy podstawowy plik konfiguracyjny o nazwie rsyslog.conf, gdzie zapisane są poszczególne konfiguracje dla modułów, dyrektyw czy ról. Jest też tam zapis o tym skąd ma brać dodatkowe konfiguracje dla systemu logowania – w naszym przypadku, jak i w większości, jest to katalog: /etc/rsyslog.d. W tym miejscu utworzymy plik o nazwie bash.conf (nazwa mało ważna w przeciwieństwie do lokalizacji tego pliku). Wspomniany plik będzie posiadał jedną linię. Jeżeli pomimo szczerej niechęci zajrzeliście do dokumentacji rsyslog to mogło Wam się rzucić w oczy coś takiego, co nazywa się facility i priority. W tym przypadku facility nazwijmy słowem kluczowym, które określa nam typ programu, który generuje logi. Są tam dla przykładu facility typu cron, czyli wiadomości z demonów crond i atd, kern –  wiadomości jądra, ale są również słowa kluczowe zarezerwowane do użytku lokalnego: local0-local7. Piority oznacza poziom wagi wiadomości jaki ma zostać zapisany: crit – krytyczny, warning ostrzeżenie etc. Zarówno facility jak i priority można zastąpić znakiem * oznaczającej wszystkie alternatywy. Do naszego przykładu wykorzystamy local6.* i dokonamy wpisu w naszym bash.conf w postaci:

local6.* /var/log/commands.log 
co oznacza ni mniej, ni więcej, że wszystkie logi dla facility local6 i wszystkich dostępnych priorytetów mają lądować w lokalizacji w pliku /var/log/commands.log.

Przejdźmy teraz do powłoki.

Jak już wspomniałem wcześniej pod pojęciem powłoki mam na myśli jedną konkretną jaką jest bash, czyli tę najbardziej popularną obecnie, która jest powszechnie używana. Ma ona kilka plików konfiguracyjnych między innymi /etc/profile, ~/.bash_profile, ~/bash_login czy ~/.bashrc. W trakcie zakładania użytkownika kopiowane są pliki konfiguracyjne znajdujące się w /etc/skel. Jeżeli chcecie wiedzieć dokładnie jakie konfigurują powłokę – a musicie jeszcze brać pod uwagę fakt, że rodziny linuxów mają różne podejścia i np. dla rodziny Red Hat w katalogu domowym zawsze po utworzeniu użytkownika będzie .bash_profile to dla przykładu rodzina Debian korzysta z .profile w katalogu domowym – to poczytajcie sobie o tym w dokumentacji, ponieważ jest to bardzo obszerny temat i spokojnie dałoby się o tym co znajduje się w poszczególnych plikach konfiguracyjnych i w jakiej kolejności są czytane, napisać oddzielny artykuł.

My skupimy się na plikach, które są czytane przy każdym wywołaniu powłoki – nieważne czy akurat użytkownik się zalogował, czy też przełączył na inne okno – czyli plikach bashrc. Można byłoby ładować ustawienia, które będziemy implementować w przykładzie w każdym katalogu domowym użytkowników w pliku .bashrc, ale byłoby mówiąc oględnie dość żmudna robota jeżeli tych użytkowników byłoby dużo. Można również wrzucić zapis do /etc/skel gdzie znajduje się .bashrc, który jest kopiowany do katalogów nowo zakładanych użytkowników jednak wtedy już istniejący użytkownicy nie byliby objęci taką konfiguracją. Dlatego też jest jeszcze jedno miejsce w katalogu /etc gdzie znajduje się plik, który każdy użytkownik czyta przy wywołaniu bash. Dla rodziny Red Hat plik konfiguracyjny nosi nazwę bashrc, a dla rodziny Debian bash_bashrc. W tym pliku umieścimy wpisy, które będą generowały zapis do logu po wywołaniu poleceń.

whoami="$(whoami)@$(echo $SSH_CONNECTION | awk '{print $1}')"
export PROMPT_COMMAND='RETRN_VAL=$?;logger -p local6.debug "$whoami [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" ) [$RETRN_VAL] "' 
Pierwsza linia to ustawienie zmiennej środowiskowej, która będzie zawierała użytkownika i z jakiego adresu ten się zalogował, o ile było to logowanie via SSH. Druga wykorzysta zapis z pierwszej deklaracji, czyli whoami i będzie odpowiadała za pokazanie kto i jakie polecenie wywołał oraz wrzuci wynik do logu, który wcześniej definiowaliśmy. Zapisem do logu zajmie się polecenie logger, które właśnie do tego służy, a w opcji -p wskazujemy naszą definicję local6 z priorytetem debug. Wykonana komenda wyciągana jest poleceniem history. Reszta linii stanowi formatowanie wyniku.

Tym optymistycznym akcentem mamy załatwione to, co jest nam potrzebne, żeby log powstawał, a jego zawartość miała sens. Wystarczyłoby zrestartować rsyslogd i zaczęłoby się logowanie. Nie wiem jak Wam, ale mnie się wydaje, że strasznie się nagadałem jak na tak niewiele rzeczy, które trzeba było po stronie systemu zrobić, żeby osiągnąć taki efekt. Jednak warto wiedzieć co z czego wynika – wiedza to podstawa i stąd taki wywód.

No dobrze, logowanie załatwione, jednak chcę mieć jako taki porządek z wynikami naszej pracy. Najlepiej, aby logi były agregowane codziennie. Starsze powinny zostać skompresowane i utrzymywane przez skończoną ilość dni. Taki mechanizm już istnieje w Waszych systemach i nazywa się logrotate. Wystarczy zdefiniować odpowiedni zapis dla naszego logu i voila – będzie Pan zadowolony (oczywiście jeżeli dotyczy to Pani to Pani również będzie zadowolona – przecież nie będziemy nikogo dyskryminować).

Podstawowym logiem dla logtotate jest /etc/logrotate.conf my jednak skorzystamy z możliwości indywidualnej konfiguracji dla naszego rozwiązania. W katalogu /etc/logrotate.d utworzymy plik commands z definicją jak ma się zachowywać logrotate w przypadku naszego logu:

/var/log/commands.log {

rotate 31

daily

compress

delaycompress

notifempty

dateyesterday

dateext

create

postrotate

/usr/bin/killall -HUP rsyslogd

endscript

}

Co poszczególne wpisy oznaczają w krótkich żołnierskich słowach:

- rotate 31 – ile razy ma rotować przed usunięciem

- daily – logi rotowane są dziennie

- compress – stare logi są kompresowane

- przedostatni log nie jest kompresowany

- notifempty – nie rotuj jeżeli log jest pusty

- dateyesterday – użyj daty wczorajszej w rozszerzeniu

- dateext – do nazwy logu dodaj datę (działa z dateyesterday)

- create – po rotacji utwórz nowy log

- postrotate/endscript – uruchom po rotacji (używa /usr/sh)

Polecenie /usr/bin/killall -HUP rsyslogd reinicjuje logowanie systemowe. Samo polecenie killall nie zawsze jest dostępne w domyślnej konfiguracji na niektórych systemach Linux więc koniecznym rozwiązaniem będzie doinstalowanie pakietu psmisc, który zawiera to polecenie. Jeżeli nie macie wiedzy, a potrzebujecie więcej informacji o działaniu logrotate to zapraszam do dokumentacji, chociaż myślę, że całkiem sporo opcji użyłem w naszym przykładzie, aby unaocznić jego funkcjonalność, a same opcje wytłumaczyłem ludzkim językiem.

Wracamy zatem do leniwego administratora, który nie będzie tego robił ręcznie, bo jak ma to zrobić na wielu serwerach to zostanie trafiony szlagiem. Z pomocą przychodzi automatyzacja by Ansible. I już administrator jest mniej nerwowy bo wystarczy szybki yaml i można uruchomić playbook-a. Przedstawiam rzeczonego yaml’a, a po nim na szybko jeszcze wytłumaczę co się w nim dzieje – chociaż to właściwie to o czym już napisałem, jednak diabeł tkwi w szczegółach.

  - hosts: Linux

remote_user: aciek

become: true

become_method: sudo

vars:

tasks:

- name: Install package psmisc on RedHat

yum:

name: psmisc

state: present

when: ansible_os_family == 'RedHat'

- name: Install package psmisc on Debian

apt:

name: psmisc

state: present

when: ansible_os_family == 'Debian'

- name: Check /etc/rsyslog.d/bash.conf exists

stat:

path: /etc/rsyslog.d/bash.conf

register: bash_conf_check

- name: Create backup of bash.conf to /tmp

copy:

remote_src: yes

src: /etc/rsyslog.d/bash.conf

dest: /tmp/bash.conf

when: bash_conf_check.stat.exists

- name: Create config bash.conf for rsyslog

copy:

dest: /etc/rsyslog.d/bash.conf

content: |

local6.* /var/log/commands.log

- name: Create backup of bashrc to bashrc.old on RedHat

copy:

remote_src: yes

src: /etc/bashrc

dest: /tmp/bashrc.old

when: ansible_os_family == 'RedHat'

- name: Create backup of bash.bashrc to bash.bashrc.old on Debian

copy:

remote_src: yes

src: /etc/bash.bashrc

dest: /tmp/bash.bashrc.old

when: ansible_os_family == 'Debian'

- name: Delete whoami and export PROMPT_COMMAND in bashrc when present on RedHat

lineinfile:

path: /etc/bashrc

regexp: "{{ item.regexp }}"

state: absent

loop:

- { regexp: '^whoami=' }

- { regexp: '^export PROMPT_COMMAND=' }

when: ansible_os_family == 'RedHat'

- name: Delete whoami and export PROMPT_COMMAND in bashrc when present on Debian

lineinfile:

path: /etc/bash.bashrc

regexp: "{{ item.regexp }}"

state: absent

loop:

- { regexp: '^whoami=' }

- { regexp: '^export PROMPT_COMMAND=' }

when: ansible_os_family == 'Debian'

- name: Insert variables whoami and export PROMPT_COMMAND when not present on RedHat

blockinfile:

path: /etc/bashrc

insertafter: EOF

block: |

whoami="$(whoami)@$(echo $SSH_CONNECTION | awk '{print $1}')"

export PROMPT_COMMAND='RETRN_VAL=$?;logger -p local6.debug "$whoami [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" ) [$RETRN_VAL]"'

when: ansible_os_family == 'RedHat'

- name: Insert variables whoami and export PROMPT_COMMAND when not present on Debian

blockinfile:

path: /etc/bash.bashrc

insertafter: EOF

block: |

whoami="$(whoami)@$(echo $SSH_CONNECTION | awk '{print $1}')"

export PROMPT_COMMAND='RETRN_VAL=$?;logger -p local6.debug "$whoami [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" ) [$RETRN_VAL]"'

when: ansible_os_family == 'Debian'

- name: Remove logrotate commands configuration if exists

file:

path: /etc/logrotate.d/commands

state: absent

- name: Create logrotate commands configuration

blockinfile:

path: /etc/logrotate.d/commands

create: yes

block: |

/var/log/commands.log {

rotate 31

daily

compress

delaycompress

notifempty

dateyesterday

dateext

create

postrotate

/usr/bin/killall -HUP rsyslogd

endscript

}

- name: Restart RSYSLOG

systemd:

name: rsyslog

state: restarted

daemon_reload: yes

Jeżeli chcecie liznąć trochę wiedzy na temat Ansible to rzućcie okiem na artykuł #AdminCases – czyli gotowa solucja: jak Ansiblem zainstalować nginx’a i zmienić mu konfigurację SSL tak, aby korzystał z certyfikatów z własnym CA. Jeżeli chcielibyście mieć kilka informacji o tym w jaki sposób używać użytkownika, który nie jest root-em w Ansible lub jak działają niektóre moduły, które zostały tutaj wykorzystane sięgnijcie do artykułu: #AdminCases – czyli gotowa solucja: jak Ansiblem zainstalować i skonfigurować VSFTPD z obsługą FTPS

Lecimy z tym koksem.

Na początek uruchamiamy instalację psmisc bo – jak już pisałem wcześniej – musi być zainstalowany killall, z którego będziemy korzystali w przypadku logrotate, a że mamy 2 rodziny Linux do obsłużenia używamy modułu apt i yum. Następnie sprawdzamy, czy już istnieje /etc/rsyslog.d.bash.conf jeżeli tak, to w następnym kroku zrobimy kopię tej konfiguracji. Kolejnym krokiem będzie założenie pliku /etc/rsyslog.d.bash.conf z wpisem zawierającym facility i priority oraz wskazaniem, gdzie log ma się znajdować. Teraz musimy wykonać kopię w zależności od systemu /etc/bash.bashrc lub /etc/bashrc. Następnie, jeżeli istnieją wpisy whoami i export COMMAND_PROMPT należy je usunąć, a gdy jesteśmy pewni, że ich nie ma wprowadzić nowe wpisy. Przechodzimy do części związanej z logrotate. Usuwamy plik /etc/logrotate.d/commands i zakładamy nowy z wpisami, o których już dość dużo opowiedziałem i wyjaśniłem do czego każda z wykorzystanych opcji służy. Pozostaje nam tylko zrestartować rsyslog w systemd i już możemy witać się z gąską. Wystarczy uruchomić playbook i sprawdzić czy tworzą się logi, gdy używamy lub nasi użytkownicy w systemie wykonują polecenia.

Chcecie zobaczyć, jaki jest efekt naszych sztuczek magicznych? Ano całkiem pozytywny. Jeżeli zostały wykonane jakiekolwiek polecenie przez użytkownika w systemie, to znajdą się one w pliku /var/log/commands.log. Może najłatwiej będzie jak pokażę Wam listing plików logów z kilku dni po rotowaniu oraz zawartość, która zawiera polecenia wykonane na jednym z serwerów testowych. Mamy tam log bieżący commands.log, przedostatni log nie został spakowany zgodnie z parametrem dla logrotate delaycompress, pozostałe logi są spakowane i mają datę jako rozszerzenie, co również było konfigurowane w systemie rotacji. Po wyświetleniu zawartości mamy informacje, kiedy zostało wykonane polecenie, na jakim serwerze, jakim użytkownikiem, jaki był numer procesu, treść samego polecenia oraz oczywiście kod wykonania innymi słowy czy się udało, czy też nie. W listingu widać polecenie ls -l qqqq, które zwróciło kod błędu. Zresztą sami rzućcie okiem.

Tym optymistycznym akcentem dotarliśmy do końca naszej permanentnej inwigilacji i od teraz będziemy mogli znaleźć co kto nabroił, co powinno znacznie ułatwić nam dochodzenie, do tego dlaczego coś działało, a teraz już nie działa lub też kto był łaskaw usunąć plik, który był i znikł. Mam nadzieję, że odrobina wiedzy na temat systemu operacyjnego Linux i tego co zawiera i gdzie szukać plików konfiguracyjnych czy też jakie są opcje również się przyda w przyszłości.

Liczę na to, że przedstawiłem ten temat w możliwie przyjazny i zrozumiały sposób, a jeżeli nie to proszę o odzew lub pytania, na które postaram się odpowiedzieć. Pozdrawiam Was serdecznie i mam nadzieję, że permanentna inwigilacja okaże się ładnym cackiem :)


Adam Paszkiewicz

Adam aka aciek, Ekspert ds. Technologii z Asseco Warszawa. Z technologiami związany od okresu dojrzewania (a ten był dawno temu) ale gotować i żeglować też potrafi. Rzeczy niemożliwe realizuje od ręki, na cuda trzeba chwilę zaczekać. Uważa, że w życiu jak i w pracy dobrze mieć kupę radości ... z przewagą radości.


Wydrukuj