Jak uzależnić wykonanie skryptu powłoki od systemu operacyjnego dla platform Unix-like
Zacznijmy od tego, że każdy rasowy administrator systemów operacyjnych wywodzących się z Unixa powinien umieć tworzyć skrypty w różnych powłokach. Oczywiście coraz częściej i szerzej administratorzy korzystają z potęgi języków programowania takich jak Python, które dają bardzo wiele możliwości, jednak wiele rzeczy można szybko i bezproblemowo oprogramować, korzystając z potencjału, jakie daje możliwość programowania w powłokach.
Zaprawdę powiadam Wam, że w większości przypadków nie ma sensu strzelać do wróbla z armaty i tworzyć potworów. Ja osobiście staram się być wierny starej regule o bardzo wdzięcznej nazwie KISS, czyli Keep It Simple, Stupid – co w moim rozumieniu oznacza rób to co masz do zrobienia w maksymalnie prosty sposób. I jeszcze jedno - chociaż nie wszyscy się z tym zgadzają – tworzenie skryptów shell jest również programowaniem… kropka.
To wróćmy do Shells. Oczywiście jest wiele powłok, jednak obecnie najczęściej stykamy się z Bash, czyli Bourne-Again Shell, która w większości Linuxów jest standardem oraz Ksh, innymi słowy, Korn Shell - ze względu na starsze systemy, jak AIX czy też HP-UX, które Ksh używają jako powłoki podstawowej. Oczywiście nie należy zapominać o podstawowej sh (Bourne Shell), dostępnej na wszystkich systemach uniksopodobnych.
Każda z powłok ma swoją specyfikę, jednak nie będziemy się skupiać na tychże, a raczej musimy brać je pod uwagę ze względu na to, o czym wspomniałem, czyli standard dla systemu operacyjnego.
Zacznijmy od przedstawienia problemu. Oto, na czym polega nasz przykład i co chcemy w nim osiągnąć: mamy za zadanie sprawdzenie błędnych logowań do systemu operacyjnego w celu ich monitorowania. Wybrałem taki przykład ze względu na to, że każdy system operacyjny jest specyficzny, a jego specyfika - patrząc przez prymat zadania - polega na tym, że logi, w których odkładają się informacje o logowaniach, znajdują się w różnych miejscach i mają różne nazewnictwo.
Oczywiście można logować się na każdy host i wykonywać polecenie, które wyciągnie interesujące nas dane, jednak my stworzymy jeden prosty skrypt, który rozpozna, z jakiej rodziny wywodzi się system operacyjny, a mając tę informację, wywoła odpowiednie polecenie na tym logu, które wskaże próby nieprawidłowych logowań.
Przedstawmy zatem podział systemów operacyjnych, jaki jest nam niezbędny do wykonania zadania wraz z lokalizacją i nazewnictwem interesującego nas logu:
- Red Hat(RHEL, CentOS, Oracle Linux) - /var/log/secure
- Debian(Debian, Ubuntu) - /var/log/auth.log
- Solaris- /var/log/authlog
- AIX- /var/log/messages
- HP-UX- /var/adm/syslog/syslog.log
- Vmware- /var/log/auth.log
To tyle, jeżeli chodzi o to, skąd wyciągnąć informację. Od razu mogę zastrzec, że badane są tylko takie systemy, ponieważ akurat takimi dysponowałem. W razie potrzeby można dopisać kolejne.
Przystąpmy do działania.
Pierwsza rzecz, o której musimy pamiętać i o której już wcześniej napomknąłem – systemy operacyjne mogą mieć różne powłoki i nie wszystkie będą w posiadaniu Basha, więc składnia, dla tego konkretnego przypadku musi spełniać warunki wykonania na powłoce Bourne Shell, czyli sh. Dlaczego? Tutaj musimy mieć wiedzę odnośnie systemów operacyjnych i wykorzystywanych przez nie powłok. Jeżeli chodzi o nasz przykład, to z Bourne Shell (sh) korzysta Vmware (i jest dostępny na wszystkich innych systemach związanych z UNIX).
W przypadku, gdy piszemy skrypt przeznaczony do pracy na wielu systemach i różnych powłokach, nie należy podawać w shebang line (czyli na początku skryptu) ścieżki do konkretnej powłoki – domyślnie skorzysta z Bourne Shell (sh). Można skorzystać ze składni:
#!/usr/bin/env sh
Co dalej? Zajmijmy się znalezieniem rodziny systemu operacyjnego no i dla porządku nazwy hosta. Skorzystam tutaj z polecenia uname -a
, którego wywołanie wygląda dla przykładu:
[aciek@nielot os_scripts_and_configs]$ uname -a
Linux nielot 3.10.0-1160.2.1.el7.x86_64 #1 SMP Tue Oct 6 10:02:36 PDT 2020 x86_64 x86_64 x86_64 GNU/Linux
aciek@solaris:~$ uname -a
SunOS solaris 5.11 11.3 i86pc i386 i86pc
Powyżej mamy wyniki polecenia uname
z dwóch systemów operacyjnych: Linux oraz Solaris. Oczywiście na innych wygląda nieco inaczej, ale uname -a
działa na każdym z Unix-like systemów operacyjnych.
Wróćmy do naszego programu. Do wyciągnięcia danych skorzystam z języka AWK. Składnia, z której skorzystam, wygląda następująco:
SYSTEM=$(uname -a |awk ' { print $1 }')
NAME=$(uname -a |awk ' { print $2 }')
Oczywiście w przypadku nazwy hosta można skorzystać z innych poleceń, jak na przykład hostname
, ale to już według uznania.
No dobrze, teraz podzielimy systemy na dwie gałęzie: Linux oraz wszystkie inne Unix-like. Wykorzystamy do tego dane, które przypisaliśmy do zmiennej SYSTEM. Jeżeli chodzi o to, co Linuxem nie jest, to mamy jasną sytuację. Każdy system w polu pierwszym polecenia uname -a
ma unikalny identyfikator, po którym go rozpoznamy:
- SunOS to Solaris
- AIX to AIX
- Hpux to HP-UX
- VMkernel to VmWare
W przypadku Linuxa polecenie uname
pokazuje nam string „Linux” niezależnie od dystrybucji. Musimy zdobyć niezbędną dla nas informację, czyli to, z jakiej rodziny dystrybucja się wywodzi, bo to wiąże się z lokalizacją logów. Na potrzeby naszego przypadku potrzebujemy wiedzieć, czy dystrybucja, którą badamy, jest pochodną Red Hat czy też Debiana. Wszystkie dystrybucje Linux, które wywodzą się od Red Hat, mają w katalogu /etc plik o nazwie redhat-release. Jeżeli taki plik nie występuje, dystrybucja ma inne pochodzenie.
Z tego skorzystamy, pisząc drugi warunek. Następnie sprawdzimy, czy to znana nam dystrybucja Linux, poprzez odczytanie pliku os-release w katalogu /etc (plik ten istnieje również w systemach Red Hat - znajduje się tam zapis o szczegółach dystrybucji np. Oracla Linux). Jeżeli pplik istnieje, to możemy założyć, że to Debian (chociaż wiem, że i Suse korzysta z tego pliku). W razie potrzeby można tutaj doczytać informację o dokładnej dystrybucji z os-release jednak dla naszego programu jest to zbędne.
W tym momencie wystarczy przy konkretnych warunkach, które rozpoznają system lub dystrybucję, wstawić polecenia wyszukujące, czyli grep dla stringu Failed w odpowiednich logach i winniśmy otrzymać wyniki. Składnia powinna wyglądać następująco:
SYSTEM=$(uname -a |awk ' { print $1 }')
NAME=$(uname -a |awk ' { print $2 }')
Oczywiście moglibyśmy jeszcze sprawdzać, czy istnieje plik logu w danej lokalizacji, ale w jaki sposób to zrobić, przedstawię w innym przykładzie.
if [ $SYSTEM = "Linux" ]
then
if [ -f /etc/redhat-release ]
then
cat /var/log/secure|grep "Failed"
else
if [ -f /etc/os-release ]
then
cat /var/log/auth.log|grep "Failed"
else
echo "Unknown Linux Distribution"
fi
fi
else
if [ $SYSTEM = "SunOS" ]
then
cat /var/log/authlog|grep "Failed"
fi
if [ $SYSTEM = "AIX" ]
then
cat /var/log/messages|grep "Failed"
fi
if [ $SYSTEM = "HP-UX" ]
then
cat /var/adm/syslog/syslog.log|grep "Failed"
fi
if [ $SYSTEM = "VMkernel" ]
then
cat /var/log/auth.log|grep "Failed"
fi
fi
Możemy teraz zawołać nasz skrypt. Z racji tego, że będziemy sięgać do logów systemowych, aby mieć do nich uprawnienia należy użyć sudo.
aciek@nielot os_scripts_and_configs]$ sudo ./check_failed.sh
Linux nielot
FAILED LOGIN
Dec 2 22:04:26 nielot sshd[1932]: Failed password for aciek from 192.168.0.100 port 62632 ssh2
Dec 2 22:04:30 nielot sshd[1932]: Failed password for aciek from 192.168.0.100 port 62632 ssh2
Dec 2 22:04:33 nielot sshd[1932]: Failed password for aciek from 192.168.0.100 port 62632 ssh2
Skrypt można również zawołać zdalnie przez ssh np. ssh root@server < check_failed.sh
, pamiętając o tym, że potrzebujemy odpowiednich uprawnień, aby móc czytać logi lub innymi sposobami, chociażby via Rundeck.
Podsumowanie
Skrypty shell są naprawdę fajne i dość proste, a ich podstawy winien znać każdy, kto cokolwiek robi w systemach Unix-like. I tutaj możemy dojść do dość prostego wniosku: jeżeli znasz system operacyjny i jego polecenia, to nie ma problemu, aby z nich skorzystać, tworząc skrypty, które zapewne ułatwią Ci życie. I jeszcze jedno - można robić różne cuda na kiju i wodotryski – jednak nie zapomnijmy o podstawach - no i oczywiście o regule KISS!
Do zobaczenia i niech Shell będzie z Wami :)