Red Hat Enterprise Linux 9 - Red Hat Certified System Administrator

Źródła:

  1. RHCSA® Red Hat® Enterpise Linux® 8 (UPDATED). Training and Exam Preparation Guide. Second Edition. Asghar Ghori
  2. How to configure a storage device to use VDO deduplication and compression in RHEL 9?
  3. RHEL 9 networking: Say goodbye to ifcfg-files, and hello to keyfiles
  4. Strony podręcznika Red Hat Enterprise Linux 9
 ____          _ _   _       _   
|  _ \ ___  __| | | | | __ _| |_ 
| |_) / _ \/ _` | |_| |/ _` | __|
|  _ <  __/ (_| |  _  | (_| | |_ 
|_| \_\___|\__,_|_| |_|\__,_|\__|
                                 

1. Wstęp

Chcąc zmienić coś w swoim życiu postanowiłem, że zacznę od pracy. Dalej chciałem pracować w IT, tylko na innym stanowisku (praca szkolnego administratora jest porównywalna z pracą technika IT, tu ma ktoś problem z systemem, tu trzeba podłączyć rzutnik, szczerze to znudziła się praca tego typu). Dobrze czułem się pracując z Linuksem, to była moja pasja. Znałem dwa stanowiska na których mógłbym pracować: Linux System Administrator lub Linux System Engineer. Teraz na rynku pracy jeśli chodzi o IT ważniejsze są uzyskane certyfikaty i odbyte kursy niż wykształcenie akademickie. Szukając certyfikatów, które spowodują większe szanse na zatrudnienie, znalazłem dwa. Linux Fundation oraz Red Hat. Po przeczytaniu postu a na Reddit doszedłem do wniosku, że lepszym wyborem będzie Red Hat. Jeśli chodzi o produkcyjne wykorzystanie Linuksa to większość używa Red Hat Enterprise Linux lub jego darmowego odpowiednika. Kiedyś CentOS ale obecnie może być to zarówno Rocky lub Alma Linux, oba są oparte na tym systemie. Szukając jakiś materiałów w internecie, które pomogły by mi się przygotować natrafiłem na Amazonie na książke Asghara Ghori pt. RHCSA Red Hat Enterprise Linux 8 (UPDATED): Training and Exam Preparation Guide (EX200), Second Edition zamówiłem ją na swojego Kindla i zacząłem przygotowywać się do egzaminu.

1.1. Egzamin

Egzamin EX200 dający uprawnienia Red Hat Certified System Administrator jest egzaminem praktycznym sprawdzającym umiejętność instalacji, konfiguracji oraz rozwiązywania podstawowych problemów administracyjnych na dystrybucji Red Hat. Egzamin będzie odbywać się w postaci elektronicznej i zdający będzie mięć do dyspozycji dwie maszyny wirtualne do wykonania zadań egzaminacyjnych. Takie też środowisko przygotujemy sobie do nauki. Na dzień 11 listopada 2020 (tak jak podaje książka) egzamin obejmuje 69 zagadnień jednak ta liczba, ze względu na rozwój technologii jak i samego RHEL-a mogła ulec już zmianie.

1.2. Zagadnienia

Zagadnienia używane na egzaminie mogą cały czas ewoluować. Odnośnik obok przedstawia aktualne zagadnienia egzaminacyjne: http://www.redhat.com/training/courses/ex200/examobjective. Obecnie egzamin zadawany jest na RHEL 9, wydanym 17 maja 2022. Dość istotną zmianą w produkcji tego systemu jest zmiana upstreamu. Ta wersja Red Hat nie jest już oparta na dystrybucji Fedora tak jak wszystkie do tej pory, ale na CentOS Stream (nowa wersja non-stream tej dystrybucji nie jest już dostępna stąd rozbicie społeczonościowych dystrybucji do zastosowań produkcyjnych na Rocky oraz Alma Linux) i tej wersji będziemy używać na maszynach wirtualnych.

1.3. Przygotowania

Kiedy uznałem, że chce zdobyć RHCSA, uzyskałem Red Hat na zasadzie Non-cost RHEL Individual Developer Subscription, gdzie istotne informacje znajdują się w odnośniku obok: https://developers.redhat.com/articles/faqs-no-cost-red-hat-enterprise-linux. Przy użyciu tej subskrypcji uzyskamy dostęp do Red Hat przez rok na 16 instalacji (bez znaczenia czy to fizyczny komputer, czy maszyna wirtualna). Po tym czasie należy zalogować się ponownie, zakceptować warunki umowy i do około 30 minut powinnimy uzyskać ponowny rok subskrypcji i tak co roku. Wszystko jest opisane na powyższej stronie. System w wersji 9 zainstalowałem na komputerze przeznaczony do codziennego użytku. Najlepszym sposobem na nauczenie się administracji systemem jest jego używanie na co dzień, a że jest entuzajstą a nie profesjonalistą, mój kontakt z systemami tego typu jest raczej dość mocno ograniczony. Dlatego też przesiadłem się na RHEL, póki co to ten system poprostu działa, wcześniej korzystałem z Fedory 35 oraz Debiana 11. Do celów dydaktycznych potrzebujemy peceta lub laptopa z min. dwurdzeniowym procesorem z możliwością wirtualizacji 64-bitowych systemów, 8GB pamięci operacyjnej oraz min. 40GB wolnej przestrzeni na dysku na potrzeby utworzenia maszyn wirtualnych z wymagną przez ćwiczenia przestrzenią dyskową. Do tego celu możemy użyć dowolnego środowiska wirtualizacji, które znamym. Jeśli nie mieliśmy wcześniej żadnego kontatku z tego typu oprogramowaniem, to najlepiej jest użyć oprogramowania Oracle VirtualBox, dającego najprostszy interfejs do interakcji z maszynami, gdyż przy późniejszych zagadnieniach będziemy musieli zmienić jej ustawienia lub dodać kilka urządzeń. Czy ktoś zainstaluje na swoim komputerze RHEL czy nie to, już indywidualna sprawa i nie wypłynie ona wykonanie ćwiczeń. Nie mniej jednak, w podpunkcie odnośnie maszyn wirtualnych tego rodziału przedstawie skrypt powłoki, który po uruchomieniu utworzy automatycznie maszyny wymagane do wykonania ćwiczeń. Ale póki co przedstawie teraz instalacje Oracle VirtualBox na RHEL 9.

1.3.1. Instalacja Oracle VirtualBox na RHEL 9.0

Pierwszą czynnością jest uruchomienie przeglądarki i przejście pod adres: https://www.virtualbox.org/wiki/Linux_Downloads, na samym dole strony znajdują się pliki repozytorium dla kilku dystrybucji Linuksa. Kopiujemy adres odnośnika do pliku konfiguracyjnego repozytorium. Następnie możemy wydać polecenia:

[xf0r3m@primeb450 ~]$ sudo wget https://download.virtualbox.org/virtualbox/rpm/el/virtualbox.repo -O /etc/yum.repos.d/virtualbox.repo
[xf0r3m@primeb450 ~]$ sudo dnf update
Updating Subscription Management repositories.
Red Hat Enterprise Linux 9 for x86_64 - AppStream (RPMs)        6.3 kB/s | 4.1 kB     00:00    
Red Hat Enterprise Linux 9 for x86_64 - BaseOS (RPMs)            10 kB/s | 4.1 kB     00:00    
Oracle Linux / RHEL / CentOS-9 / x86_64 - VirtualBox             99  B/s | 181  B     00:01    
Oracle Linux / RHEL / CentOS-9 / x86_64 - VirtualBox            1.9 kB/s | 1.7 kB     00:00    
Importowanie klucza GPG 0x98AB5139:
 Identyfikator użytkownika: „Oracle Corporation (VirtualBox archive signing key) <info@virtualbox.org>”
 Odcisk                   : 7B0F AB3A 13B9 0743 5925 D9C9 5442 2A4B 98AB 5139
 Z                        : https://www.virtualbox.org/download/oracle_vbox.asc
W porządku? [t/N]: t
Oracle Linux / RHEL / CentOS-9 / x86_64 - VirtualBox                    8.6 kB/s | 7.8 kB     00:00    
Rozwiązano zależności.
Nie ma nic do zrobienia.
Ukończono.
[xf0r3m@primeb450 ~]$ sudo dnf install VirtualBox-6.1 kernel-devel
Updating Subscription Management repositories.
Ostatnio sprawdzono ważność metadanych: 0:04:10 temu w dniu sob, 13 sie 2022, 10:12:38.
Rozwiązano zależności.
...

Po instalacji restartujemy komputer.

[xf0r3m@primeb450 ~]$ sudo reboot

Teraz Oracle VirtualBox jest gotów do działania.

Jeśli chcemy możemy użyć RPMFusion. Po załadowaniu tych repozytoriów do swojego systemu możemy od razu wydać polecenie:

[user@centos9 ~]$ sudo dnf install VirtualBox kernel-devel
...
[user@centos9 ~]$ sudo su
[root@centos9 user]# akmods
Checking kmods exist for 5.14.0-142.el9.x86_64 [  OK  ]
[root@centos9 user]# sudo systemctl restart vboxdrv.service
[root@centos9 user]# sudo systemctl status vboxdrv.service
● vboxdrv.service - Linux kernel module init script
     Loaded: loaded (/usr/lib/systemd/system/vboxdrv.service; enabled; vendor preset: enabled)
     Active: active (exited) since Mon 2022-08-15 16:13:27 CEST; 6s ago
    Process: 62653 ExecStart=/sbin/modprobe vboxdrv (code=exited, status=0/SUCCESS)
    Process: 62657 ExecStart=/sbin/modprobe vboxnetflt (code=exited, status=0/SUCCESS)
    Process: 62658 ExecStart=/sbin/modprobe vboxnetadp (code=exited, status=0/SUCCESS)
   Main PID: 62658 (code=exited, status=0/SUCCESS)
        CPU: 133ms
...

1.3.2. Pozyskanie obrazu ISO z Red Hat Enterprise Linux 9

Aby pobrać potrzebny nam obraz musimy zarejestrować się w subskrypcji dla indywidualnych deweloperów. Przechodzimy na stronę https://developers.redhat.com/register i rejestrujemy się. Następnie logujemy i po zalogowaniu przechodzimy na stronę: https://developers.redhat.com/products/rhel/download. w sekcji All Downloads znajdują się wszystkie dostępne wersje RHEL objęte jeszcze wsparciem. Nas będzie interesować wyłącznie wersja 9 i tą też pobieramy, oczywiście dla architektury x86_64 w wersji DVD.

1.3.3. Tworzenie maszyn wirtualnych z RHEL

Tworzenie maszyn wirtualnych, nie jest zaganieniem egzaminacyjnym. Na egzaminie dostaniemy dostęp do dwóch zdalnych maszyn. Teoretycznie można by użyć fizycznych maszyn o ile ktoś ma do takowych dostęp, jednak do drugiej maszyny trzeba dodać aż 8 dysków, więc użycie wirtualnej maszyny, to chyba jedyne efektywne rozwiązanie. Poniżej znajdują się wymagania jakie powinna spełniać każda z nich:

Maszyny możemy utworzyć ręcznie, zgodnie z wtycznymi dla używanego przez nas środowiska wirtualizacji. Jeśli jednak korzystamy z VirtualBox możemy wykorzystać poniższe skrypty. W przypadku drugiej maszyny dodatkowe dyski trzeba będzie dodać z poziomu ustawień maszyny.

Powyższe skrypt są również dostępne na moim profilu na github-ie. Pod poniższymi linkami.

Korzystając ze skryptów należy pamiętać o zmianie intefejsu sieciowego wykorzystywanego do mostkowania. Zmienna bridge_if.

Po utworzeniu maszyn przyszedł czas na instalację.

1.3.4. Instalacja Red Hat Enterprise Linux 9.0

Zaraz po uruchomieniu maszyny rozpocznie się proces ładowania zawartości płyty. Menu bootowania płyty zawiera trzy opcje.

  1. Install Red Hat Enterprise Linux 9.0
  2. Test this media & install Red Hat Enterprise Linux 9.0
  3. Troubleshooting

Domyślnie zaznaczoną jest opcja numer 2. Mamy minutę na zmianę decyzji. Naciśnięcie dowolnego klawisza zatrzymuje odliczanie. Dając nam czas do namysłu, jednak na ten moment nie ma co za bardzo rozmyślać dlatego też wybieramy opcję numer 1. Po wybraniu tej opcji nastąpi uruchomienie systemu oraz instalatora. Instalator używany przez RHEL oraz dystrybucje z nim związane nazywa się Anaconda. Swoje działanie rozpoczyna jeszcze w trybie tekstowym, następnie domyślnie uruchamia tryb graficzny, gdzie odbywa się konfiguracja instalcji a następnie już właściwa instalacja. Nie mniej jednak instalacja w trybie tekstowym jest również możliwa. Podczas instalacji, poza głównym instalatorem będziemy mieć do dyspozycji multipekser terminala, który do momentu uruchomienia właściwego instalatora (w wersji graficznej) był główną konsolą. W multiplekserze będzie my mieć dostęp do komunikatów diagnostyczny, które w wersji 8 RHEL był wyświetlane na oddzielnych konsolach; kilka powłok z uprawnieniami superużytkownika (uruchomione na konsolach 2, 3 i 5) oraz konsolę z komunikatami przechwytywanymi przez główny rejestrator systemu rsyslog. Udostępnienie tych funkcjonalności podczas instalacji systemu, może pomóc w ewentualnym diagnozowaniu i naprawie problemów z instalacją. Pliki z komunikatami na czas instalacji przechowywane są w katalogu /tmp, po instalacji zostaną one przeniesione do katalogu /var/log/anaconda. Według mnie jest dość przydatna funkcjonalność, ponieważ na podstawie czasu modyfikacji tych plików możemy dowiedzić się kiedy system został zainstalowany. Poniżej znajduje się lista, opisująca za co odpowiada konkretna konsola (kolejny ekran, dostępny po naciśnięciu klawiszy Ctrl + Alt + F1 - F6).

Instalacja Red Hat Enterprise Linux rozpoczyna się od wyboru języka procesu instalacji. Chcąc pracować już w takim prawowitym IT, należy posługiwać się językiem angielskim i to technicznym. Systemy z którymi zderzmy się w potencjalnej pracy raczej będą systemami o zasięgu międzynarodowym, prawdopodobnie już o tym wspominałem ale egzamin pozwalający uzyskać certyfikacje RHCSA również będzie po angielsku. Dlatego też instalować jak i korzystać z systemu będziemy w języku angielskim, na pierwszym ekranie wybieramy English pozostawiając wariant English (United States). Następnym ekranem jest Installation Summary (lewy górny róg), jest tak jakby pulpit programu instalacyjnego, na nim występują komponenty, które możemy skonfigurować. W wersji 9.0, te komponenty są podzielone na 4 sekcje: Localization, Software, System oraz User Settings. W pierwszym procesie instalacji, przejdziemy przez wszystkie z nich.

Zwróć my uwagę na to, iż system sam dokonał wstępnej konfiguracji instalatora, na pozostało jedynie sprawdzić oraz skonfigurować tylko te opcje oznaczone znakiem ostrzegawczym oraz czerwoną podpowiedzią. Po ich konfiguracji program umożliwi nam zainstalowanie systemu.

W sekcji lokalizacyjnej do ustawienia są takie opcje, układ klawiatury, język, oraz data i czas. Podczas ładowania pierwszego okna (moment wybrania języka instalacji), instalator na podstawie informacji GeoIP postara się określić naszą przybliżoną lokalizację, o ile posiada on na tym momencie połączenie z internetem. Dlatego też możemy spotkać się z samoinstnie wybranym języku instalacji ustawionym na Polski. Język został zmieniony, ale wybrana strefa czasowa może pozostać, wręcz powinna. Dzięki tej funkcji możemy zaoszczędzić czas, i przejść do kolejnej sekcji.

Kolejną sekcja dotyczy oprogramowania, tutaj konfigurowane będzie na przykład połączenie z firmą Red Hat. RHEL jest system licencjonowanym i jeśli chcemy skorzystać z oficjalnych repozytoriów należy "podłączyć" system do naszego konta założonego w celu pozyskania obrazu płyty. Jednak na razie nie trzeba tego, będzie to do zrobienia na późniejszych etapach nauki administracji systemem. Kolejną opcją jest Installation Source (Źródło instalacji), konfigurąc tę opcję możemy wybrać czy będziemy korzystać pod czas instalacji z obrazu płyty lub sieciowego repozytorium lub Red Hat CDN. Z racji tego, iż ściągneliśmy pełny obraz ważący kilka gigabajtów, nie ma sensu używać instalacji sieciowej. Tego typu źródło instalacji należy wybrać w momencie gdy pobraliśmy minimalistyczną wersję obrazu, dlatego też zostaniemy przy standardowym wyborze Local media. Ta opcja pozwala też na dodanie podczas instalacji dodatkowego repozytorium, jednak na tym etapie nie będziemy się tym zajmować. Ostanią opcją z tej sekcji jest Software selection, ta opcja daje nam możliwość, wyboru rodzaju instalacji. Na potrzeby nauki, wybierzemy opcję Server with GUI, jeśli będziemy instalować RHEL na naszym komputerze do codziennego użytku możemy wybrać Workstation. Wybierając rodzaj instalacji po lewej stronie, po prawej wyświetają się dodatkowe pakiety lub warianty do zainstalowania.

Sekcja System, służy głównie do wyboru docelowego miejsca dla instalacji. W opcji Instalation Destination, wskazujemy docelowy dysk do instalacji, możemy go spartycjonować ręcznie lub automatycznie możemy również zaszyfrować partycje, jeśli jest taka potrzeba. W przypadku szyfrowania danych bardzo ważny jest układ klawiatury. Innymi opcjami póki co dla nas ważnymi jest Network & Host name, w założeniach zadania, może być wymagane ustawienie odpowiedniego adresu oraz nazwy dla maszyny, to te opcje ustawiamy właśnie tam. Pozostałe opcje tej sekcji, poki co nie są dla nas istotne.

Ostatnia sekcja ustawia hasło dla superużytkownika oraz tworzy konto dla pierwszego użytkownika. Nie musi on posiadać uprawnień administratora i nie będzie ich miał. Opcja utworzenia użytkownika może się nie mieścić na ekranie, podczas instalacji systemu maszynie witualnej, dlatego nie należy przejmować, że jej nie ma. Wystarczy przewinąć wyświetlany oraz paskiem przewiajania po prawej stronie.

Teraz kiedy wszyskie (te istotne dla instalacji) opcje zostały opisane możemy zainstalować system zgodnie założeniami pierwsze maszyny i powtorzyć to na drugiej. I to jest pierwsze ćwiczenie.

Ćwiczenie 1: Instalacja Red Hat Enterprise Linux 9

Stwórz zgodnie założeniami podanymi w podrozdziale Tworzenie maszyn wirtualnych z RHEL maszynę RHEL-VM1. Następnie korzystając z sugestii podanych podczas opisu procesu instalacji zainstaluj na niej Red Hat Enterprise Linux 9. Powtórz tę czynność na drugiej maszynie przeznaczonej do laboratorium. Pamiętaj, że założenia dla drugiej maszyny różnią się.

Ćwiczenie 2: Logowanie zdalne do RHEL

Wykorzystując program do obsługi protokołu SSH dla twoje systemu operacyjnego zaloguj się na superużytkownika. Pamiętaj o tym, że bezpośrednie logowanie na superużytkownika wymagało zaznaczenia opcji w trakcie ustawiania hasła dla niego podczas instalacji.

Podsumowanie

W tym rozdziale przeszliśmy przez proces instalacji RHEL. Dowiedzielśmy się, że możliwa jest instalacja w trybie tekstowym oraz oraz, że nie należy ona do najtrudniejszych. Warto zwrócić uwagę na to iż jeśli do tej pory korzystaliśmy z jakiś materiałów to dotyczą one wersji 8 RHEL, my będziemy korzystać z wersji 9, bo to na niej w 2022 roku będziemy przeprowadzany egzamin, a między tymi wersjami jest kilka zmian. W następnym rozdziale zapoznamy się z środowiskiem graficznym, strukturą katalogów oraz podstawowym narzędziami do pracy w powłoce.

2. Poruszanie się po systemie RHEL 9

Wykonując ćwiczenia z pierwszego rozdziału, zainstalowaliśmy sobie Red Hat w wersji 9 na maszynie wirtualnej. Jako wariant wyboru oprogramowania wybraliśmy Server with GUI i od drobnego omówienia środowiska graficznego rozpocznę ten rodział.

2.1. Środowisko graficzne RHEL 9

Red Hat jest dystrybucją Linuksa przeznaczoną do zastosowań produkcyjnych, więc system tego typu musi wykazywać się dużą stabilnością. Znaczące zmiany są wprowadzane raczej w dużych wydania jak RHEL 9, takim znaczącym przeskokiem jest zmiana linii jądra z 4.18 (w przypadku RHEL 8) na 5.14 (w przypadku RHEL 9) oraz zastąpienie klasycznego serwera X Window protokołem Wayland, to jednak nastąpiło z wydaniem RHEL 8. Zmiany w RHEL 9, odnośnie środowiska graficznego to przejście z Gnome 3.38 na Gnome 40, ta zmiana przyniosła również odświerzenie menedżera wyświetlania lub menedżera logowania czym w przypadku środowiska Gnome jest GDM. Większość elementów pozostała taka sama na górnej belce mamy datę oraz czas w prawyn górnym rogu do wyboru opcje ułatwień dostępu, wskaźnik połączenia sieciowego oraz porozwinięciu możliwość regulacji głośności i jasności ekranu oraz wyłączenia komputera. Wiecej opcji w tym miejscu będzie dostępne dopiero po zalogowaniu. Będzie można zmienić kilka ustawień jak np. sieć bezprzewodwą. Zmianie uległ również sposób prezentacji kont użytkownika. Z racji tego iż root nie jest zwykłym użytkownikiem nie będzie pojawiać się na liscie, ale zalogowanie na niego jest możliwe za pomocą kliknięcia w Not Listed? pod listą użytkowników, wówczas będzie mogli ręcznie podać nazwę użytkownika oraz hasło. Ekranem startowym po zalogowaniu jest ekran Activities, który pozwala na podgląd wykonywanych czynności, rozpoczęcie nowych (wybierając aplikacje z doka lub z pełnej listy aplikacji, która jest dostępna po kliknięciu na ikonę 9 kropek w doku) oraz przełacznie się między obszarami roboczymi. Domyślnie GNOME daje dostęp do dwóch przestrzeni roboczych, gdy tylko przeniesiemy aplikację do na nowy obszar, GNOME udostępni nam kolejny i tak dalej. Wyszukiwarka nad podgląd obszaru roboczego daje możliwość wyszukiwania nie tylko aplikacji ale także ustawień co jest normą w GNOME od wersji 3. Na doku znajdują się ulubione aplikacje, które możemy usuwać i dodawać za pomocą PPM i następnie wybierając dodaj lub usuń z ulubionych.

2.2. Struktura katalogowa w Uniksach

Zanim jednak przejedziemy do obsługi wiersza poleceń omówimy sobie hierachiczną strukturę katalogów, która występuje nie tylko w RHEL ale i w innych Uniksach, ponieważ HFS (ang. Hierarchy File System) jest ogólnym standardem. Na Uniksach możemy spotkać się z trzema typami systemów plików.

Struktura plików i katalogów na Uniksach ma postać odwróconego drzewa. Korzeń (katalog główny) znajduje się na szczycie struktury i od niego rozchodzą się poszczególne gałęzie (podkatalogi), gałęzię mogą zawierać kolejne rozgałezienia, tworząc całe struktury. Na gałęziach znajdują się również liscie czyli pliki i na liściach struktura się kończy.

Dostęp do konkretnych plików i katalogów w strukturze określany jest za pomocą ścieżek. Scieżki określają położenie plików i katalogów wewnątrz HFS. Separatorem elementów ścieżki (posczególnych podkatalogów), jest ukośnik, slash (/).

Podczas pracy z elementami jak foldery oraz pliki możemy używać scieżek bezwzględnych zaczynające się od katalogu głównego, lub ścieżek względnych zaczynających się od elementu znajdującego w obecnym katalogu robocznym (tym katalogu, w którym się obecnie znajdujemy).

Katalog główny jest miejscem do przechowywania wszystkich plików, jakie znajdują się w systemie, jest pierwszym element znajdujący się na ścieżce (bezwzględnej) i oznaczny jest on poprostu ukośnikiem (/), od tego katalogu zaczyna się cała struktura. Poniżej znajduje się lista najważniejszych podkatalogów katalogu głównego, każdy z nich ma swoje znaczenie w systemie.

Za pomocą polecenia tree, możemy wyświetlić strukturę katalogą w postaci drzewa, jednak najpierw nauczymy się korzystać z poleceń w RHEL.

2.3. Obsługa wiersza poleceń i najprostsze polecenia

Obsługa wiersza poleceń zależy w znacznym stopniu od powłoki, ponieważ to ona zajmuje się jego interpretacją i koniec końców wykonaniem programu, którego nazwę podaliśmy chcąc wykonać polecenie. Aby móc skorzystać w RHEL z wiersza poleceń musimy albo uruchomić w środowisku graficznym aplikację Terminal, lub zalogować się zdalnie za pomocą protokołu SSH. Uruchomienie terminala lub zalogowanie się przez SSH uruchomi powłokę, która swoją gotowość na otrzymywanie poleceń obwieści wyświetleniem znaku zachęty.

Znak zachęty jest wskaźnikiem za kursorem po którym możemy wprowadzać do systemu polecenia. Kiedyś znaki zachęty były pojedynczymi znakmi informującymi o uprawnieniach z jakimi działa proces powłoki (o tym za chwilę). Teraz znaki zwracają nazwę użytkownika, nazwę komputera oraz obecny katalog roboczy wewnątrz nawiasu kwadratowego, następnie zanim znajduje się znak krzyżyka (#) mówiący, że powłoka działa uprawnieniami użytkownika root lub symbol dolara ($) mówiący, że połoka działa z uprawnieniami zwykłego użytkownika. Poniżej przedstawiłem oba znaki:

[root@server1 ~]#

[user@server1 ~]$

W różnych dystrybucjach, znak zachęty może różnorako wyglądąć. Możemy dostować go do własnych potrzeb, wiele osób korzysta z jak najmniejszych znaków zachęty, aby zaoszczedzić miejsce w wierszu polecenia. Teraz możemy już wydawać polecenia.

Kazde polecenie w jakimkolwiek Uniksie, składa się z nazwy polecenia, ewentualnych opcji oraz ewentualnych argumentów. Nie wszystkie polecenia do uzyskania żądanych przez nas efektów wymagają opcji czy argumentów.

$ nazwa_polecenia [opcje] [argumenty]

Każde polecenie przed zatwierdzeniem możemy edytować, za pomocą strzałek lub skrótów klawiszowych dostarczanych przez bibliotekę GNU Readline. Więcej na ten temat znajdziemy w internecie.

Przez cały kurs będziemy poznawać jakieś polecenia ale w tym momencie nauczymy się najprostszych z nich.

2.3.1. Listowanie zawartości katalogu

Aby wyświetlić zawartość katalogu w powłoce, możemy użyć polecenia ls. Polecenie bez podania argumentu w postaci katalogu wyświetli zawartość folderu, w którym się znajdujemy. Najważniejszymi opcjami między innymi są:

Poniżej znajduje się przykład najczęściej wykorzystywanego polecenia ls wraz z opcjami:

[root@server1 ~]# ls -al
total 10576
dr-xr-x---. 17 root root    4096 Jun 16 18:49 .
dr-xr-xr-x. 18 root root     235 Jun  9 19:46 ..
-rw-------.  1 root root    1197 Jun  9 19:59 anaconda-ks.cfg
-rw-------.  1 root root    3908 Jun 16 18:50 .bash_history
-rw-r--r--.  1 root root      18 Aug 10  2021 .bash_logout
-rw-r--r--.  1 root root     141 Aug 10  2021 .bash_profile
-rw-r--r--.  1 root root     429 Aug 10  2021 .bashrc
drwxr-xr-x.  3 root root      17 Jun 16 18:14 bzipped
drwx------.  8 root root     120 Jun 13 10:55 .cache
-rw-r--r--.  1 root root      11 Jun 16 13:10 catfile1
drwx------.  9 root root    4096 Jun 13 13:42 .config
-rw-r--r--.  1 root root     100 Aug 10  2021 .cshrc
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Desktop
drwxr-xr-x.  4 root root      30 Jun 16 16:44 dir20
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Documents
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Downloads
drwxr-xr-x.  3 root root      55 Jun 16 11:34 etc
-rw-r--r--.  1 root root 4986788 Jun 16 18:13 etc-bzipped.tar.bz2
-rw-r--r--.  1 root root 5772869 Jun 16 18:12 etc-gzipped.tar.gz
-rw-r--r--.  2 root root       0 Jun 16 17:26 file10
-rw-r--r--.  2 root root       0 Jun 16 17:26 file20
-rw-r--r--.  1 root root     579 Jun 16 11:07 fstab
drwxr-xr-x.  3 root root      17 Jun 16 18:14 gzipped
-rw-------.  1 root root      42 Jun 16 18:22 .lesshst
drwx------.  3 root root      19 Jun 13 10:55 .local
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Music
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Pictures
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Public
lrwxrwxrwx.  1 root root       6 Jun 16 17:30 soft10 -> file10
-rw-r--r--.  1 root root     129 Aug 10  2021 .tcshrc
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Templates
drwxr-xr-x.  2 root root       6 Jun 13 10:55 Videos
-rw-------.  1 root root    8264 Jun 16 18:38 .viminfo
-rw-r--r--.  1 root root     464 Jun 16 18:38 vipractice

Podczas korzystania z wielu programów możemy natknąć się na wiele opcji. Dzielą się one na długie i krótkie, opcje krótkie występują w postaci pojedynczego znaku poprzedzonego jednym myślnikiem (-). Opcje długie występuje w postaci pojedynczego (najczęsciej, ponieważ to zależy od programu) słowa poprzedzonego dwoma myślnikami (--). W przypadku tak prostych poleceń jak ls również możemy użyć długich opcji np. zamiast -a możemy użyć --all.

2.3.2. Wyświetlenie bieżącego katalogu roboczego

Na pierwszy rzut oka mogłoby się wydawać po co nam to polecenie, skoro w znaku zachęty na RHEL jest wyświetlona nazwa bieżącego katalogu. Polecenie pwd wyświetla ścieżkę bezwzględną do bieżącego katalogu, a po drugie to polecenie ma bardzo ważną cechę związaną z dowiązaniami, ale o tym będzie przy dowiązaniach. Do normalnych zastosowań pwd nie potrzebuje żadnej opcji, ani żadnego argumentu.

[root@server1 ~]# pwd
/root

2.3.3. Poruszanie się wśrod katalogów

Generalnie, do zmiany bierzącego katalogu czy przejścia z/do innego katalogu służy polecenie cd. Pozwala ono na kilka sztuczek.

Poniżej zamieściłem przykłady kilku z nich.

[root@server1 ~]# cd -
/home/user
[root@server1 user]# cd ..
[root@server1 home]# cd 

2.3.4. Identyfikacja urządzenia terminala

Czasmi może zajść potrzeba zidentyfikowania własnego urządania terminalowego służy do tego polecenie tty. Jeśli wynikiem działania tego polecenie będzie /dev/pts* oznacza to, że korzystamy z emulatora terminala np. programu Terminal w środowisku GNOME lub połączenia zdalnego za pomocą protokołu SSH. Jeśli naszę urządzenie to /dev/tty* oznacza to, że korzystamy z jednej wirtualnych konsol dostępnych w systemie.

[root@server1 ~]# tty
/dev/pts/0

2.3.5. Czas systemowy oraz załadowanie procesora

Większości dostępnych Uniksów zapewne zawierać będzie polecenie uptime. To niepozorne polecenie zawraca dużą ilość informacji.

[root@server1 ~]# uptime
 13:28:40 up 8 min,  1 user,  load average: 0.00, 0.12, 0.12

2.3.6. Czyszczenie ekranu powłoki

Do czyszczenia terminala ze zbędnego wyjścia poleceń służy polecenie clear lub kombinacja klawiszy Ctrl+l.

[root@server1 ~]# clear

2.3.7. Określanie ścieżki polecenia

Czasami w pewnych konfiguracjach lub też w skryptach powłoki niezbędne będzie podanie ścieżki do konkretnego programu kryjącego się za nazwą polecenia, w RHEL możemy zrobić to na trzy sposoby wykorzystując do tego różne polecenia. Polecenia podałem w przykładzie w raz z informacją zwracaną przez polecenie.

[user@rhel90 ~]$ which ls
alias ls='ls --color=auto'
	/usr/bin/ls
[user@rhel90 ~]$ whereis ls
ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz /usr/share/man/man1p/ls.1p.gz
[user@rhel90 ~]$ type ls
ls jest aliasem do ls --color=auto'

Które z tych poleceń będzie właściwe to już zależy, do czego będą nam potrzebne tego typu informacje. Polecenie whereis wydaje się szczególnie pomocne.

2.3.8. Wyświetlenie informacji o systemie

Polecenie uname należy do poleceń, które są dość niepozorne, ale za pomocą jego opcji możemy otrzymać wersję użytego jądra, architekturę systemu i tym podobne informacje. Polecenie bez podania żadnej z opcji zwraca wyłącznie nazwę systemu i jest to Linux. Aby otrzymać wszelkie zwracane przez to polecenie informacje możemy użyć opcji -a. Resztę opcji znajdziemy na stronach podręcznika (o tym będzie w dalszej części materiału).

[user@rhel90 ~]$ uname -a
Linux latitude-e5270 5.14.0-70.13.1.el9_0.x86_64 #1 SMP PREEMPT Thu Apr 14 12:42:38 EDT 2022 x86_64 x86_64 x86_64 GNU/Linux

2.3.9. Wyświetlanie informacji o procesorze

W RHEL możemy łatwo sprawdzić informacje na temat zainstalowanego w naszym komputerze procesora w przystępny sposób. Do dyspozycji mamy bowiem polecenie lscpu. Wyświetla ono wszystkie dostępne informacje jakie może znaleźć w systemie na temat tego komponentu.

[root@server1 ~]# lscpu
Architecture:            x86_64
  CPU op-mode(s):        32-bit, 64-bit
  Address sizes:         39 bits physical, 48 bits virtual
  Byte Order:            Little Endian
CPU(s):                  1
  On-line CPU(s) list:   0
Vendor ID:               GenuineIntel
  Model name:            Intel(R) Core(TM) i5-6300U CPU @ 2.40GHz
    CPU family:          6
    Model:               78
    Thread(s) per core:  1
    Core(s) per socket:  1
    Socket(s):           1
    Stepping:            3
    BogoMIPS:            5004.62
    Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush
                          mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology
                          nonstop_tsc cpuid tsc_known_freq pni pclmulqdq monitor ssse3 cx16 pcid sse4_1 
                         sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm 3dnowpre
                         fetch invpcid_single pti fsgsbase avx2 invpcid rdseed clflushopt md_clear flush
                         _l1d
Virtualization features: 
  Hypervisor vendor:     KVM
  Virtualization type:   full
Caches (sum of all):     
  L1d:                   32 KiB (1 instance)
  L1i:                   32 KiB (1 instance)
  L2:                    256 KiB (1 instance)
  L3:                    3 MiB (1 instance)
NUMA:                    
  NUMA node(s):          1
  NUMA node0 CPU(s):     0
Vulnerabilities:         
  Itlb multihit:         KVM: Mitigation: VMX unsupported
  L1tf:                  Mitigation; PTE Inversion
  Mds:                   Mitigation; Clear CPU buffers; SMT Host state unknown
  Meltdown:              Mitigation; PTI
  Spec store bypass:     Vulnerable
  Spectre v1:            Mitigation; usercopy/swapgs barriers and __user pointer sanitization
  Spectre v2:            Mitigation; Retpolines, STIBP disabled, RSB filling
  Srbds:                 Unknown: Dependent on hypervisor status
  Tsx async abort:       Not affected

2.4. Uzyskiwanie pomocy

2.4.1. Strony podręcznika

W dystrybucjach Linuksa nie tylko w RHEL możemy znaleźć duża bazę zawierającą opisy poleceń, plików konfiguracyjnych oraz innych komponentów takich jak biblioteki. Służą temu strony podręcznika. Aby uzyskać do nich dostęp wystarczy wydać polecenie man podając jako argument interesujący nas komponent. Nie zawsze musi być to polecenie. Strony podręcznika są podzielone na rozdziały czy też sekcje ich opisy oraz numery sa zawarte na (o ironio) stronie podręcznika polecenia man. Wydajmy poniższe polecenie aby zapoznać się z nim oraz z ich organizacją.

[xf0r3m@hp-z600 ~]$ man man

Różne komponenty na Linuksie mogą mieć takie same nazwy. Polecenie man zwraca pierwsze znalezione wystąpienie, jeśli wpiszemy polecenie man passwd to otrzymamy informacje na temat polecenia, ale jeśli interesowałoby nas poznanie szczegółów pliku /etc/passwd musimy podać przed jego nazwą, numer rozdziału odpowiedzialny za pliki konfiguracyjne.

[xf0r3m@hp-z600 ~]$ man 5 passwd

Strony podręcznika, możemy przeszukiwać pod kątem występowania słów kluczowych, w przypadku kiedy znamy zagadnienie ale nie znamy odpowiedniego polecenia. Do tego celu możemy użyć opcji -k polecenia man lub polecenia apropos. Wynik działania tych programów jest taki sam.

[user@rhel90 ~]$ apropos dos
dos2unix (1)         - DOS/Mac to Unix and vice versa text file format converter
dosfsck (8)          - check and repair MS-DOS FAT filesystems
dosfslabel (8)       - set or get MS-DOS filesystem label or volume ID
fatlabel (8)         - set or get MS-DOS filesystem label or volume ID
filesystems (5)      - Linux filesystem types: ext, ext2, ext3, ext4, hpfs, iso9660, JFS, minix, msdos, ncpfs nfs, ntfs, proc, Reiserfs, smb, sysv, umsdos, vfat, XFS, xiafs
fs (5)               - Linux filesystem types: ext, ext2, ext3, ext4, hpfs, iso9660, JFS, minix, msdos, ncpfs nfs, ntfs, proc, Reiserfs, smb, sysv, umsdos, vfat, XFS, xiafs
fsck.fat (8)         - check and repair MS-DOS FAT filesystems
fsck.msdos (8)       - check and repair MS-DOS FAT filesystems
fsck.vfat (8)        - check and repair MS-DOS FAT filesystems
mac2unix (1)         - DOS/Mac to Unix and vice versa text file format converter
mkdosfs (8)          - create an MS-DOS FAT filesystem
mkfs.fat (8)         - create an MS-DOS FAT filesystem
mkfs.msdos (8)       - create an MS-DOS FAT filesystem
mkfs.vfat (8)        - create an MS-DOS FAT filesystem
unix2dos (1)         - DOS/Mac to Unix and vice versa text file format converter
unix2mac (1)         - DOS/Mac to Unix and vice versa text file format converter
vdosetuuid (8)       - sets a new uuid for the vdo volume stored on a backing store
vdostats (8)         - Display configuration and statistics of VDO volumes

W pierwszej kolumnie znajduje się nazwa komponentu, w drugiej (w nawiasie) numer rozdziału a następnie krótki opis.

Szukając opisu polecenia możemy posiłkować się poleceniem whatis. Polecenie to poszukuje konkretnego komponentu na stronach podręcznika, a nie słowa kluczowego.

[user@rhel90 ~]$ whatis xdg-open 
xdg-open (1)         - opens a file or URL in the user's preferred application

Polecenie whatis, zachowuje się identycznie jak polecenie man z opcją -f.

2.4.2. Pomoc w samym poleceniu

Inny sposobem niż wertowanie stron podręcznika w celu znalezienia odpowiedniej opcji jest po prostu wywołanie polecenia a opcją --help.

[user@rhel90 ~]$ ranger --help
Usage: ranger [options] [path]

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -d, --debug           activate debug mode
  -c, --clean           don't touch/require any config files.
  --logfile=file        log file to use, '-' for stderr
  --cachedir=dir        change the cache directory.
                        (/home/xf0r3m/.cache/ranger)
  -r dir, --confdir=dir
                        change the configuration directory.
                        (/home/xf0r3m/.config/ranger)
  --datadir=dir         change the data directory.
                        (/home/xf0r3m/.local/share/ranger)
  --copy-config=which   copy the default configs to the local config
                        directory. Possible values: all, rc, rifle, commands,
                        commands_full, scope
  --choosefile=OUTFILE  Makes ranger act like a file chooser. When opening a
                        file, it will quit and write the name of the selected
                        file to OUTFILE.
  --choosefiles=OUTFILE
                        Makes ranger act like a file chooser for multiple
                        files at once. When opening a file, it will quit and
                        write the name of all selected files to OUTFILE.
  --choosedir=OUTFILE   Makes ranger act like a directory chooser. When ranger
                        quits, it will write the name of the last visited
                        directory to OUTFILE
  --selectfile=filepath
                        Open ranger with supplied file selected.
  --show-only-dirs      Show only directories, no files or links
  --list-unused-keys    List common keys which are not bound to any action.
  --list-tagged-files=tag
                        List all files which are tagged with the given tag,
                        default: *
  --profile             Print statistics of CPU usage on exit.
  --cmd=COMMAND         Execute COMMAND after the configuration has been read.
                        Use this option multiple times to run multiple
                        commands.

Posiadanie tej opcji nie jest standardem, wiec nie wszystkie polecenia będą ją mieć.

2.4.3. Dokumentacja texinfo

Projekt GNU, zaproponował zmianę formatu dokumentacji programów, z suchych stron podręcznika do dokumentów przypominających ksiązkę lub inne dokumenty. Strony podręcznika nadal są podstawowym i wybudowanym (w większość dystrybcji) źródłem wiedzy na temat systemu i jego komponentów. Nie mniej jednak jeśli uznamy, że zawartość strony podręcznika nie jest dla zadowalająca może spojrzeć też tam, o ile ten format jest zainstalowany w naszym systemie. Dostęp do niego uzyskujemy za pomocą polecenia info lub pinfo, następnie podając nazwę polecenia jako argument. Wydając samo polecenie info, możemy zobaczyć ile dokumentacji w formacie texinfo znajduje się w systemie.

2.4.4. /usr/share/doc

Innym miejscem przechowującym jakieś informacje na temat poleceń czy też programów może być katalog /usr/share/doc. Możemy w nim znaleźć na przykład domyślne plik konfiguracyjne wielu usług (oczywiście zależy to czy zostaną one dodane do paczek z oprogramowaniem). Nie należy jednak od razu skreślać tego katalogu, często mogą znajdować się tam informacje, których nie znajdziemy na stronach podręcznika czy w formacie info np. tutoriale lub gotowe konfiguracje.

2.4.5. Dokumentacja Red Hat Enterprise Linux

Innym źródłem informacji na temat obsługi systemu jest dokumentacja Red Hat dostępna w internecie (dla RHEL 9) pod tym adresem: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9

Każdy zebranych tam tematów możemy pobrać w formacie HTML, PDF oraz ePub (czytniki e-booków).

Odnośnie egzaminu:
Na egzaminie możemy korzystać ze wszystkiego co jest dostępne w systemie warto pamiętać o tym. Jeśli będziemy mieć problemy z poleceniem man, warto wydać polecenie mandb odświeży to bazę danych stron podręcznika.

Ćwiczenie 1: Nawigacja pośród katalogów Linuksa

Jako zwykły użytkownik na maszynie wirtualnej server1, wyświetl ścieżkę na której się znajdujesz. W tym samym katalogu wyświetl wszystkie pliki. Przjedź do katalogu /etc następnie wyświetl ścieżkę, na której się znajdujesz, następnie powróć do poprzedniego katalogu i jeszcze raz wyświetl ścieżkę.

Ćwiczenie 2: Rożne zadania

Jako zwykły użytkownik na maszynie server1 w terminalu polecenie tty. Następnie uruchom kilka kart terminala wydaj w nich to samo polecenie i porównaj zwracane wartości. Wyświetl czas pracy systemu oraz średnie obciążenie procesora. Za pomocą trzech poznanych poleceń wyświetl lokalizacje polecenia vgs.

Ćwiczenie 3: Informacje o systemie

Jako zwykły użytkownik na maszynie server1 za pomocą poznanego polecenia wyświetl wszystkie informacje o systemie (oczywście w miarę możliwości polecenia). Następnie użyj innego polecenia aby sprawdzić informacje na temat zainstalowanego w komputerze procesora.

Ćwiczenie 4: Używanie pomocy

Jako zwykły użytkownik na maszynie server1 Wyświetl stronę podręcznika polecenia uname oraz stronę podręcznika dla pliku /etc/shadow. Wykonaj trzy polecenia: apropos ext4, man -k ext4, whatis group. Czym różnią się informacje zwracane przez te polecenia?

Podsumowanie

W tym rozdziale skupiliśmy się na podstawach obsługi systemu, poznaliśmy środowisko graficzne. Odświerzyliśmy sobie informacje na temat hierachicznego systemu plików wykorzystywanego w Uniksach. Zapoznaliśmy się z terminalem i podstawowymi poleceniami. Na koniec dowiedzieliśmy się gdzie możemy szukać pomocy na temat poleceń, plików konfiguracyjnych i tego typu rzeczy.

3. Operacje na plikach

W poprzedni rodziale poznaliśmy podstawy poruszania się po systemie, przejrzeliśmy środowisko graficzne, zapoznaliśmy się z wierszem polecenia oraz z najbardziej podstawowymi poleceniami. Było to swoiste zapoznanie się z systemem operacyjny. Teraz pora w nim trochę popracować. Ludzie zazwyczaj kiedy wspomagają swoją prace komputerem, pracują na danych. Te dane są zazwyczaj zebrane w pliki i od omówienia rodzajów plików dostępnych na Linuksie rozpoczniemy ten rozdział.

3.1. Rodzaje plików

W Uniksach możemy wymienić 7 rodzajów różnych plików. W tych systemach nie jest istotne rozszerzenie oraz zawartość tych plików. Dla systemu zawsze będzie to zwykły plik. Wszystkie pliki niewymienione na poniższej liście, które przyjdą nam do głowy są plikami zwykłymi.

Zatem w Uniksach możemy wyróżnić takie rodzaje plików jak:

Nie ma się też za bardzo co rozwodzić nad każdym z tych typów plików. Rodzaju pliku możemy ustalić podczas wyświetlania zawartości katalogu. Wskaźnikiem jest pierwszy znak w pierwszej kolumnie informacji zwracanej przez polecenie ls -l. Informacje zwracane dla poszczególnych typów wyglądają następująco:

#Zwykły plik:
[root@server1 ~]# ls -l anaconda-ks.cfg 
-rw-------. 1 root root 1197 Jun  9 19:59 anaconda-ks.cfg

#Katalog:
[root@server1 ~]# ls -ld bzipped/
drwxr-xr-x. 3 root root 17 Jun 16 18:14 bzipped/

#Urządzenie znakowe:
[root@server1 ~]# ls -l /dev/console
crw--w----. 1 root tty 5, 1 Jun 17 15:20 /dev/console

#Urządzenie blokowe:
[root@server1 ~]# ls -l /dev/sda1
brw-rw----. 1 root disk 8, 1 Jun 17 15:20 /dev/sda1

#Dowiązanie symboliczne:
[root@server1 ~]# ls -l soft10 
lrwxrwxrwx. 1 root root 6 Jun 16 17:30 soft10 -> file10

Pierwsze znaki, pierwszej kolumny róznią się od siebie w zależności od typu plików. Jest jeszcze kilka róznic. Pliki urządzeń nie posiadają rozmiaru, tylko dwie liczby oddzielone od siebie przecinkiem. Te liczby to numer główny oraz numer poboczny. Numer główny określa rodzaj urządzenia (użyty sterownik urządzenia), z kolei numer poboczny jest jego unikalny identyfikatorem w systemie. Innym rodzajem, jest dowiązanie symboliczne, które nazwa wskazuje na ścieżke innego pliku, ale do tego typu plików wrócimy poźniej.

Te informacje które są zwracane oprócz nazwy pliku przez polecenie, ls z odpowiednią opcją to tak zwane metadane. Większą ich ilość możemy wyświetlić za pomocą polecenia stat. Na poniższym przykładzie zamieszczono więcej metadanych zwykłego pliku z poprzedniego przykładu:

[root@server1 ~]# stat anaconda-ks.cfg 
  File: anaconda-ks.cfg
  Size: 1197      	Blocks: 8          IO Block: 4096   regular file
Device: fd00h/64768d	Inode: 6126703     Links: 1
Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:admin_home_t:s0
Access: 2022-06-16 10:37:11.765639347 +0200
Modify: 2022-06-09 19:59:42.592326645 +0200
Change: 2022-06-09 19:59:42.592326645 +0200
 Birth: 2022-06-09 19:59:42.237318552 +0200

Zwykłe pliki na Uniksach nie muszą mieć rozszerzenia, więc aby przed uruchomieniem pliku upewnić się co to jest, mamy do dyspozycji takie narzędzie jak program file, który zbada zawartość podanego mu w argumencie pliku.

[root@server1 ~]# file test 
test: Bourne-Again shell script, ASCII text executable

Plik nie ma rozszerzenia .sh a mimo to pozostaje skryptem powłoki.

3.2. Kompresja i archiwizowanie

Kompresja pozwala na zmniejszenie objętości plików za pomocą zaawansowanych algorytmów, dzięki czemu możemy zaoszczędzić miejsce na dysku. Wadą takiego przechowywania danych jest dłuższy czas oczekiwania na ich gotowość do pracy, ponieważ dane przed tym należy zdekompresować. Archizowanie może służyć do zebrania wielu małych plików w jeden duży. Na Uniksach możemy wymienić dwa natywne narzędzia służące do kompresji i jedno do archiwizowania.

3.2.1. gzip

Natywnym i dostępnym na każdym systemie sposobem na kompresje danych jest gzip. Algorytmy zawarte w tym narzędziu kompresują dane, powodując zmniejszenie ich objętość. Narzędzie do każdego skompresowanego pliku dodaje rozszerzenie .gz. Aby skompresować plik wystarczy podać jego nazwę lub ścieżkę do niego jako argument poleceniu gzip.

#Plik przed kompresją
[root@server1 ~]# ls -l fstab 
-rw-r--r--. 1 root root 579 Jun 16 11:07 fstab

#Kompresja
[root@server1 ~]# gzip fstab

#Plik po kompresji
[root@server1 ~]# ls -l fstab.gz 
-rw-r--r--. 1 root root 350 Jun 16 11:07 fstab.gz

Jak możemy zauważyć rozmiar pliku zmniejszył się. Zwróćmy uwagę na to iż narzędzie nie działa na kopii pliki tylko kompresuje bezpośrednio podany plik. Aby plik zdekompresować możemy użyć polecenia gunzip

[root@server1 ~]# gunzip fstab.gz

Dla plików skompresowanych za pomocą polecenia gzip, możemy podejrzeć listę skompresowanych plików (w przypadku jednego pliku może być to użyteczne do wyświetlenia statystki kompresji) wykorzystując opcje -l i podając jako argument skompresowany plik.

[root@server1 ~]# gzip -l fstab.gz 
         compressed        uncompressed  ratio uncompressed_name
                350                 579  43.7% fstab

3.2.2. bzip2

Program bzip2 jest obecnie standardem kompresji wśród wielu Uniksów. Jest wolniejszy od gzip, ale skompresowane przez niego pliki są mniejsze. Więc pozostaje w naszej gestii, który z nich będziemy stosować. Do skompresowanych przez bzip2 plików dodawane jest rozszerzenie .bz2. A kompresja za pomocą tego narzędzia wygląda następująco:

[root@server1 ~]# ls -lh test
-rw-r--r--. 1 root root 250M Jun 17 17:21 test
[root@server1 ~]# bzip2 test 
[root@server1 ~]# ls -lh test.bz2 
-rw-r--r--. 1 root root 210 Jun 17 17:23 test.bz2

Dekompresja takiego pliku takiego pliku wyglada analogicznie do narzędzia gunzip.

[root@server1 ~]# bunzip2 test.bz2
[root@server1 ~]# ls -lh test 
-rw-r--r--. 1 root root 250M Jun 17 17:25 test

W przypadku polecenia bzip2 nie mamy możliwośći wyświetlenia listy skompresowanych plików (statystyk kompresji).

3.2.3. tar

Program tar nie służy do kompresji, a do archiwizcji. Jego zadaniem jest stworzenie jednego pliku z wielu innych plików lub z całego katalogu. Na początku program swojego istnienia program tar domyślnie tworzył archiwa na napędach taśmowych, dlatego też jeśli ma on tworzyć archiwum do pliku potrzebna będzie odpowiednia opcja, i ta funkcjonalność pozostała po dziś dzień.

Opcje tego programu możemy podawać na trzy różne sposoby, wpisując z myślnikami każdą opcje po kolei, wypisując opcje pod jednym myślnikiem, lub nie używać myślnika w ogóle. Do stworzenia archiwum potrzebujemy dwóch opcji, pierwszej -c - tworzącej archiwum oraz drugiej -f - nakazujące stworzenie archiwum w podanym jako argument opcji pliku. Poniżej znajdują się opisane wyżej użycia polecenia tar do stworzenia archiwum.

[root@server1 ~]# tar -c -f etc_tarred.tar /etc
tar: Removing leading `/' from member names
[root@server1 ~]# tar -cf etc_tarred1.tar /etc
tar: Removing leading `/' from member names
[root@server1 ~]# tar cf etc_tarred2.tar /etc
tar: Removing leading `/' from member names

Zwróćmy uwagę na komunikat programu, który usuwa ze scieżek wskazujących pliki (ponieważ użyłem ścieżki bezwzględnej) początkowy ukośnik, aby przy wypakowywaniu nie nadpisać ważnych plików systemowych. Warto dodać, iż ja tworzyłem te archiwa jako superużytkownik, w momencie gdy root używa polecenia tar, używana jest domyślnie opcja -p, która powoduje zachowanie wszystkich atrybutów plików, w przypadku zwykłego użytkownika ta opcja musi zostać jawnie podana.

Rozpakowanie archiwum wymaga podania opcji -x oraz opcji -f ze wskazaniem pliku archiwum.

[root@server1 ~]# tar -xf etc_tarred1.tar -C /tmp
[root@server1 ~]# ls -ld /tmp/etc
drwxr-xr-x. 132 root root 8192 Jun 17 15:20 /tmp/etc

W tym przykładzie użyłem jeszcze jednej ważnej opcji -C, która nakazuje poleceniu tar zmianę katalogu przed wypakowaniem archiwum.

Za pomocą opcji -t, możemy podejrzeć zawartość archiwum. Opcja -v powoduje wyświetlanie komunikatów przez program.

[root@server1 ~]# tar -tvf etc_tarred2.tar
drwxr-xr-x root/root         0 2022-06-17 15:20 etc/
lrwxrwxrwx root/root         0 2022-06-09 19:47 etc/mtab -> ../proc/self/mounts
-rw-r--r-- root/root       579 2022-06-09 19:44 etc/fstab
-rw------- root/root         0 2022-06-09 19:44 etc/crypttab
drwxr-xr-x root/root         0 2022-06-09 19:47 etc/lvm/
drwxr-xr-x root/root         0 2022-06-09 19:44 etc/lvm/devices/
-rw-r--r-- root/root       286 2022-06-09 19:44 etc/lvm/devices/system.devices
drwx------ root/root         0 2022-06-09 20:48 etc/lvm/archive/
-rw------- root/root      1891 2022-06-09 20:48 etc/lvm/archive/rhel_00000-557804270.vg
drwx------ root/root         0 2022-06-09 20:48 etc/lvm/backup/
-rw------- root/root      1890 2022-06-09 20:48 etc/lvm/backup/rhel
drwx------ root/root         0 2022-02-16 13:01 etc/lvm/cache/
-rw-r--r-- root/root    111694 2022-02-16 13:01 etc/lvm/lvm.conf
-rw-r--r-- root/root      2299 2022-02-16 13:01 etc/lvm/lvmlocal.conf
...

Chcąc dodać plik do archiwum, wystarczy użyć opcji -r nie trzeba go rozpakowywać i dodawać tych plikiów wystarczy wskazać archiwum a następnie pliki, które chce się dodać. Plik zostanie dodany na koniec archiwum.

[root@server1 ~]# tar -rvf etc_tarred2.tar vipractice.bz2 
vipractice.bz2

[root@server1 ~]# tar -tvf etc_tarred2.tar
...
-rw-r--r-- root/root       208 2022-06-09 19:46 etc/.updated
-rw-r--r-- root/root       307 2022-06-16 18:38 vipractice.bz2

Dodawaniu plików do archiwum nie służy wyłącznia opcja -r, ale również -u, która dodaje nowsze pliki na końcu archwium. Wyświetlając zawrtość takiego archiwum, możemy zobaczyć pliki o tych samych nazwach. Te nazwy oczywiście odpowiadają różnym wersjom tych plików.

[root@server1 ~]# tar -uvf etc_tarred.tar /etc/nanorc
tar: Removing leading `/' from member names
/etc/nanorc
tar: Removing leading `/' from hard link targets

[root@server1 ~]# tar -tvf etc_tarred2.tar
...
-rw-r--r-- root/root       208 2022-06-09 19:46 etc/.updated
-rw-r--r-- root/root     10373 2022-06-19 09:47 etc/nanorc

Po zebraniu wszystkich plików do archiwum, może okazać się że jest to sporych rozmiarów plik. Polecenie tar, pozwala na zastosowanie poznanych wcześniej algorytmów kompresji podczas tworzenia archwium.

Rozpakowując skompresowane archiwa w RHEL możemy rozpakować podając takie same opcje jak w przypadku nie skompresowanych archiwów.

[root@server1 ~]# tar -xf etc-gzipped.tar.gz -C /tmp

Program rozpozna po zawartości pliku algortym kompresji. Wiecej informacji na temat polecenia tar znajduje na stronie podręcznika.

Odnośnie egzaminu:
Na egzaminie będziemy mieć określną ilość czasu na wykonanie zadań egzaminacyjnych, dlatego też operacje kompresji czy archiwizacji plików będą przeprowadzane na małych plikach, aby nie tracić czasu.

3.3. Edycja plików za pomocą edytora Vim

Podczas wykonywania zadań związanych z administracją systemem, niezbędna może być edycja jakiegoś pliku. Edytor Vim jest standardem jeśli chodzi o edycję plików. Ten edytor w wersji podstawowej jaką jest program Vi jest dostępny w większości Uniksów. W przypadku w wielu dystrybucji Linuksa preinstalowany jest Vim, ze względu na to ten edytor jest jednym z podstawowych narzędzi. Lepiej jest nauczyć się rozwiązania, które możemy zastosować domyślnie bez zbędnej ingerencji w system (bez instalacji ulubionego edytora, który znamy).

Edytor Vim może działać w trzech trybach:

3.3.1. Podstawy obsługi Vim

Edytor Vim uruchamiamy, jak każde inne polecenie. Podając jego nazwę w wierszu polecenia. Podczas uruchamiania możemy podać plik jako argument lub też użyć polecenia trybu ostatniej linii :o po czym podać ścieżkę do pliku.

[root@server1 ~]# vim test.txt

Poruszanie po tekscie odbywa się w trybie poleceń za pomocą klawiszy:

Przejście do edycji tekstu, wymaga użycia polecenia i co spowoduje rozpoczęcie edycji w pozycji kursora lub polecenia a, które spowoduje przesunięcie kursora o jedną pozycję i rozpoczęcie edycji, polecenie a możemy tłumczyć jako dopisywanie (ang. append), a i jako wprowadzanie (ang. insert).

Opuszczenie trybu wprowadzania i powrót do trybu polecenia wymaga naciśnięcia klawisza Esc (ang. escape). Przjście do trybu polecenia jest wymagane, aby przenieść kursor w inne miejsce. W tym trybie również poprzez polecenia ostatniej linii zapisujemy zmiany w pliku oraz opuszczamy edytor. Zmiany w tekscie również możemy porzućić, nie musimy ich zapisywać. Poniżej umieściłem kilka poleceń trybu ostatniej linii, służacych od zapisywania i opuszczania edytora.

Każdej z tych opcji może towarzyszyć wykrzyknik (!), więc jeśli chcielibyśmy porzucić zmiany w pliku i wyjść należy użyć polecenia :q!. Możemy czasami spotkać się z uprawnieniami, które nie pozwolą nam zapisać zmian w pliku. Rozwiązać tego typu problem możemy na dwa sposoby, wymusić zapisanie zamian za pomocą wykrzyknika (działa to tylko w przypadku, gdy superużytkownik chce zapisać zmiany w pliku tylko odczytu), lub podać ścieżkę do nowego pliku poleceniu :w, poźniej skopiować w plik w docelowe miejsce.

3.3.2. Pozostałe polecenia Vim

Vim posiada bogatą kolekcję komend trybu poleceń oraz trybu ostatniej linii, na poniższej liście znajdują się część z nich, możemy wykorzystać je do sprawniejszego używania edytora Vim.

Komendy trybu poleceń:

W przypadku trybu poleceń, możemy zwielokrotnić działanie tych poleceń, wprowadzając liczbę wykonywania tego polecenia przed poleceniem. Na przykład jeśli chcemy usunąć 3 linie to zamiast wydawać trzykrotnie polecenie dd, możemy nacisnąć 3 następnie dd i trzy linie spod kursora zostaną usunięte/wycięte.

Polecenia trybu ostatniej linii:

Podczas korzystania Vim istotna jest praktyka. Dlatego trzeba przesiąść się na niego i zacząć tworzyć w nim pliki.

Odnośnie egzaminu:
Podczas egzaminu, możemy spotkać się z zadaniem w którym trzeba zmienić jakiś plik. Dostępność Vim na maszynie egzaminacyjnej jest niemal pewna, dlatego też nauczenie się jego obsługi jest dość istotne.

3.4. Pliki i katalogi

Jaka kolwiek praca z komputerem prędzej czy poźniej będzie wymagać operacji na plikach i katalogach. Poniżej przedstawiam najprostsze czynności jakie możemy wykonywać na tych elementach.

3.4.1. Tworzenie plików i katalogów

Tworzenie plików może zrealizować na kilka sposób. Jednym z nich jest wykorzystanie polecenia touch, które służy generalnie do uaktualnienia czasów dostępu oraz modyfikacji, jednak kiedy podany plik nie istnieje to zostanie on utworzony przez to polecenie.

[root@server1 ~]# touch plik1
[root@server1 ~]# ls -l plik1
-rw-r--r--. 1 root root 0 Jun 19 14:52 plik1

Jak widać na powyższym przykładzie został utworzony plik, którego wielkość to całe 0 bajtów. Innym sposobem na utworzenie pliku jest wykorzystanie polecenia Vim, w którym zapisując zmiany w nowym pliku, tworzymy go mimochodem. Ostanim ze sposobów jest utworzenie plików ze strumienia (stumienie będą opisywane w dalszej części tego materiału), za pomocą polecenia cat, które omówimy sobie w następnym podrozdziale. Wydając następujące polecenie:

[root@server1 ~]# cat > plik2
test1
test2
test3
test4
test5

Uruchamiając to polecenie przełączymy strumienie tak, aby wszystko co zostanie wpisane do terminala trafi do pliku (w dużym skrócie). Taką edycję pliku możemy zakończyć naciskając kombinacje klawiszy Ctrl+d.

Za tworzenie katalogów odpowiedzialne jest polecenie mkdir. Polecenie to utworzy katalog o nazwie podanej jako argument. Ważną opcją tego polecenia jest -p, za pomocą którego jesteśmy wstanie tworzyć z poziomu pojedynczego polecenia całe struktury katalogów. Opcja ta powoduje utworzenie katalogów podanych na ścieżce, które nie istnieją.

[root@server1 ~]# mkdir dir1

3.4.2. Wyświetlanie zawartości pliku

Na Uniksach do wyświetlania zawartości plików mamy kilka rozwiązań pierwszym z nich jest wspomnianie już polecenie cat. Polecenie wyświetli zawartość pliku podanego jako argument w terminalu.

[root@server1 ~]# cat plik2 
test1
test2
test3
test4
test5

Na Uniksach, obok polecenia cat występuje polecenie tac, które ma takie samo zastosowanie jak to wyżej wymienione, wyświetlając jednak zawartość pliku odwrotnie od końca. Poniższy przykład zaraz to zobrazuje:

[root@server1 ~]# tac plik2 
test5
test4
test3
test2
test1

Podczas wyświetlania dużych plików ich zawartość może nie mieścić się na ekranie, czy też w oknie terminala. Do wyświetlania tego typu plików wykorzystuje się takie polecenia jak more czy less. Oba dzielą wyświetloną treść plików na strony wielkości ekranu bądź terminala. Program more nie jest za bardzo zaawansowanym narzędziem. Wyświetlając pliki za jego pomocą nie mamy zbyt dużego pola manewru. Możemy przesuwać stronę po stronie, do końca pliku. Dlatego też znacznie lepszym rozwiązaniem jest wykorzystanie polecenia less, który pozwala poruszać się po tekscie w góre i w dół, ale również pozwala na wyszukiwanie fraz w tekscie, możemy również nawigować po za pomocą klawiszy znanych z programu Vim. Program less uruchomi się szybciej niż, program more, ponieważ nie musi on załadować całego pliku od razu.

[root@server1 ~]# less /var/log/messages

Ostatnim już sposobem na wyświetlenie zawartości plików jest, użycie polecenia head oraz tail, ich zadaniem jest wyświetlenie kilku początkowych (domyślnie 10) linii z podanego jako argument pliku - polecenie head lub kilku ostatnich (również 10) linii z podanego pliku - polecenie tail.

[root@server1 ~]# tail /var/log/messages
Jun 19 13:59:33 server1 systemd[1]: Finished dnf makecache.
Jun 19 14:18:23 server1 cupsd[964]: REQUEST localhost - - "POST / HTTP/1.1" 200 182 Renew-Subscription successful-ok
Jun 19 15:16:43 server1 cupsd[964]: REQUEST localhost - - "POST / HTTP/1.1" 200 182 Renew-Subscription successful-ok
Jun 19 15:28:22 server1 systemd[1]: Starting dnf makecache...
Jun 19 15:28:23 server1 dnf[2173]: Updating Subscription Management repositories.
Jun 19 15:28:23 server1 dnf[2173]: Unable to read consumer identity
Jun 19 15:28:23 server1 dnf[2173]: This system is not registered with an entitlement server. You can use subscription-manager to register.
Jun 19 15:28:23 server1 dnf[2173]: Metadata cache refreshed recently.
Jun 19 15:28:23 server1 systemd[1]: dnf-makecache.service: Deactivated successfully.
Jun 19 15:28:23 server1 systemd[1]: Finished dnf makecache.

Domyślną ilość wypisywanych linii możemy regulować poprzez podanie po myślniku (-) żądanej liczby.

[root@server1 ~]# tail -5 /var/log/messages
Jun 19 15:28:23 server1 dnf[2173]: Unable to read consumer identity
Jun 19 15:28:23 server1 dnf[2173]: This system is not registered with an entitlement server. You can use subscription-manager to register.
Jun 19 15:28:23 server1 dnf[2173]: Metadata cache refreshed recently.
Jun 19 15:28:23 server1 systemd[1]: dnf-makecache.service: Deactivated successfully.
Jun 19 15:28:23 server1 systemd[1]: Finished dnf makecache.

Polecenie tail ma jedną bardzo istoną opcję, która pozwala śledzić na bierząco zmiany w pliku, ta opcja to -f.

Poleceniem związanym z zawartością plików, ale nie z jej wyświetlaniem może być polecenie wc, które domyślnie wyświetla ilość linii, liczbę słów oraz bajtów.

[root@server1 ~]# wc /var/log/messages
  639  7503 61333 /var/log/messages

3.4.3. Kopiowanie i przenoszenie plików i katalogów

Pracując z plikami, być może wystąpić potrzeba skopiowania bądź przeniesienia danych w inne miejsce. Za operację kopiowania odpowiedzialnie jest polecenie cp. Jako pierwszy argument przyjmuje ono ścieżkę lub plik źródłowy, jako drugi ścieżkę lub plik docelowy.

[root@server1 ~]# cp plik2 plik3

W przypadku gdy dany plik już istnieje w podanym katalogu polecenie zapyta się czy nadpisać docelowy plik, ale tylko w przypadku superużytkownika, bowiem on w konfiguracji swojej powłoki posiada alias (nazwę zastępczą) cp dla polecenia cp -i, opcja -i włącz tryb interaktywy, czyli właśnie takie pytania.

[root@server1 ~]# cp plik1 plik3 
cp: overwrite 'plik3'? n

Za pomocą polecenia cp możemy kopiować całe katalogi z podkatalogami przy użyciu opcji -r, która włącza kopiowanie rekurencyjne. Jeśli atrybuty plików są dla nas ważne, to żeby nie nadpisać ich należy użyć opcji -p.

Za przenoszenie plików odpowiedzialne jest polecenie mv, które ma jeszcze jedną ważną cechę ukrytą w mechanice swojego działa. Otóż jeśli będzie przenosić plik lub katalog w ramach tego samego katalogu, to zmieni on tylko swoją nazwę.

Polecenie mv w przypadku superużytkownika, posiada podobny alias co w przypadku polecenia cp, polecenie jest uruchamiane domyślnie z opcją pytającą o nadpisanie elementu docelowego.

[root@server1 ~]# mv plik1 plik3 
mv: overwrite 'plik3'? n

Polecenie mv nie potrzebuje żadnej opcji, aby przenieść cały katalog wraz z podkatalogami.

[root@server1 ~]# mv gzipped /tmp
[root@server1 ~]# ls -ld /tmp/gzipped/
drwxr-xr-x. 3 root root 17 Jun 16 18:14 /tmp/gzipped/

3.4.4. Usuwanie plików i katalogów

Zbędne plik i katalogi możemy usunąć, za pomocą polecenia rm. Polecenie do usunięcia katalogu wymaga podania nazwy pliku jako argumentu.

[root@server1 ~]# rm plik1 
rm: remove regular empty file 'plik1'? y

Polecenie rm również jest aliasem, z tą samą opcją co cp czy mv. Aby usunąć cały katalog wraz podkatalogami, należy użyć kolejno opcji -r - uruchamiającej usuwanie rekurencjne oraz opcji -f, wymuszające odpowiedź y na wszystkie pytania.

[root@server1 ~]# rm -rf /tmp/etc

Usuwanie plików i katalogów posiadając uprawnienia superużytkownika, może skończyć się katastrofą. Pomyłka w tym poleceniu może uszkodzić system. Dlatego też należy zachować szczególną ostrożność przy korzystaniu z tego polecenia, a w szcególności w połączeniu z nazwami wieloznacznymi (będzie o tym przy okazji omawiania powłoki BASH).

Do usuwania pustych katalogów, możemy użyć polecenia rmdir, jak i opcji -d polecenia rm.

[root@server1 ~]# rm -d dir1/
rm: remove directory 'dir1/'? y
[root@server1 ~]# rmdir dir2

Odnośnie egzaminu:
Polecenia manipulacji plikami są podstawowymi czynnościami związnymi z administracją każdego Uniksa, więc warto przyswoić wiedzę zebraną w tym podrozdziale.

Wśród systemów uniksowych, możemy w kategoriach manipulacji plikami wyróżnić takie elementy jak dowiązania. Dowiązania są najprościej rzecz biorąc są elementami, które wskazują na inne pliki. Dowiązania możemy podzielić na twarde oraz symboliczne.

Twarde dowiązania nie są same w sobie stricte plikami. Pliki bowiem znajdują na urządzeniach służacych do przechowywania danych. Systemy plików przechowują metadane plików, ich atrybuty oraz informacje w jakich obszarch, której z pamięci masowych należy szukać zawartości pliku, aby ludzie mogli się nimi posługiwać używa się nazw plików. Taką własnie nazwą jest twarde dowiązanie. Dla eksperymentu utworzymy pusty plik, o nazwie hard1, oto jego artybuty

[root@server1 ~]# ls -li hard1 
6043841 -rw-r--r--. 1 root root 0 Jun 19 17:31 hard1

Pierwsza kolumna zawiera numer i-node jest to identyfikator wpisu w bazie danych systemu plików, ktory zawiera metadane na temat pliku. W trzeciej kolumnie atrybutów znajduje się wartość 1. Ta kolumna przedstawia ilość dowiązań twardych. Każdy zwykły plik posiada co najmniej jedno dowiązanie - identyfikującą bezpośrednio plik nazwę. Tworząc kolejne twarde dowiązanie, podłączamy kolejną nazwę tego pliku. Teraz utwórzmy dowiązanie o nazwie hard2 do pliku hard1. Dowiązania tworzymy za pomocą polecenia ln.

[root@server1 ~]# ln hard1 hard2
[root@server1 ~]# ls -li hard*
6043841 -rw-r--r--. 2 root root 0 Jun 19 17:31 hard1
6043841 -rw-r--r--. 2 root root 0 Jun 19 17:31 hard2

Zwróć my uwagę na to, iż hard2 wskazuje na ten sam numer i-node, czyli wskazuje na ten sam pliki. Liczba dowiązań uległa zmianie tak samo w przypadku hard1 jak i hard2, ponieważ na ten sam plik wskazują dwa dowiązania. Zawartość pliku możemy zmieniać odwołując czy to do hard1 czy hard2, tak samo jest z atrybutami, ponieważ obie nazwy wskazują na ten sam węzeł i-node (wpis w bazie danych systemu plików), jeśli usuń my teraz plik hard1, to tak naprawdę usuniemy jedno z dowiązań.

[root@server1 ~]# rm hard1
rm: remove regular empty file 'hard1'? y
[root@server1 ~]# ls -li hard*
6043841 -rw-r--r--. 1 root root 0 Jun 19 17:31 hard2

Dowiązania twarde mogą powodować bałagan w plikach, ponieważ mimo usunięcia on dalej istnieje pod inną nazwą. Dopiero usunięcie wszystkich dowiązań, ostatecznie kończy egzystencje pliku w pamięci masowej, ponieważ systemy plików, pliki bez dowiązań uznają za nie potrzebne i realokują miejsce przechowania zawartości tego pliku.

Innym rodzajem dowiązań jest dowiązanie symboliczne. Często są one porównywane do skrótów znanych z systemów Windows i tak na prawdę taka jest ich rola. Dowiązania symboliczne mają wskazywać na inne pliki. W przeciwieństwie do dowiązań twardych, te dowiązania są jednak odrębnymi plikami (tak jakby), posiadają one bowiem wpis w bazie danych systemu plików, ale w miejscu przeznaczonym na adres przechowywania danych pliku na dysku lub innej pamięci znajduje się scieżka do pliku źródłowego, na który wskazuje dowiązanie. Zatem tego rodzaju elementy mają własne atrybuty. Wielkość dowiązania symbolicznego zależy od ilość znaków na ścieżce do pliku źródłowego. Dowiązania symboliczne tworzymy za pomocą tego samego narzędzia co dowiązania twarde, dodając tylko opcję -s.

[user@server1 ~]$ ln -s symfile symlink1
[user@server1 ~]$ ls -l symlink1
lrwxrwxrwx. 1 user user 7 Jun 20 19:28 symlink1 -> symfile

Dowiązania symboliczne mogłoby wydawać się, że są w wstanie zastąpić kopiowanie. Kopiując plik, odwzrowywuje się jego zawartość w innym miejscu na pamięci masowej, tworząc odrębny nienaruszony plik. Tworząc dowiązanie, podłączamy się pod istniejący już plik, który może być zmieniony nie tylko przez użytkowników, ale też programy. Dowiązania symbolicznego możemy użyć jak wymienionego wcześniej skrótu, chcąc zaoszczędzić sobie pisania długich ścieżek do głęboko osadzonych katalogów w systemie ewentualnie ułatwić pisanie ścieżek w skryptach oraz programach.

Przechodząc do folderu wskazywanego przez dowiązanie symboliczne jeśli wyświetlimy scieżkę obecnego katalogu roboczego, zobaczymy ścieżkę z uwzględeniem tego dowiązania. Aby odkryć prawdziwą ścieżkę należy polecenia pwd z opcją -P.

Ćwiczenie 1: Archiwa

Na maszynie server1 jako superużytkownik, utwórz skompresowane za pomocą programu gzip archiwum z zawartością katalogu /etc. Powtórz czynność tym razem z użyciem programu bzip2. Porównaj wielkość tych archiwów. Na koniec rozpakuj oba archiwa bez zaznaczania algorytmu kompresji (bez podawania odpowiedniej opcji).

Ćwiczenie 2: Praktyka z Vim-a

Na maszynie server1 jako zwykły użytkownik, utworz przepisując treść ćwiczenia nr. 1, każde zdanie w odrębnej linii, zapisz zmiany w pliku o nazwie vimpractice i zamknij edytor. Otwórz plik w edytorze ponownie, wyświetl numery linii. Skopiuj linię 2 i 3 na koniec pliku, tak aby było łącznie 6 linii. Przenieś linię nr. 3 w miejsce pierwszej. Przejdź na koniec pliku i dodaj do niego zawartośc w pliku .bash_profile (znajdziesz go w katalogu domowym użytkownika). Zamień wszystkie wszystkie wystąpienia słowa profile na pro file. Usuń linię od 5 do 8, zapisz zmiany w pliku i opuść edytor. Na koniec wyświetl podsumowanie pliku za pomocą polecenia wc.

Ćwiczenia 3: Manipulacja plikami

Na maszynie server1 jako zwykły użytkownik, utwórz jeden dowolny plik oraz jeden katalog, wyświetl ich atrybuty, sprawdź uprawnienia, nazwę właściciela oraz grupę do której ten plik należy. Co się stanie gdy spróbujesz przenieść utworzony wcześniej plik do katalogu /var/log. Przenieś plik oraz katalog do katalogu /tmp. Utwórz duplikat pliku i zmień jego nazwę na jakąkolwiek inną. Usuń wszystkie pliki utworzone na potrzeby tego ćwiczenia.

Podsumowanie

W tym rozdziale zapoznaliśmy się, z typowymi zadaniami związanymi z administracją plikami w systemie. Poznaliśmy edytor Vim, którego nauka wymaga zachodu, aczkolwiek nie pójdzie ona na marne. Ten edytor w uboższej podstawowej wersji w postaci programu Vi spotkamy w każdym Uniksie, zatem będzie potrafili sprawnie edytować pliki w każdym systemie, niezależnie od tego czy nasz ulubiony edytor jest dostępny. Ciekawszą rzeczą w tym rodziale z pewnością były dowiązania symboliczne. W następnym rodziale skupimy się na uprawnieniach plików i katalogów, oraz przekonamy się jak potężnym narzędziem jest polecenie find.

4. Zaawansowane zarządzanie plikami

W Uniksach możemy zabezpieczyć pliki i katalogi przed ingerencją innych użytkowników. Służą temu specjalny rodzaj zabezpieczeń jakim są uprawnienia.

4.1. Uprawnienia podstawowe

Uprawnienia przypisywane są w trzech klasach, klasie użytkownika (u) nazwywanego również właścicielem elementu; grupie (g), do której należy ten plik oraz oraz innym użytkownikom systemu (o), ta klasa uprawnień jest również oznaczna jako uprawnienia publiczne.

Każdej z tych klas możemy przypisać odpowiednie uprawnienia takie jak: prawo do odczytu (r), prawo do zapisu (w) oraz prawo do wykonania (x).

Za nim zaczniemy zmieniać uprawnienia, nauczymy się je identyfikować. Uprawnienia do pliku możemy wyświetlić za pomocą polecenia ls -l lub polecenia stat.

[user@server1 ~]$ ls -l file1
-rw-r--r--. 1 user user 0 Jun 21 14:21 file1

Uprawnienia znajdują się w pierwszej kolumnie: -rw-r--r--., łącznie 11 znaków, ale nas będzie interesować tylko 9 w środku. Pierwszy znak oznacza typ pliku, natomiast kropka związana jest z kontekstem SELinux, nie będziemy się teraz tym zajmować. Te dziewięć pól są to miejsca przeznaczone na uprawnienia poszczególnych klas. Pierwsze trzy pola z lewej to uprawnienia użytkownika (właściciela), kolejne trzy pola to uprawnienia grupy do której należy ten plik natomiast pozostałe trzy to uprawnienia publiczne, stosowane wobec użytkowników nie związanych z tym elementem. Uprawnienia przypisane każdej z klas układają się kolejno w rwx, myślnik na którymś z tych miejsce oznacza brak takiego prawa w danej klasie.

Każde z określonych praw pozwala nam, na wykonanie innych czynności związanych z tym elementem. Prawo read pozwala na odczyt zawartości pliku oraz jego skopiowanie, w przypadku katalogów pozwala na wyświetlenie jego zawartości. Prawo write pozwala na modyfikowanie elementu, wraz z jego usunięciem. Ostatnie prawo execute pozwala na uruchomienie pliku o ile jest to plik wykonywalny, w przypadku katalogów to prawo jest niezbędne aby użytkownik mógł przejść do niego (za pomocą polecenia cd).

Zatem w przypadku pliku z powyższego przykładu, właściciel może czytać i pisać, a grupa oraz wszyscy inni mogą tylko czytać, dla nich wówczas ten plik jest plikiem tylko do odczytu.

Prawdopodobnie kiedyś bedziemy chcieli ukryć jakieś pliki lub będziemy musieli dać uprawnienia jednemu z użytkowników do innych plików w systemie, wówczas wymagana będzie zmiana uprawnień.

Poszczególnym klasom (u, g, o) możemy nadać (+), obebrać (-) lub przypisać (=) odpowiednie uprawnienia. Do manipulacji uprawnieniami służy polecenie chmod, jako pierwszy argument przyjmuje on sekwencje zmiany uprawnień (uprawnienia) a jako drugi plik lub katalog, którego mają się tyczyć uprawnienia.

Załóżmy, że plik z powyższego przykładu jest skryptem i musimy nadać użytkownikowi prawo do wykonania, grupie prawo do zapisu i wykonania a pozostałym użytkownikom zabrać uprawnienia, ponieważ ten plik nie powinien być stosowany poza grupą. Aby zrealizować tę zmianę uprawnień należy wydać poniższe polecenie:

[user@server1 ~]$ chmod u+x,g+wx,o-r file1

Po zmianie, uprawnienia do tego pliku prezentują się w następujący sposób:

[user@server1 ~]$ ls -l file1
-rwxrwx---. 1 user user 0 Jun 21 14:21 file1

Zapis uprawnień w tej postaci, który swoją drogą nazwa się zapisem symbolicznym, wymaga dużo pisania. Uprawnienia można zmieniać łatwiej jednak wymagać to będzie więcej myślenia.

Każde z omówionych wcześniej praw posiada swoją wagę, te wagi sumują się dając uprawnienia dla konkretnej klasy. A więc: prawo do odczytu (r) posiada wagę 4, prawo do zapisu (w) posiada wagę 2 a prawo do wykonania (x) posiada wagę 1. Brak prawa (-) ma wagę 0. Tak złożone razem sumy tych wag dają uprawnienia do pliku czy katalogu. Chcąc określić uprawnienia pliku z pierwszego przykładu jescze przed zmianą uprawnień, będą one wynosić: 644.

[user@server1 ~]$ ls -l file1
-rw-r--r--. 1 user user 0 Jun 21 14:21 file1
----------------------------------------------
u = rw- = 4 + 2 + 0 = 6
g = r-- = 4 + 0 + 0 = 4
o = r-- = 4 + 0 + 0 = 4
ugo = 644

Natomiast uprawnienia po zmianie bedą wynosić:

[user@server1 ~]$ ls -l file1
-rwxrwx---. 1 user user 0 Jun 21 14:21 file1
----------------------------------------------
u = rwx = 4 + 2 + 1 = 7
g = rwx = 4 + 2 + 1 = 7
o = --- = 0 + 0 + 0 = 0
ugo = 770

W ten sposób przeliczonymi uprawnieniami (tzw. uprawnieniami bezwględnymi, ponieważ przypisują one uprawnienia każdej z klas) możemy posługiwać się podczas ich zmiany przy użyciu polecenia chmod

[user@server1 ~]$ chmod -v 644 file1
mode of 'file1' changed from 0770 (rwxrwx---) to 0644 (rw-r--r--)
[user@server1 ~]$ chmod -v 770 file1
mode of 'file1' changed from 0644 (rw-r--r--) to 0770 (rwxrwx---)

Na powyższym przykładzie zastosowałem opcję -v, która wyświetla jak zmieniają się uprawnienia oraz przedstawia je w obu formach. W przypadku wyświetlonej wartości liczbowej, pierwsze zero z lewej strony jest miejscem na uprawnienia specjalne o których będzie za chwilę, w przypadku standardowych uprawnienień nie ma ono znaczenia.

Zapis uprawnień bezwzględnych w zapisie symbolicznym wyglądał by następująco:

[user@server1 ~]$ chmod -v u=rw,g=r,o=r file1
mode of 'file1' changed from 0770 (rwxrwx---) to 0644 (rw-r--r--)

Definiując uprawnienia bezwzględne, posługujemy się cyframi od 0 do 7. Są to wszystkie cyfry systemu liczbowego o podstawie 8 (tzw. ósemkowy lub oktalny) dlatego też często nazwa się takie ustalanie uprawnień trybem oktalnym.

Podczas ustalania uprawnień dla plików lub katalogów, nie ma znaczenia jakiego trybu użyjemy, nie ma lepszego i gorszego. Jest tryb czytelniejszy oraz szybszy do zapisania.

Na koniec jako ciekawostke podam iż istnieje czwarta dodatkowa klasa oznaczająca wszystkie klasy (a), za jej pomocą można nadawać, odbierać i przypisywać takie same uprawnienia dla wszystkich klas.

[user@server1 ~]$ chmod -v a=rwx file1
mode of 'file1' changed from 0644 (rw-r--r--) to 0777 (rwxrwxrwx)

4.1.1. Uprawnienia domyślne

Każdy nowoutworzony plik czy katalog Uniksie ma już z góry przypisane uprawnienia i są nimi 666 dla plików oraz 777 dla katalogów. Uprawnienia te są korygowane za pomocą wartości umask.

Wartość umask (ang. user mask) jest maską uprawnień stosowaną na tworzonych przez użytkowników nowych plikach i katalogach. Każdy użytkownik ma swoją indywidualną maskę i może ją dowolnie zmieniać. Maska ma za zadanie usunięcie tych praw, których nie życzymy sobie widzieć w poszczególnych klasach. Aby wyświetlić maskę użytkownika należy wydać poniższe polecenie:

[user@server1 ~]$ umask
0022

Maska w przypadku RHEL 9 wygląda tak jak na powyszym przykładzie. Ta wartość usunie z klasy grupy oraz z klasy pozostałych użytkowników prawo do zapisu. Rachunek uprawnień jest bardzo prosty.

#W przypadku plików:

0666
0022 -
------
0644

#W przypadku katalogów:

0777
0022 -
------
0755

Chcąc zmienić maskę wystarczy wydać polecenie:

[user@server1 ~]$ umask 002
[user@server1 ~]$ umask
0002

Tak zdefiniowana maska usunie tylko prawo do zapisu w klasie pozostałych użytkowników dla każdego nowego pliku i katalogu. Maska o tej wartości była domyślną maską dla zwykłych użytkowników w RHEL 8. Wartość maski zdefiniowanej z poziomu wiersza poleceń, utrzyma się tylko przez czas trwania otwartej sesji terminala. Po jego zamknięciu lub wylogowaniu się maska wróci do poprzedniej wartości. Jeśli chcemy aby wartości tej maski została zapisana na stałe, należy umieść polecenie umask w jednym z plików uruchomieniowych powłoki, będzie o tym w dalszej części tego materiału.

4.1.2. Zmiana właściciela oraz grupy pliku

Do zmiany właściciela pliku oraz grupy, do której on należy służy polecenie chown, które przyjmuje jako pierwszy argument trzy możliwe wersje: parę właściciel:grupa; samego właściela lub nazwę grup poprzedzoną dwukropkiem (:) a następnie pliki. Poniższy przykład obrazuje wszystkie trzy możliwe zmiany właściciela oraz grup, do której należy plik.

[root@server1 tmp]# chown user100:user100 acluser
[root@server1 tmp]# ls -l acluser 
-rw-rw-r--. 1 user100 user100 9 Jun 22 18:25 acluser
[root@server1 tmp]# chown user acluser
[root@server1 tmp]# ls -l acluser 
-rw-rw-r--. 1 user user100 9 Jun 22 18:25 acluser
[root@server1 tmp]# chown :user acluser
[root@server1 tmp]# ls -l acluser
-rw-rw-r--. 1 user user 9 Jun 22 18:25 acluser

4.2. Uprawnienia specjalne

Poza podstawowym rodzajem uprawnień w Uniksach istnieją dodatkowe uprawnienia, których zadaniem może być podniesienie uprawnień w systemie, zaoszczędzenie czasu lub ochrona przed skasowaniem pliku w miejscach gdzie wszyscy mają uprawnienia zapisu. Teraz omówimy sobie każdy z nich.

4.2.1. Uprawnienia bitu setuid

Każde z podstawowych praw, możemy określić mianej bitu, z uprawnieniami specjalnymi jest podobnie, każde z nich jest kolejnym bitem (prawem) o szczególnym znaczeniu. Tak jest i tym przypadku. Bit setuid służy do podniesienia uprawnień. Jest on oznaczany małą lub wielką literą s w klasie użytkownika/właściciela. Ma on zastosowanie głównie na plikach wykonywalnych. Kiedy uruchomimy program z ustawionym takim bitem, program ten będzie działać z uprawnieniami właściciela tego pliku. Bit ten jest wykorzystywany najczęściej do uruchomienia programu z uprawnieniami root przez zwykłego użytkownika. Takim przykładem może być program su, który służy przełączaniu się miedzy użytkownikami, bez tego bitu przelogowanie nie było by możliwe. Dla eksperymentu możemy usunać ten bit, następnie spróbować jako zwykły użytkownik przelogować na kogoś innego.

[root@server1 ~]# chmod -v u-s /usr/bin/su
mode of '/usr/bin/su' changed from 4755 (rwsr-xr-x) to 0755 (rwxr-xr-x)
[user@server1 ~]$ su user100
Password: 
su: Authentication failure

Jak widać przełączanie na użytkownika user100 nie powiodło się mimo iż polecenie zapytało o hasło (polecenie su nie pyta o hasło tylko superużytkownika). Zwróćmy również uwagę na to jak wygląda bit setuid w oktalnym zapisie uprawnień. Do wskazywania uprawnień specjalnych wykorzystywana jest nieużywana przy zwykłych uprawnieniach czwarta liczba całkiem z lewej strony. Teraz przywrócimy setuid temu programowi.

[root@server1 ~]# chmod -v +4000 /usr/bin/su
mode of '/usr/bin/su' changed from 0755 (rwxr-xr-x) to 4755 (rwsr-xr-x)
[user@server1 ~]$ su user100
Password: 
[user100@server1 user]$ 

Podczas przywracania uprawnień, nie naruszyłem tych podstawowych używając wartości +4000, nie potrzeba wskazywać klasy, ponieważ program sam wie, że ten bit należy nadać w klasie właściciela.

Czasami bit setuid może występować w postaci wielkiej litery S, oznacza to tyle iż w klasie właściciela nie występuje prawo do wykonywania (bit x).

4.2.2. Uprawnienia bitu setgid

Zastosowania bitu setgid, mogą być dwa jedno podobne do bitu setuid, ale tyczące się grupy, drugie zaś dotyczy się katalogów współdzielonych. Bit setgit oznaczany jest małą lub wielką literą s w klasie grupy.

Kiedy katalog posiada ustawiony bit setgid, to grupa, która jest właścicielem takiego katalogu będzie przypisywana jako grupa każdemu nowemu elementowi utworzonemu w tym katalogu.

[user2000@server1 sdir]$ ls -ld /sdir
drwxrws--T. 3 root sgrp2 65 Jun 21 17:31 /sdir
[user2000@server1 sdir]$ mkdir user2000dir
[user2000@server1 sdir]$ ls -l
total 4
-rw-rw-r--. 1 user1000 sgrp2 5 Jun 20 16:00 user1000file
drwxrwsr-x. 2 user2000 sgrp2 6 Jun 21 17:30 user2000dir
[user2000@server1 sdir]$ touch user2000file
[user2000@server1 sdir]$ ls -l
total 4
-rw-rw-r--. 1 user1000 sgrp2 5 Jun 20 16:00 user1000file
drwxrwsr-x. 2 user2000 sgrp2 6 Jun 21 17:30 user2000dir
-rw-rw-r--. 1 user2000 sgrp2 0 Jun 21 17:31 user2000file

Katalog /sdir jest własnością grupy sgrp2 oraz posiada ustawiowiony bit setgid (bit s w miejscu x w klasie grupy). Utworzyłem katalog oraz plik, oba elementy odziedziczyły grupę sgrp2 właśnie dzięki tym uprawnieniom. Zwróćmy uwagę również na fakt iż nowoutworzony katalog odziedziczył również bit setgid. Tego typu funkcjonalność możemy wykorzystać tworząc katalogi do pracy grupowej, aby zaoszczędzić użytkownikom grupy zmianę uprawnień przy każdym nowym pliku lub podkatalogu w tego typu folderach.

Innym przykładem jest wykorzystanie pliku wykonywalnego z ustawionym bitem setgid. Weźmy pod lupe taki plik jak program write, ten program służy do przesyłania wiadomości między użytkownikami (między terminalami).

[user@server1 ~]$ ls -l /usr/bin/write 
-rwxr-sr-x. 1 root tty 24472 Feb 24 11:25 /usr/bin/write

Posiada on bit setgid, którym pozwoli mu w tym przypadku skorzystać z uprawnień grupy na plikach urządzeń. Pierwsza konsola wirtualna w systemie:

[user@server1 ~]$ ls -l /dev/tty0 
crw--w----. 1 root tty 4, 0 Jun 21 15:13 /dev/tty0

Grupa tty posiada prawo do zapisu na tym urządzeniu. Sprawdźmy jeszcze pierwsze urządzenie pseudoterminala (okno programu Terminal lub połączenie przez SSH).

[user@server1 ~]$ ls -l /dev/pts/0
crw--w----. 1 user tty 136, 0 Jun 21 17:51 /dev/pts/0

Tutaj grupa również posiada prawo zapisu. Usuwanie i przypisywanie tego bitu jest analogiczne do bitu setuid, wymaga tylko zmiany kilku wartości

[root@server1 ~]# chmod -v g-s /usr/bin/write
mode of '/usr/bin/write' changed from 2755 (rwxr-sr-x) to 0755 (rwxr-xr-x)
[root@server1 ~]# chmod -v +2000 /usr/bin/write
mode of '/usr/bin/write' changed from 0755 (rwxr-xr-x) to 2755 (rwxr-sr-x)

Przeglądając atrybuty plików i katalogów możemy natknąć sie na bit setgid zapisany wielką literą, jest to ten sam bit, tylko że w klasie grupy brakuje prawa do wykonania (bitu x).

4.2.3. Uprawnienia bitu sticky

Bit sticky ma za zadanie zabezpieczyć pliki i katalogi przed nieuprawnionym usunięciem w ogólodostępnych katalogach (tam gdzie wszyscy mają prawo do zapisu). Jeśli katalog ma ustawiony taki bit, to wówczas tylko właściciel pliku bądź podkatalogu będzie mógł go usunąć. Bit sticky oznaczany jest za pomocą małej lub wielkiej litery t w klasie publicznej (pozostałych użytkowników).

Usunięcie lub przypisanie tego bitu może odbywać się tak jak w przypadku poprzednich bitów.

[root@server1 ~]# chmod -v -o-t /sdir
mode of '/sdir' changed from 3770 (rwxrws--T) to 2770 (rwxrws---)
[root@server1 ~]# chmod -v +1000 /sdir
mode of '/sdir' changed from 2770 (rwxrws---) to 3770 (rwxrws--T)

Tutaj zwróćmy uwagę na to, iż bit sticky jest zapisany wielką literą, otóż działanie tego bitu się nie zmienia, jest jedynie informacja, że klasie publicznej brakuje prawa do wykonania (x). Wszystkie bity specjalne umieszczane w poszczególnych klasach, rezydują w miejscu prawa do wykonywania.

4.3. Polecenie find

Polecenie find, jak sama nazwa wskazuje służy do wyszukiwania plików i katalogów. Najczęściej jest ono wykorzystywane przez użytkowników w podstawowym zakresie, zwracając gigantyczną ilość danych. Często korzystając z tego narzędzia nie wykorzystuje się w pełni jego potencjału, który ukrywa się w dużej ilości opcji zawężających wyniki poszukiwania oraz możliwości wykonania akcji w wyszukanych plikach.

Najprostszy sposób na użycie polecenia find znajduje się poniżej:

[root@server1 user]# find / -name network
/run/systemd/network
/tmp/gzipped/etc/sysconfig/network
/tmp/gzipped/etc/vmware-tools/scripts/vmware/network
/etc/sysconfig/network
/etc/vmware-tools/scripts/vmware/network
/root/bzipped/etc/sysconfig/network
/root/bzipped/etc/vmware-tools/scripts/vmware/network
/usr/lib/systemd/network

Polecenie przeszukało cały główny system plików pod kątem plików o nazwie network. Sama opcja -name jest zawężeniem wyników wyszukiwania, bez tej opcji polecenie zwróciłoby wszystkie pliki znalezione w podanym katalogu czyli w tym przypadku minimum kilka tysięcy.

Poniżej zamieszczam najważniejsze opcje służące do bardziej szczegółowego wyszukiwania plików.

4.3.1. Opcje akcji polecenia find

Na powyższych przykładach stosowałem wielkrotnie dodatkową opcję -exec. Jest to opcja akcji podejmowana na pliku gdy tylko zostanie znaleziony. Składania opcji -exec może wyglądać następująco:

$ find ... -exec ls -ld {} \;

Po opcji -exec podajemy zwykłe polecenie, tak jak gdybyśmy wpisywali je do wiersza polecenia. Odniesieniu do znalezionego pliku służy zapisana para nawiasów klamrowych {} każde polecenie wydawane za pomocą opcji -exec czy innej musi zostać za terminowane zacytowanym średnikiem (\;), ponieważ jest koniec polecenia wprowadzonego do tej opcji, a nie koniec polecenia find. Wynik działania przykładowego użycia opcji -exec znajduje się w przykładach na początku tego podrozdziału.

Poza poleceniem -exec, istnieją jeszcze dwa polecenia akcji -delete, które usuwa pliki oraz -ok, które różni się tym od -exec, że pyta sie użytkownika czy wykonać podane polecenie na pliku.

Odnośnie egzaminu:
Polecenie find, wydaje się bardzo pomocne, posiada masę opcji część znich znajduje się tutaj, pozostałe są rozpisane na stronach podręcznika. Powinniśmy umieć się posługiwać tym narzędziem, zarówno zaweżać obszar poszukiwania jak i prawidłowo używać opcji -exec.

4.4. Lista kontroli dostępu

Oprócz klasycznego zestawu uprawnień w Uniksach oraz określenia właściciela i grupy do której należy plik bądź katalog, w RHEL możemy dodatkowo wskazać innych użytkowników oraz grupy i przypisać im uprawnienia nieżależne od tych klasycznych (standardu ugo/rwx). Temu właśnie służy lista kontroli dostępu. Ten mechanizm obsługiwany jest za pomocą dwóch poleceń, pierwszym z nich jest getfacl i służy ono do wypisania listy kontroli dostępu. Stwórzmy w katalogu /tmp plik o nazwie aclfile, następnie wykonajmy polecenie getfacl podając ten plik jako argument.

[user@server1 ~]$ cd /tmp
[user@server1 tmp]$ touch acluser
[user@server1 tmp]$ getfacl acluser 
# file: acluser
# owner: user
# group: user
user::rw-
group::rw-
other::r--

[user@server1 tmp]$ ls -l acluser
-rw-rw-r--. 1 user user 9 Jun 22 18:25 acluser

Kiedy plik niema zdefiniowanych żadnych dodatkowych użytkowników oraz grup, jego lista kontroli dostępu jest równoznaczna z klasycznymi uprawnieniami Uniksa i te listy oznaczają kolejno właściciela, grupę do której należy ten plik oraz pozostałych użytkowników. Listy kontroli dostępu możemy modyfikować za pomocą polecenia setfacl. Zanim jednak poznamy jak należy modyfikować ACL, poniżej znajduje się lista najważniejszych opcji tego polecenia. Są one dość istotne, ponieważ pozwalają na manipulowanie listami elementu.

Listy kontroli możemy zastoswać dla trzech podmiotów:

To co zostało ujęte w nawiasach kwadratowych, jest zapisem opcjonalnym. Identyfikator podmiotu możemy zapisać albo za pomocą numerów UID oraz GID, lub za pomocą nazw. Jeśli nazwa lub UID/GID zostaną pominiete to listy user oraz group będą tyczyć się właściciela oraz grupy, do której należy dany element. Uprawnienia mogą być podane za pomocą symboli lub sumy wag dla pojedynczej klasy. Ustawię teraz listę dla jednego z użytkowników w systemie.

[user@server1 tmp]$ setfacl -m u:user100:rw acluser
[user@server1 tmp]$ ls -l acluser
-rw-rw-r--+ 1 user user 9 Jun 22 18:25 acluser

Pierwsze polecenie ustawia prawo do czytania oraz pisania użytkownikowi user100 za pomocą list dostępu. Wyświetliłem szczegóły pliku. Zwróćmy uwagę na to, iż kropka na końcu uprawnień została zastąpiona plusem (+), taki zapis informuje użytkowników, że uprawnienia tego pliku są rozszerzone za pomocą ACL. Teraz przyjrzymy sie jak wyglądają listy pliku:

[user@server1 tmp]$ getfacl -c acluser
user::rw-
user:user100:rw-
group::rw-
mask::rw-
other::r--

W tym przykładzie użyłem opcji -c, która nie wyświetla wcześniej pokazywanego nagłówka. Nie jest on nam potrzebny. Nowy wpis pojawia się pod wpisem właśiciela. Mówi on wprost, że użytkownik user100 posiada prawo do czytania oraz modyfikowania pliku acluser, mimo iż na podstawie klasycznych uprawnień oraz praw własności nie jest on nijak związany z tym plikiem.

Poza uprawnienia dla użytkownika pojawi się wpis oznaczony słowem mask. Jest to maska list kontroli i wyznacza ona maksymalne uprawnienia jakie można dać danemu podmiotowi. Przy każdej zmianie list kontroli dostosowuje się ona do najwyższych uprawnień na liście. Dodam teraz drugiego użytkownika z wyższymi od user100 uprawnieniami, bedziemy mogli wówczas zaobserowować jak zmienia się maska.

[user@server1 tmp]$ setfacl -m u:user200:7 acluser
[user@server1 tmp]$ getfacl -c acluser
user::rw-
user:user100:rw-
user:user200:rwx
group::rw-
mask::rwx
other::r--

Maska została ponownie przeliczona i wskazuje teraz uprawnienia o sumie wag równej 7 (rwx). Teraz wymuszę inną wartość dla maski:

[user@server1 tmp]$ setfacl -m m:4 acluser 
[user@server1 tmp]$ getfacl -c acluser
user::rw-
user:user100:rw-		#effective:r--
user:user200:rwx		#effective:r--
group::rw-			    #effective:r--
mask::r--
other::r--

Na powyższym przykładzie za pomocą selektora (u: - user, itd.) m: wskazuje uprawnienia dla maski ACL. Teraz przy zdefiniowanych przez nas listach użytkowników, pojawił się komentarz #effective:r--. Oznacza to, iż uprawnienia zostały zmniejszone ponieważ, przekraczają nałożoną na plik maskę list kontroli. Zrócmy uwagę, ze to ograniczenie uprawnień maską dotknęło grupę, do której należy plik. Aby wyłączyć automatyczne przeliczanie maski, należy użyć opcji -n. Usunę teraz jedną z list, aby zaprezentować jak można to zrobić kiedy listy dla poszczególnych użytkowników nie będą nam potrzebne:

[user@server1 tmp]$ getfacl -c acluser 
user::rw-
user:user100:rw-
user:user200:rwx
group::r--
mask::rwx
other::r--

[user@server1 tmp]$ setfacl -x u:user200 acluser 
[user@server1 tmp]$ getfacl -c acluser 
user::rw-
user:user100:rw-
group::r--
mask::rw-
other::r--

Teraz oczyszcze plik z list, ponieważ nie są nam już one potrzebne.

[user@server1 tmp]$ setfacl -b acluser
[user@server1 tmp]$ getfacl -c acluser
user::rw-
group::r--
other::r--

Po usunięciu list z tak rygorystczną maską możemy zaobserwować, że zostały zmodyfikowane uprawnienia grupy. Przed usunięciem list należy ustawić maskę aby odzworowywała wcześniejsze uprawnienia.

4.4.1. Domyślne listy kontroli dostępu

Domyślne listy kontroli dostępu są stosowane w udostępnionych katalogach, aby zapewnić spójny zestaw uprawnień dla elementów w nim zawartych. Listy kontroli zdefiniowane dla takiego katalogu są dzieczone przez elementy, które się w nim znajdują. Dzięki czemu nie trzeba poprawiać uprawnień przy każdym nowym pliku lub katalogu. Dziedziczenie nieco różni się w przypadku plików i podkatalogów:

Domyślnie listy definiuje się tak samo jak zwykłe, jednak robi się to tylko na katalogach za pomocą polecenia setfacl z dodatkową opcją -d. Utworzę teraz podkatalog o nazwie projekty w katalogu /tmp, następnie nadam mu dwie domyślnie listy dając dwóm użytkownikom systemu pełne uprawnienia do niego.

[user@server1 tmp]$ mkdir projekt
[user@server1 tmp]$ setfacl -dm u:user100:7,u:user200:7 projekt/
[user@server1 tmp]$ getfacl -c projekt/
user::rwx
group::r-x
other::r-x
default:user::rwx
default:user:user100:rwx
default:user:user200:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

Następnie utworzę plik o nazwie zalozenia_projektu.txt, poniżej znajdują sie jego szczegóły:

[user@server1 projekt]$ touch zalozenia_projektu.txt
[user@server1 projekt]$ ls -l
total 0
-rw-rw-r--+ 1 user user 0 Jun 23 15:56 zalozenia_projektu.txt

Jak widzimy posiada on już jakieś zdefiniowane listy. Są to listy odziedziczone po katalogu nadrzędnym. Oto listy tego pliku:

[user@server1 projekt]$ getfacl -c zalozenia_projektu.txt 
user::rw-
user:user100:rwx		#effective:rw-
user:user200:rwx		#effective:rw-
group::r-x			#effective:r--
mask::rw-
other::r--

Zauważmy, że wpływ na listy mają uprawnienia domyślne elementów (plików i katalogów) oraz wartość umask. Maska ACL przyjmuje wartość najwyższych zdefiniowanych uprawnień, w przypadku plików jest 6 (odczyt oraz zapis). Następnie odziedziczone listy są konfrontowane z maską i zdefiniowane w nich uprawnienia albo pozostają bez zmian, albo ustawiane są tylko te bity występujące na masce, dlatego też efektywne listy kontroli są takie a nie inne. Teraz do zwykłych list katalogu projekty dodam listy dwóch użytkowników dodanych jako listy domyślnie i utworze podkatalog.

[user@server1 tmp]$ setfacl -m u:user100:7,u:user200:7 projekt
[user@server1 tmp]$ getfacl -c projekt/
user::rwx
user:user100:rwx
user:user200:rwx
group::r-x
mask::rwx
other::r-x
default:user::rwx
default:user:user100:rwx
default:user:user200:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

[user@server1 tmp]$ cd projekt/
[user@server1 projekt]$ mkdir commonfiles
[user@server1 projekt]$ getfacl -c commonfiles/
user::rwx
user:user100:rwx
user:user200:rwx
group::r-x
mask::rwx
other::r-x
default:user::rwx
default:user:user100:rwx
default:user:user200:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

Jak możemy zauważyć, listy kontroli są identyczne. Teraz dwaj pozostali użytkownicy posiadają możliwości zapisywania oraz odczytywania, w przypadku katalogów i plików stworzonych przeze mnie. Jednak ja jako user będę chciał zapisać coś w plikach innych użytkowników to nie mam takich uprawnień, ponieważ listy dotyczą tylko tych dwóch użytkowników zdefiniowanych na listach.

4.4.2. Dziwny problem z listami kontroli dostępu

Odnośnie egzaminu:
Zapoznając się ze zwykłymi listami dostępu po zdefiniowaniu list o uprawnieniach dających pełne prawa jednemu z użytkowników nie mogłem (jako on) zapisać nic do pliku znajdującego się w katalogu /tmp za pomocą jakiego kolwiek edytora. Dane mogłem zapisać natomiast przekierowując strumień na przykład z polecenia echo. Ten problem zauważyłem w RHEL 9, w wersji 8.6 nie występował. Problem nie występuje na RHEL 9, gdy plik odziedziczy listy od katalogu, a uprawnienia zostaną zmniejszone przez maskę, wówczas użytkownicy z mogą zapisywać zmiany za pomocą edytorów.

Ćwiczenie 1: Manipulowanie uprawnieniami pliku

Jak zwykły użytkownik na maszynie server1, stwórz dwa pliki file11 oraz dir11. Uruchom polecenie umask aby określić obecną maskę użytkownika. Używając zapisu symbolicznego ustaw maskę użytkownika na wartość 0035. Utwórz plik o nazwie file22 oraz katalog o nazwie dir22 w katalogu domowym. Sprawdź i porównaj uprawnienia plików i katalogów utworzonych na potrzeby tego ćwiczenia. Używając polecenia chmod zmień uprawnienia pliku file11 aby były takie jak uprawnienia pliku file22 oraz zmień uprawnienia katalogu dir22 tak aby identyczne jak uprawnienia folderu dir11. Nie usuwaj jeszcze tych plików.

Ćwiczenie 2: Konfiguracja katalogu do pracy grupowej

Jako superużytkownik na maszynie server1, utwórz katalog /sdir następnie utwórz grupę (groupadd) sgrp oraz dwóch użytkowników user1000 oraz user2000 (useradd). Dodaj tych dwóch użytkowników do wcześniej utworzonej grupy. Ustaw właściela katalogu na użytkownika root oraz sgrp jako grupę do której należy, następnie ustaw odpowiednie uprawnienia: grupa=rwx; inni=--- poza tym ustaw bity setgid oraz sticky. Zaloguj się jako user1000 i utwórz w wyżej wspomnianym katalogu plik, następnie zaloguj się jako user2000 dokonaj zmian w utworzonym przez user1000 pliku. Na koniec spróbuj go usunąć. Zapisanie zmian powinno się zakończyć się sukcesem, ale próba usunięcia już nie.

Ćwiczenie 3: Wyszukiwanie plików

Jako superużytkownik na maszynie server1, wyszukaj wszystkie pliki w całej strukturze katalogu głównego, które zostały zmodyfikowane w ciągu ostatnich 300 minut i wyświetl ich typ. Wyszukaj w całej strukturze katalogu głównego pliki nazwanych potoków oraz gniazd.

Ćwiczenie 4: Zaawansowane wyszukiwanie plików

Jak superużytkownik na maszynie server1, wyszukaj katalogu /usr zwykłe pliki z czasem dostępu starszym niż 100 dni oraz rozmiarem nie przekraczającym 5 MB, których właścicielem jest root.

Ćwiczenie 5: Ustawianie list dostępu

Jako superużytkownik na maszynie server1, utwórz plik testfile w katalogu /tmp. Utwórz uzytkowników user3000 oraz user4000. Stwórz na wszcześniej utworzonym pliku listy kontroli, ustawiając użytkownikowi user2000 uprawnienia równe 7, dla user3000 uprawnienia równe 6, a dla użytkownika user4000 uprawnienia równe 4. Usuń listę użytkownika user2000 i wyświetl pozostałe listy. Następnie oczyść plik z list dostępu oraz sprawdź czy nie pozostały żadne listy.

Podsumowanie

W tej części materiału zapoznaliśmy się z podstawowymi uprawnieniami plików i katalogów, nauczyliśmy się je ustawiać w dwóch różnych formach zapisu. Poznaliśmy uprawnienia specjalne oraz jak je przypisywać. Dowiedzielśmy się jak ważnym narzędziem jest polecenie find. Na koniec poznaliśmy sposób na rozszerzenie uprawnień poza standard ugo/rwx za pomocą list kontroli dostępu. W następnym rozdziale zajmiemy się użytkownikami.

5. Podstawy zarządzania użytkownikami

Użytkownik jest encją, czyli odwzrowaniem rzeczywistego elementu. Obecność użytkowników w systemie ma jeden cel - wyznaczać granice, bowiem każdy z użytkowników posiada swój katalog domowy, w którym przechowuje swoje pliki osobiste oraz programy. W tym rozdziale nauczymy się tworzyć użytkowników oraz modyfikować ich podstawowe informacje, jak np. położenie katalogu domowego czy zmianę numeru UID. Na początku jednak zapoznamy się z metodami sprawdzenia aktywności użytkowników w systemie, prześledzimy ich próby uzyskania dostępu do systemu.

5.1. Kto jest zalogowany do systemu i co robi

Za pomocą polecenia who, możemy sprawdzić kto jest zalogowany w systemie, na jakim terminalu pracuje, o której się zalogował, jeśli jest połączenie zdalne wówczas po czasie zalogowania zobaczymy adres IP w nawiasie. W moim systemie obecnie zalogowanych jest dwóch użytkowników:

[user@server1 ~]$ who
root     pts/0        2022-06-25 15:06 (192.168.8.192)
user     pts/1        2022-06-25 15:06 (192.168.8.192)

Innym poleceniem zwracającym bieżącą aktywność użytkowników systemie jest polecenie w. Poniżej znajduje się wynik uruchomienia polecenia na jednym z moich systemów.

user@server1 ~]$ w
 15:09:43 up 4 min,  2 users,  load average: 0.11, 0.23, 0.12
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0     15:06    7.00s  0.10s  0.09s /usr/bin/python3 -s /usr/bin/ranger
user     pts/1     15:06    1.00s  0.00s  0.00s w

Pierwsza linia informacji zwracanych przez to polecenie jest identyczna z informacjami, które zwraca polecenie uptime. Następnie zwraca ono tabele z nazwą użytkownika (USER), nazwą terminala (TTY), czasem zalogowania się (LOGIN@), czasem bezczynności (IDLE), czasem procesora zużytym na wykonanie wszystkich zadań po podłączeniu się do terminala wraz z zadaniami wykonywanymi w tle (JCPU), czasem procesora zużytym na wykonanie bieżącego procesu (PCPU) oraz nazwę procesu, która jest obecnie wykonywana (WHAT).

5.1.1. Monitorowanie prób logowania się do systemu

Monitorując aktywność systemu nie należy zapominać o sprawdzaniu prób zalogowania się do niego. W dystrybucjach Linuksa istnieją trzy polecenia przeznaczone do tego celu. Pierwszym z nich jest polecenie last, które zwraca listę ostatnio zalogowanych użytkowników.

[root@server1 ~]# last
user     pts/1        192.168.8.192    Sat Jun 25 15:06   still logged in
root     pts/0        192.168.8.192    Sat Jun 25 15:06   still logged in
reboot   system boot  5.14.0-70.13.1.e Sat Jun 25 17:05   still running
user     pts/0        192.168.8.182    Fri Jun 24 14:20 - 17:14  (02:53)
reboot   system boot  5.14.0-70.13.1.e Fri Jun 24 16:19 - 17:14  (00:54)
root     pts/1        192.168.8.182    Thu Jun 23 15:54 - 17:27  (01:32)
user     pts/0        192.168.8.182    Thu Jun 23 14:18 - 17:27  (03:08)
user     pts/0        192.168.8.182    Thu Jun 23 11:04 - 14:17  (03:13)
...

Polecenie poza tym kto się zalogował, do jakiego terminala, czy zdalnie czy lokalnie, zwraca również informacje, o której system został uruchomiony - wpisy rozpoczynające się od reboot. Nieścisłości czasowe wynikają z tego iż, zegar RTC na serwerze ustawiony jest z uzględniem strefy czasowej oraz zmian czasu lokalnego (powinien wskazywać na czas UTC), poźniej te dane są skorygowane po skomunikowaniu się z serwerami czasu. Chcąc wyświetlić tylko listę, kiedy dokonano startu systemu wydajemy to polecenie z argumentem reboot, ten argument jest tak naprawdę nazwą użytkownika.

[root@server1 ~]# last reboot
reboot   system boot  5.14.0-70.13.1.e Sat Jun 25 15:42   still running
reboot   system boot  5.14.0-70.13.1.e Sat Jun 25 17:05 - 15:42  (-1:23)
reboot   system boot  5.14.0-70.13.1.e Fri Jun 24 16:19 - 17:14  (00:54)
reboot   system boot  5.14.0-70.13.1.e Thu Jun 23 11:23 - 17:27  (06:03)
reboot   system boot  5.14.0-70.13.1.e Wed Jun 22 17:30 - 00:17  (06:47)
reboot   system boot  5.14.0-70.13.1.e Tue Jun 21 15:13 - 18:14  (03:00)
reboot   system boot  5.14.0-70.13.1.e Mon Jun 20 21:11 - 19:47  (-1:23)
reboot   system boot  5.14.0-70.13.1.e Mon Jun 20 11:54 - 16:18  (04:24)
...

Dane wyświetlane przez to polecenie przechowywane są w pliku /var/log/wtmp.

Alternatywnie do listy ostatnio zalogowanych użytkowników, możemy przejrzeć listę prób logowania się do systemu, które się nie powiodły. Temu służy polecenie lastb.

[root@server1 ~]# lastb
user     ssh:notty    192.168.8.182    Thu Jun 23 14:18 - 14:18  (00:00)
user     ssh:notty    192.168.8.182    Thu Jun 23 14:18 - 14:18  (00:00)
user1000 pts/0                         Tue Jun 21 17:28 - 17:28  (00:00)
user     ssh:notty    192.168.8.182    Mon Jun 20 19:27 - 19:27  (00:00)
user100  pts/1                         Mon Jun 20 13:00 - 13:00  (00:00)
user     ssh:notty    192.168.8.182    Mon Jun 20 10:44 - 10:44  (00:00)
root     pts/0                         Fri Jun 10 17:59 - 17:59  (00:00)

W przypadku połączeń SSH oznaczenie notty, mówi nam że nie przydzielono terminala z powodu niepowodzenia logowania. Kiedy w drugiej kolumnie zobaczymy oznaczenie pts/X (X oznacza numer urządzenia), to możemy stwierdzić, że nie powiodło się przełączenie na wskazanego w pierwszej kolumnie użytkownika. Na serwerach, które mają otwarty port 22 oraz pozwalają na zalogowanie się za pomocą hasła polecenie będzie zwracać zazwyczaj bardzo długą listę. Wiele automatów na całym świecie próbuje zalogować się na serwer poprzez zagdywanie użytkownika i hasła.

Plikiem, który przechowuje informacje zwracane przez to polecenie jest /var/log/btmp.

Ostanim narzędziem pozwalającym na sprawdzenie kto logował się do systemu jest polecenie lastlog, wyświetla ono ostatnie logowanie na podstawie listy użytkowników.

[root@server1 ~]# lastlog
...
user             pts/1    192.168.8.192                             Sat Jun 25 15:06:16 +0200 2022
vboxadd                                                             **Never logged in**
user100          pts/1                                              Thu Jun 23 15:55:01 +0200 2022
user200          pts/0                                              Fri Jun 24 16:55:08 +0200 2022
user1000         pts/0                                              Tue Jun 21 17:28:44 +0200 2022
user2000         pts/0                                              Tue Jun 21 17:28:50 +0200 2022
user3000                                                            **Never logged in**
user4000                                                            **Never logged in**
user3            pts/0                                              Thu Jun 23 12:13:34 +0200 2022
user4            pts/0                                              Thu Jun 23 12:33:09 +0200 2022
user4100                                                            **Never logged in**
user4200                                                            **Never logged in**
user4300         pts/0                                              Thu Jun 23 13:01:04 +0200 2022

Napis **Never logged in** oznacza, że użytkownik nie logował się do systemu. Informacje zwracane przez to polecenie są przechowywane w pliku /var/log/lastlog.

5.1.2. Informacje o użytkowniku

Podczas pracy z użytkownikami, możemy potrzebować informacji na jego temat. Równie dobrze moglibyśmy sprawdzić go w pliku /etc/passwd, ale jest lepszy sposób. Na Linuksach dostępne jest polecenie id, które zwraca UID, GID podstawowej grupy oraz grup, do których należy użytkownik podany jak argument tego polecenia:

[root@server1 ~]# id user100
uid=1001(user100) gid=1001(user100) groups=1001(user100),9999(sgrp)

Wówczas jeśli jako my sami potrzebuje wyświetlić, grupy do których należymy możemy użyć polecenia groups.

[user100@server1 ~]$ groups
user100 sgrp

5.2. Pliki autentykacji użytkowników

Każdy użytkownik, chcący skorzystać z systemu musi się uwierzytelnić za pomocą tylko jemu znanych poświadczeń, wówczas zostaje on autoryzowany do wykonywania różnych czynności w systemie. Sam system do uwierzytelniania użytkowników stosuje kilka plików.

5.2.1. Pliki /etc/passwd

Ten plik jest swoistą bazą danych o użytkownikach w systemie. Każdy jeden wpis to jeden użytkownik.

[root@server1 ~]# tail -5 /etc/passwd
user:x:1000:1000:user:/home/user:/bin/bash
user100:x:1001:1001::/home/user100:/bin/bash
user200:x:1002:1002::/home/user200:/bin/bash
user1000:x:1003:1003::/home/user1000:/bin/bash
user2000:x:1004:1004::/home/user2000:/bin/bash

Każdy z tych wpisów zawiera kilka pól oddzielonych od siebie dwukropkiem (:). Natomiast same pola oznaczają kolejno:

5.2.2. Plik /etc/shadow

Plik /etc/shadow, koreluje z plikem /etc/passwd ich wspólnym kluczem jest nazwa użytkownika. Plik ten zamiast ogólnych informacji o użytkowniku zawiera jego hasło oraz zasady związane z hasłami. Tak jak w przypadku pliku passwd każdy wpis odpowiada jedemu użytkownikowi.

[root@server1 ~]# tail -5 /etc/shadow
user:$6$bNlZFNZV/wWKGQU8$DgIHeUdALGWs7bZ7Foet2UeHFSANSvYcEtLmn6aLm5QxLcx10jZNkUg.dphdmizej/k5Xe.oEqSCeHAf5/7Gv0::0:99999:7:::
user100:$6$N3norXlohg0yE1pi$FyeKcBNl4mj/nLbNRlx7KaAQnddG0y/kXpQ9pbgjTV9ti3dQ5q3pIpXm1poa85oxFHFVT9HPTyBmaRNkrSJPX.:19163:0:99999:7:::
user200:!!:19163:0:99999:7:::
user1000:!!:19163:0:99999:7:::
user2000:!!:19163:0:99999:7:::

Każdy z tych wpisów zawiera kilka pól oddzielonych od siebie dwukropkiem (:). Natomiast same pola oznaczają kolejno:

W przypadku pól od zaszyfrowanego hasła do pola zarezerowanego, puste pole lub 0 wyłącza przeznaczone im właściwości. Część z tych opcji ustawiana jest w pliku /etc/login.defs i nazwane są zasadami ważności haseł lub ang. pasword aging. Na koniec zwróćmy uwagę na szczegóły tego pliku:

[root@server1 ~]# ls -l /etc/shadow
----------. 1 root root 2012 Jun 25 18:53 /etc/shadow

Nikt nie ma żadnych uprawnień do tego pliku. Możliwa jest jego modyfikacja za pomocą specjalnego mechanizmu, który będzie omawiany przy okazji SELinux.

5.2.3. Plik /etc/group

Plik /etc/group jest odpowiednikiem dla pliku /etc/passwd tylko, że dla group. Ten plik ma podobną budową do plików /etc/passwd lub /etc/shadow posiada jednak mniejszą ilość pól.

[root@server1 ~]# tail -5 /etc/group
user:x:1000:
user100:x:1001:
user200:x:1002:
sgrp:x:9999:user100,user200
sgrp2:x:8888:user1000,user2000

A każde z pół przechowuje kolejno:

5.2.4. Plik /etc/gshadow

Grupy jak i użytkownicy również mają swoje hasła. W opisie pola hasła przy okazji pliku /etc/group wyjaśniłem dlaczego tak jest. Te hasła muszą być gdzieś składowane i to w bezpieczny sposób. Hasła grup również są przesłonione jak hasła użytkowników. Miejscem składowania haseł grup jest jest plik /etc/gshadow, zawiera on podobne wpisy jak plik /etc/group i jest z nim powiązany wspólnym kluczem jakim jest nazwa grupy.

[root@server1 user]# tail /etc/gshadow
user:!::
user100:!::
user200:!::
dba:!::
user1000:!::
user2000:!::
dbagrp:!::
user90:!::
g1:!::

Plik /etc/gshadow zawiera 4 pola tak jak plik /etc/group, każde z tych pól oznacza kolejno:

5.3. Podstawy zarządzania użytkownikami

W tym podrozdziale poznamy podstawy zarządzania użytkownikami. Będziemy tworzyć użytkowników, zmieniać informacje o nich oraz ich usuwać. Tworząc nowego użytkownika wydajemy proste polecenie i podajemy jego nazwę. Taki użytkownik tworzony jest z domyślnymi wartościami. Zanim zaczniemy tworzyć użytkowników poznamy dwa miejsca, skąd mogą brać się te domyślne wartości.

5.3.1. Domyślnie informacje użytkowników

Informacje o tym jaką przydzielić użytkownikowi powłokę, czy między jakimi wartościami powinien znajdować się jego UID są w systemie z góry określone. Takimi miejscami, w których możemy szukać domyślnych wartości jest plik /etc/default/useradd, którego zawartość dostępna jest po wydaniu poniższego polecenia:

[user@server1 ~]$ useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes

Z tych informacji możemy wynioskować początkowy GID (GROUP), jednak ta opcja używana jest gdy opcja USERGROUPS_ENAB, w pliku /etc/login.defs (drugim z plików) jest ustawiona na NO. Poza tym znamy domyślną lokalizację tworzenia katalogów domowych (HOME), czas nieaktywność (INACTIVE), który określa liczbę dni pomiędzy wygaśnięciem hasła a trwałym zablokowaniem konta, domyślną datę wygaśnięcia konta (EXPIRE), domyślną powłokę (SHELL), lokalizację katalogu szkieletowego (SKEL) - katalog ten przechowuje pliki kopiowane do każdego katalogu domowego nowego użytkownika w momencie jego utworzenia. Ostatnią wartością jest zmienną określająca czy tworzyć pliki skrzynki mailowej każdemu użytkownikowi (CREATE_MAIL_SPOOL). Nie które z tych wartości możemy modyfikować używając tego samego polecenia, więcej informacji na ten temat znajduje się na stronie podręcznia polecenia useradd.

Innym źródłem wartości domyślnych wykorzystywanych podczas tworzenia użytkowników jest plik /etc/login.defs. Zawartość tego pliku najlepiej jest wyświetlić za pomocą poniższego polecenia, które odfiltruje komentarz i puste linie:

[user@server1 ~]$ grep -v ^# /etc/login.defs | grep -v ^$
MAIL_DIR	/var/spool/mail
UMASK		022
HOME_MODE	0700
PASS_MAX_DAYS	99999
PASS_MIN_DAYS	0
PASS_WARN_AGE	7
UID_MIN                  1000
UID_MAX                 60000
SYS_UID_MIN               201
SYS_UID_MAX               999
SUB_UID_MIN		   100000
SUB_UID_MAX		600100000
SUB_UID_COUNT		    65536
GID_MIN                  1000
GID_MAX                 60000
SYS_GID_MIN               201
SYS_GID_MAX               999
SUB_GID_MIN		   100000
SUB_GID_MAX		600100000
SUB_GID_COUNT		    65536
ENCRYPT_METHOD SHA512
USERGROUPS_ENAB yes
CREATE_HOME	yes
HMAC_CRYPTO_ALGO SHA512

Wewnątrz tego pliku znajdują się definicje takich wartości jak maska użytkownika (umask), elementy polityki haseł (PASS_...) czy zakresy wartości UID oraz GID. W tym pliku również znajduje się nazwa algorytmu używanego do przesłaniania haseł. Opisy poszczególnych wartości zawartych w tym pliku znajdują się na stronie podręcznika.

5.3.2. Tworzenie użytowników

Do tworzenia użytkowników systemie służy polecenie useradd, domyślnie wymaga on wyłącznie podania nazwy użytkownika. Podczas tej czynności zostaną dodane wpisy do plików autentykacyjnych, które omwialiśmy wcześniej, zostanie utworzony katalog domowy i przekopiowane zostaną do niego pliki z katalogu szkieletowego (/etc/skel). Utworzymy sobie teraz testowego użytkownika.

[root@server1 user]# useradd user1

To polecenie utworzy użytkownika z domyślnymi wartościamy identyfkatora, powłoki oraz innymi. Polecenie to daje możliwość zmiany tych wartości podczas tworzenie nowego użytkownika. Poniżej znajduje się lista opcji, na której zawarto najważniejsze z nich:

Chcąc użyć tych opcji podaje się je po nazwie polecenia przed nazwą użytkownika. W ramach ćwiczeń możemy utworzyć kilku użytkowników z losowymi wartościami opcji podanych powyżej. W tym rozdziale poznamy narzędzie, które służy do usuwania użytkowników, ale za nim to zrobimy poznamy polecenie, dzięki któremu będziemy mogli zmienić opcje już utworzonych użytkowników.

Tak utworzonym użytkowników pozostało tylko nadać hasło, przy użyciu polecenia passwd.

Tutaj podano tylko część opcji tego polecenia, więcej znajduje się na stronie podręcznika. Nie uczmy się także opcji na pamięć, raczej zawsze będziemy mieć dostęp do stron podręcznika.

5.3.3. Zmiana informacji użytkownika

Zmianie informacji użytkownika służy polecenie usermod, ma ono prawie identyczne opcje jak polecenie useradd. Poza kilkoma wyjątkami.

Na poniższym przykładzie pokazano zmianę katalogu domowego użytkownika user90:

[root@server1 ~]# tail /etc/passwd
...
user90:x:5003:5003::/home/user90:/bin/bash

[root@server1 ~]# usermod -d /usr/home90 -m user90
[root@server1 ~]# tail /etc/passwd
...
user90:x:5003:5003::/usr/home90:/bin/bash

[root@server1 ~]# ls -al /usr/home90
total 12
drwx------.  3 user90 user90  78 Jun 26 16:06 .
drwxr-xr-x. 13 root   root   158 Jun 27 14:31 ..
-rw-r--r--.  1 user90 user90  18 Nov  5  2021 .bash_logout
-rw-r--r--.  1 user90 user90 141 Nov  5  2021 .bash_profile
-rw-r--r--.  1 user90 user90 492 Nov  5  2021 .bashrc
drwxr-xr-x.  4 user90 user90  39 Jun 26 13:48 .mozilla

Pozostałe opcje, które znajdują się na stronie podręcznika tego polecenia możemy używać w ten sam sposób jak w przypadku useradd.

5.3.4. Usuwanie użytkowników

Kiedy użytkownicy przestają być potrzebni, ze względów bezpieczeństwa można, albo ich zablokować albo usunąć. W tym rodziale zajmiemy się wyłącznie usuwaniem. Polecenie do usuwania użytkowników to userdel. Jest ono prostolinijne i niewymaga dodatkowych opcji, wystarczy podać nazwę użytkownika jako argument polecenia i użytkownik zostanie usunięty, jednak jego dane pozostaną w systemie. Użytkownika wraz z danymi możemy usunąć za przy użyciu opcji -r.

[root@server1 ~]# userdel -r user90
[root@server1 ~]# tail /etc/passwd
chrony:x:986:981::/var/lib/chrony:/sbin/nologin
dnsmasq:x:985:980:Dnsmasq DHCP and DNS server:/var/lib/dnsmasq:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
systemd-oom:x:978:978:systemd Userspace OOM Killer:/:/usr/sbin/nologin
user:x:1000:1000:user:/home/user:/bin/bash
user100:x:1001:1001::/home/user100:/bin/bash
user200:x:1002:1002::/home/user200:/bin/bash
user5000:x:5000:7000::/home/user5000:/bin/bash
user1000:x:5001:5001::/home/user1000:/bin/bash
user2000:x:5002:5002::/home/user2000:/bin/bash
[root@server1 ~]# ls -al /usr/home90
ls: cannot access '/usr/home90': No such file or directory

5.3.5. Użytkownicy bez możliwości logowania

Tak jak na początku powiedzieliśmy sobie, że użytkownicy w systemie istnieją, po to aby wyznaczać granice. Takim użytkownikami są najczęściej są użytkownicy systemowi, którzy są związani z róznego rodzaju usługami. Cechą która ich wyróżnia jest fakt, iż nie mają oni możliwości logowania do systemu, inaczej mówiac są użytkownikami nieinteraktywnymi. Użytkownicy tego typu w miejscu powłoki mają wskazany program /sbin/nologin. Ten mały program przy próbie zalogowania zwróci informacje o tym, że to konto jest obecnie niedostępne. Treść tego komunikatu możemy zmieniać, wpisująć nasz komunikat do pliku /etc/nologin.txt. Użytkownikiem bez możliwości logowania może być każdy, kto ma zamiast powłoki ustawiony właśnie ten program. Poniżej znajduje się polecenie, które zmieni użytkownika user2000 w użytkownika nieinteraktywnego.

[root@server1 ~]# usermod -s /sbin/nologin user2000
[root@server1 ~]# tail /etc/passwd
chrony:x:986:981::/var/lib/chrony:/sbin/nologin
dnsmasq:x:985:980:Dnsmasq DHCP and DNS server:/var/lib/dnsmasq:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
systemd-oom:x:978:978:systemd Userspace OOM Killer:/:/usr/sbin/nologin
user:x:1000:1000:user:/home/user:/bin/bash
user100:x:1001:1001::/home/user100:/bin/bash
user200:x:1002:1002::/home/user200:/bin/bash
user5000:x:5000:7000::/home/user5000:/bin/bash
user1000:x:5001:5001::/home/user1000:/bin/bash
user2000:x:5002:5002::/home/user2000:/sbin/nologin

Kiedy spróbuje się na niego zalogować otrzymam wspomniany komunikat.

[root@server1 ~]# su - user2000
This account is currently not available.

Odnośnie egzaminu:
Jeśli w systemie użytkownik, który teoretycznie nie powinien mieć możliwości logowania, zaloguje się do systemu to znaczy, że mamy poważny problem z bezpieczeństwem. Wówczas należy sprawdzić w pliku /etc/passwd czy wszyscy użytkownicy nieinteraktywni mają ustawiony domyślny program na /sbin/nologin.

Ćwiczenie 1: Sprawdzenie prób logowania użytkowników

Jako superużytkownik na maszynie server1. Zapisz przy użyciu polecenia Vim ostaniego użytkownika, któremu udało się zalogować oraz ostatniego którego logowanie nie powiodło się. Zapisz ostatnie uruchomienie systemu, poza obecnym włączniem oraz listę użytkowników, którzy nigdy się nie logowali do systemu.

Ćwiczenie 2: Weryfikacja użytkownika oraz grupy

Jako superużytkownik na maszynie server1, uruchom polecenie who oraz w porównaj wyniki ich działania. Uruchom polecnia id oraz groups następnie omów informacje zwracane przez nie.

Ćwiczenie 3: Tworzenie użytkowników

Jak superużytkownik na maszynie server1, utwórz użytkownika o nazwie user4100, o identyfikatorze 4100, katalogu domowym znajdującym się katalogu /usr. Następnie utworz użytkownika user4200 z domyślnymi ustawieniami. Przypisz obu hasło. Następnie wyświetl dotyczące ich wpisy w plikach autentykacji (/etc/{passwd,shaddow,group,gshadow}).

Ćwiczenie 4: Tworzenie użytkowników nieinteraktywnych

Jako superużytkownik na maszynie server1, utwórz użytkownika user4300, następnie zmień informacje o nim tak, aby stał sie użytkownikiem nieinterkatywnym. Ustaw mu hasło i spróbuj się zalogować na niego. Jaki komunikat został wyświetlony? Wyświetl wpis użytkownika w pliku /etc/passwd. Co spowodowało, że użytkownik nie może się zalgować?

Podsumowanie

W tym rozdziale dowiedzieliśmy się w jaki sposób możemy sprawdzić próby logowania się do systemu, kto jest zalgowany i co robi. Poznaliśmy zestaw plików odpowidzialnych za autentykacje, a także nauczyliśmy w podstawowym stopniu zarządzać użytkownikami. W następnym rozdziale skupimy się na zasadach haseł i przełączaniu użytkowników.

6. Zaawansowane zarządzanie użytkownikami

W poprzednim rozdziale nauczyliśmy się tworzyć, usuwać użytkowników oraz edytować informacje o nich. Poznaliśmy plik uwierzytelniania, którym między innymi jest plik /etc/shadow, w nim przechowywane są przesłonione hasła, zasady ważności haseł oraz kont i tym zajmiemy sie na początku.

6.1. Zasady ważności hasła

Każdy administrator w systemie, który wykorzystuje intensywnie użytkowników, powinien wdrożyć zasady bezpieczeństwa z nimi związane. Może wymusić na osobach korzystających z kont ich zabezpieczenie po przez regularną zmianę hasła, zablokować nieużywane konta czy utworzyć dla użytkowników tylko konta tymczasowe, które po upływie określonego czasu zostaną automatycznie zablokowane. Te zasady mieszczą w się w dziedzinie ustawień ważności hasła i są realizowane w dystrybucjach Linuksa za pomocą dwóch poleceń: chage oraz passwd. Poniżej w tabeli skumulowałem opcje tych dwóch poleceń odpowiadające za konkrentą czynność.

chage passwd
Data ostatniej modyfikacji hasła -d X
Data wygaśnięcia konta -E X
Czas nieaktywności hasła -I -i
Informacje o zasadach użytkownika -l -S
Minimalna ważność hasła -m -n
Maksymalna ważność hasła -M -x
Okres ostrzegania -W -w

Po za tymi wspólnymi cechami, polecenie passwd, jest wstanie:

Stosując chage lub passwd nazwę użytkownika podajemy po opcjach i ich wartościach. Na poniższym przykładzie za prezentowałem w jaki sposób możemy ustawić wszystkie pozostałe pola we wpisie w pliku /etc/shadow za pomocą powyższych poleceń.

#chage:
[root@server1 ~]# chage -m 7 -M 28 -W 5 -I 7 -E 2022-12-31 user3000
[root@server1 ~]# chage -l user3000
Last password change					: Jun 28, 2022
Password expires					: Jul 26, 2022
Password inactive					: Aug 02, 2022
Account expires						: Dec 31, 2022
Minimum number of days between password change		: 7
Maximum number of days between password change		: 28
Number of days of warning before password expires	: 5
[root@server1 ~]# grep 'user3000' /etc/shadow
user3000:$6$AjNe9ajpYrZ1Sjld$NN36zAMKPlnJElCvuUX1UM0fc6Ymc5pLY4medoHRMCWJhnsoiuKSpjbo7uwTZVkVlv.swqQJ5Dxoa2QZyjsXO.:19171:7:28:5:7:19357:

#passwd:
[root@server1 ~]# passwd -n 5 -x 28 -w 5 -i 7 user4000
Adjusting aging data for user user4000.
passwd: Success
[root@server1 ~]# passwd -S user4000 
user4000 PS 2022-06-28 5 28 5 7 (Password set, SHA512 crypt.)
[root@server1 ~]# grep 'user4000' /etc/shadow
user4000:$6$29yZ7yqj2lWdSoQE$AMp2ZXAqxa.wWMA88dINDdogjych/Yshnip1whwaqymLTS4ALW9UGx9406tZmsghOTXptBIFrq4UvDF/.sl2f0:19171:5:28:5:7::

Za pomocą polecenia passwd, nie możemy dodać daty wygaśnięcia konta. Jednak to polecenie wraz z poleceniem, które znamy z poprzednie rozdziału usermod, może blokować i odblokowywać konta. Zablokujemy i odblokumjemy oba wcześniej wykorzystwane konta.

Blokowanie kont za pomocą polecenia passwd:

[root@server1 ~]# passwd -l user3000
Locking password for user user3000.
passwd: Success
[root@server1 ~]# grep 'user3000' /etc/shadow
user3000:!!$6$AjNe9ajpYrZ1Sjld$NN36zAMKPlnJElCvuUX1UM0fc6Ymc5pLY4medoHRMCWJhnsoiuKSpjbo7uwTZVkVlv.swqQJ5Dxoa2QZyjsXO.:19171:7:28:5:7:19357:
[root@server1 ~]# passwd -u user3000
Unlocking password for user user3000.
passwd: Success
[root@server1 ~]# grep 'user3000' /etc/shadow
user3000:$6$AjNe9ajpYrZ1Sjld$NN36zAMKPlnJElCvuUX1UM0fc6Ymc5pLY4medoHRMCWJhnsoiuKSpjbo7uwTZVkVlv.swqQJ5Dxoa2QZyjsXO.:19171:7:28:5:7:19357:

Jak możemy zauważyć po zablokowaniu konta przed przesłonionym hasłem zostały umieszczone dwa wykrzykniki, które po odblokowaniu konta zostały usunięte. Poniżej znajdują się te same czynności wykonane przy użyciu polecenia usermod.

[root@server1 ~]# usermod -L user4000
[root@server1 ~]# grep 'user4000' /etc/shadow
user4000:!$6$29yZ7yqj2lWdSoQE$AMp2ZXAqxa.wWMA88dINDdogjych/Yshnip1whwaqymLTS4ALW9UGx9406tZmsghOTXptBIFrq4UvDF/.sl2f0:19171:5:28:5:7::
[root@server1 ~]# usermod -U user4000
[root@server1 ~]# grep 'user4000' /etc/shadow
user4000:$6$29yZ7yqj2lWdSoQE$AMp2ZXAqxa.wWMA88dINDdogjych/Yshnip1whwaqymLTS4ALW9UGx9406tZmsghOTXptBIFrq4UvDF/.sl2f0:19171:5:28:5:7::

W przypadku polecenia usermod, umieszczony został tylko jeden wykrzyknik przed hasłem, jednak nie ważne czy został umieszczony jeden czy dwa, wykrzyknik przed przesłonionym hasłem oznacza, że konto jest zablokowane.

6.2. Zarządzanie grupami

Podobnie tak jak w przypadku użytkowników, w poprzednim rozdziale tak i grupami możemy zarządać w podobny sposób. Możemy je dodawać z dostosowanymi do potrzeb informacjami, zmieniać ich informacje oraz usuwać.

6.2.1. Tworzenie nowych grup

Do tworzenia nowych grup służy polecenie groupadd, które do utworzenia grupy z domyślnymi wartościami wymaga jedynie jej nazwy. Poniżej znajdują się najważniejsze opcje tego polecenia:

[root@server1 ~]# groupadd -g 9999 sgrp
[root@server1 ~]# grep 'sgrp' /etc/group
sgrp:x:9999:

Na powyższym przykładzie utworzyłem grupę z niestandardowym identyfikatorem, następnie wyświetliłem jej wpis w pliku /etc/group.

6.2.2. Zmiana informacji grupy

Polecenie groupmod, posiada takie samo zastosowanie dla grup, jak polecenie usermod dla użytkowników. Część opcji groupmod odpowiada opcjom z polecenia groupadd. Na wyszczególnienie zasługuje opcja -n, która pozwala na zmianę nazwy grupy.

[root@server1 ~]# groupmod -g 8888 -n sysadm sgrp
[root@server1 ~]# grep 'sgrp' /etc/group
[root@server1 ~]# grep 'sysadm' /etc/group
sysadm:x:8888:

Jak możemy zobaczyć na przykładzie grupie sgrp został zmieniony identyfikator oraz nazwa. Teraz ta grupa widnieje pod GID-em 8888 oraz pod nazwą sysadm.

6.2.3. Usuwanie grup

Jeśli grupa nie jest już potrzebna w systemie możemy ją usunąć. Do usuwania grup służy polecenie groupdel. Polecenie jest bardzo proste w użyciu. Jedyny wymagany argument to nazwa grupy.

[root@server1 ~]# groupdel sysadm
[root@server1 ~]# grep 'sysadm' /etc/group

Grupa została usunięta. Polecenie grep nie mogło odnaleźć wzorca sysadm w pliku /etc/group.

6.3. Przełączanie użytkowników oraz uruchamianie poleceń jako superużytkownik

Wykonywanie zadań administracyjnych najczęściej będzie wymagać podniesienia uprawnień do poziomu superużytkownika - root. Do tej pory wszystkie polecenia w tym materiale wykonywałem z poziomu konta root (wyjątkowowo kilka poleceń zostało wykonanych jako zwykły użytkownik), więc aby wykonywać zadania administracyjne należy się zalogować jak superużytkownik. Logowanie bezpośrednie na to konto nie jest dobrą praktyką. A do dyspozycji mamy dwa rozwiązania. Pierwszym z nich jest przełączenie użytkownika.

6.3.1. Przełącznie użytkownika

Pierwszą z metod jest zalogowanie się na nasze konto (konto dostępowe, zwykły użytkownik), a następnie przełącznie się na konto root za pomocą polecenia su. To polecenie pozwala na przełączenie się na dowolne konto, jeśli jako pierwszy argument podamy myślnik (-) przed nazwą użytkownika podczas przełączania zostaną uruchomione skrypty startowe, które stworzą nam środowisko jak najbardziej zbliżone do bezpośredniego logowania.

[user@server1 ~]$ su - root
Password: 
[root@server1 ~]# 

Za pomocą tego polecenia możemy przełączyć się na dowolne konto, jeśli zwykli użytkownicy przełączają się między kontami wymagane będzie podanie hasła dla docelowego konta, wyjątkiem tutaj jest superużytkownik, który nie musi podawać żadnych haseł.

Aby korzystać z tej metody nie trzeba nawet otwierać sesji powłoki jako przełączony użytkownik, możemy po prostu wykonać polecenie jako on za pomocą opcji -c.

[user@server1 ~]$ su root -c 'vgs'
Password: 
  VG   #PV #LV #SN Attr   VSize   VFree
  rhel   1   2   0 wz--n- <19.00g    0 

Korzystanie z polecenia su, nie jest zbyt elastycznym rozwiązaniem, daje pełen dostęp do superużytkownika gdzie nie jest to konieczne. Ten rodzaj podniesienia uprawnień w systemie używany jest przez kilka dystrybucji w jednym celu aby pobrać i skonfigurować to drugie rozwiązanie.

6.3.2. Polecenie sudo

Użycie polecenia sudo jest znacznie lepszym rozwiązaniem. Głównym argumentem jest fakt, iż możemy skonfigurować to polecenie w taki sposób, aby dać użytkownikom dostęp tylko do tych poleceń których naprawdę potrzebują. Jednak na początku zajmiemy się prostszą konfiguracją, czyli stworzeniem lokalnego administratora, którego konto dalej będzie zwykłym użytkownikiem.

Do konfiguracji tego narzędzia służy plik /etc/sudoers oraz katalog /etc/sudoers.d, w którym wystarczy umieścić plik z konfiguracją. Plik /etc/sudoers, możemy edytować dowolnym edytorem, jednak pakiet sudo posiada specjalne polecenie visudo, które stworzy kopie tymczasową pliku, na której będziemy dokonywać zmian (polecenie samoczynnie uruchomi edytor), następnie polecenie nadpisze oryginalny plik i usunie plik tymczasowy. Najprostszą konfiguracją jaką możemy wykonać jest nadanie zwykłemu użytkownikowi uprawnień do wszystkich funkcji administracyjnych. Zmian możemy dokonać w dowolnym miejscu w pliku, jednak nalepiej znaleźć odpowiednie do tego miejsce. Możemy je namierzyć wyszukując linię root ALL=(ALL) ALL, skopiować ją, wkleić pod nią i zmienić tylko nazwę użytkownika na początku linii:

user ALL=(ALL)  ALL

Ten wpis zapewni użytkownikowi user dostęp do wszystkich funkcji administracyjnych w systemie. Oprócz użytkownikom, dostęp do wszystkich lub określonych funkcji możemy nadać grupie. Kiedy zajdzie taka potrzeba wystarczy dopisać użytkownika do grupy i nie trzeba już edytować pliku. Konfiguracja grupy różni się od konfiguracji użytkownika tym, że nazwę grupy poprzedza się znakiem procentu (%):

%sysadms ALL=(ALL)  ALL

We wpisach konfiguracyjnych znajdują się aż trzy słowa ALL, każde z nich ma oddzielne znacznie. Pierwsze słowo ALL po nazwie użytkownika oznacza nazwę komputera (lub adres IP), ponieważ plik /etc/sudoers może być współdzielony między innymi komputerami, jednak ten plik będzię dotyczyć tylko tego systemu dlatego też może pozostać ALL. Drugie słowo ALL w nawiasie oznacza jako kto będzie wykonywać te polecenie, ta lista pozwala wybrać użytkownika (po zastosowaniu opcji -u) do wykonania polecenia. W nie których systemach możemy spotkać się z zapisem (ALL:ALL), zapis ALL po dwukropku oznacza listę grup. Grupę możemy wybrać z pomocą opcji -g. Ostanie ALL jest listą programów do których użytkownik poprzedzający polecenie narzędziem sudo będzie mieć dostęp. Poniżej przykładowe użycie polecenia sudo.

[user@server1 ~]$ sudo vgs
[sudo] password for user: 
  VG   #PV #LV #SN Attr   VSize   VFree
  rhel   1   2   0 wz--n- <19.00g    0

Podczas uruchamia programu vgs, sudo, po prosiło o hasło - hasło użytkownika, który wydaje to polecenie. Narzędzie sudo możemy skonfigurować w taki sposób aby nie wymagało hasła podczas uruchamiania programu. We wcześniejszej dyrektywie, konfigurującej użytkownika przed ostatnim słowem ALL dopisujemy NOPASSWD:.

user ALL=(ALL)  NOPASSWD:ALL

Najczęściej naszym zadaniem związanym z poleceniem sudo będzie przypisanie konkretnym użytkownikom dostępu do określonych poleceń. Jeśli do skonfigrowania mamy wiele takich elementów za równo poleceń jak i użytkowników, to aby zrobić to jak najefektywniej należy użyć aliasów. Sudo swojej konfiguracji wyróżnia aliasy przeznaczone dla użytkowników jak i dla poleceń. Alias z listą użytkowników tworzymy tak jak na przykładzie:

User_Alias  PKGADM = user1, user100, user200

Po słowie User_Alias zapisujemy nazwę aliasu, w tym przypadku jest PKG_ADM po znaku równości (=) podajemy listę użytkowników tak jak na przykładzie powyżej. Ze zbiorem poleceń jest podobnie ale zmienia się dyrektywa inicjująca alias.

Cmnd_Alias PKGCMD = /usr/bin/yum, /usr/bin/rpm

Po zdefiniowaniu obu aliasów, możemy przejść do przypisnia aliasowi PKGADM (określonym użytkownikom) dostępu do poleceń zapisanych w PKGCMD.

PKGADM ALL = PKGCMD

Teraz użytkownicy zdefiniowani w aliasie PKGADM będą mogli użyć tylko i wyłącznie poleceń zdefiniowanych w PKGCMD

Podczas pracy będziemy zazwyczaj definiować aliasy użytkowników, ponieważ jeśli rozejrzymy się po pliku /etc/sudoers, definicje aliasów z pogrupowanymi poleceniami są już zapisane w komentarzach wystarczy usunąć znak komentarza oraz przypisać ten alias określonej grupie użytkowników.

Użycie polecenia sudo jest rejestrowane. Wpisy zapisywane są do pliku /var/log/secure ich odczyt wymaga uprawnień superużytkownika. We wpisach znajduje się nazwa użytkownika, który wywołał to polecenie jak i samo polecenie.

Więcej informacji na temat tego narzędzia znajduje się na stronie podręcznika samego polecenia jak i pliku /etc/sudoers. Jak ćwiczenie, nadajmy uprawnienia do pełnych funkcji administracyjnych zwykłemu użytkownikowi, którego tworzyliśmy podczas instalacji systemu na maszynie. Od tego momentu na wszystkich przykładach będę używać tego konta, a uprawnienia administracyjne będę wymuszać za pomocą polecenia sudo.

6.4. Zmiana praw własności plików i katalogów

Polecenie chown, omówiliśmy sobie podczas omawiania uprawnień plików. Polecenie to służy do zmiany właściciela oraz ewentualnie grupy, do której należy element.

[user@server1 ~]$ ls -l file1 
-rw-r--r--. 1 user user 0 Jun 26 15:30 file1
[user@server1 ~]$ sudo chown user100 file1
[sudo] password for user: 
[user@server1 ~]$ ls -l file1
-rw-r--r--. 1 user100 user 0 Jun 26 15:30 file1

Jednak w systemie istnieje inne polecenie, dzięki któremu możemy zmienić grupę. Jest nim chgrp. Składania polecenia identyczna do składni chown.

[user@server1 ~]$ sudo chgrp user100 file1
[sudo] password for user: 
[user@server1 ~]$ ls -l file1 
-rw-r--r--. 1 user100 user100 0 Jun 26 15:30 file1
[user@server1 ~]$ 

Jak możemy zauważyć grupa, do której należy plik została zmieniona w porównaniu do szczegółów z poprzedniego przykładu.

Polecenia chown możemy również używać rekurencyjnie, aby zmieniać prawa własności całych struktur katalogowych wraz z plikami. Tej czynności służy opcja -R.

[user@server1 ~]$ sudo chown -R root:root /tmp/etc

Na powyższym przykładzie zmieniłem właściciela oraz grupę do wszystkim elementom w katalogu /tmp/etc.

Ćwiczenie 1: Utwórz użytkowników oraz skonfiguruj zasady haseł

Na maszynie server1 utwórz grupę lnxgrp o numerze GID 6000. Utwórz użytkownika user5000 o numerze UID 5000 oraz numerze GID 6000. Przypisz mu hasło, a następnie ustaw minimum 4 dni do następnej zmiany hasła, ważność hasła na 30 dni oraz okres ostrzegania na 10 dni. Konto tego użytkownika powinno wygasnąć 20 grudnia 2022 roku.

Ćwiczenie 2: Zablokuj i odblokuj użytkownika

Na maszynie server1 zablokuj użytkownika user5000 za pomocą polecenia passwd. Sprawdź jak zmienił się jego wpis w pliku /etc/shadow. Spróbuj się zalogować na użytkownika. Odblokuj go za pomocą polecenia usermod zweryfikuj jego odblokowanie na podstawie wpisu z pliku powyżej.

Ćwiczenie 3: Zmień dane grupy

Na maszynie server1 zmień GID grupy lnxgrp z 6000 na 7000, dodaj do niej użytkowników user1000 oraz user2000 (jeśli nie istnieją utwórz ich), na koniec zmień nazwę grupy na dbagrp i zweryfikuj zmiany.

Ćwiczenie 4: Konfiguracja dostępu do sudo

Na maszynie server1, skonfiguruj polecenie sudo dla użytkownika user5000, tak aby miał dostęp do wszystkich funkcji administracyjnych bez podawania hasła. W celu sprawdzenia wydaj polecenie sudo vgs.

Ćwiczenie 5: Zmiana praw własności do plików i katalogów

Na maszynie server1, utwórz w katalogu /tmp plik f6 oraz katalog d6. Utwórz użytkownika user90. Za pomocą polecenia chown ustaw właściciela pliku f6 na user90. Za pomocą polecenia chgrp ustaw grupę na dbagrp. Utwórz grupę g1, następnie ustaw rekrencyjnie prawa własności dla katalogu d6, na user90:g1.

Podsumowanie

Tym rodziałem zakończyliśmy zarządzanie użytkownikami. Zapoznaliśmy się z narzędziami do ustalania zasad haseł w praktyce, poznaliśmy narzędzia do zarządzania grupami oraz efektywne sposoby na bezpieczne podnoszenie uprawnień za pomocą polecenia sudo. Na koniec odświerzyliśmy informacje na temat praw własności plików i katalogów. W następnym rozdziale poznamy podstawy powłoki BASH.

7. Powłoka BASH

Powłoka jest to program odpowiedzialny za możliwość wprowadzania poleceń do systemu i ich interpretowanie. Powłoka jest domyślnym interfejsem użytkownika w różnych wariantach systemów uniksopodobnych. To właśnie proces połoki jest uruchamiany po uruchomieniu okna terminala.

Na przestrzeni lat wraz z kolenymi odłamami oryginalnego Uniksa powstawało wiele powłok, każda z nich miała swoje wady i zalety. Obecnie najpowszechniejszą wersją z nich jest BASH, jej główną zaletą jest zebranie większości funkcjonalność różnych powłok, poprawienie ich działa oraz dostosowanie do obecnych czasów. Ten rodzaj powłoki również domyślnie jest instalowany w wielu dystrybucjach Linuksa i Red Hat nie jest w tym temacie wyjątkiem. W tym rodziale chciałbym omówić najważnejsze funkcje tej powłoki oraz jej skrypty startowe, dzięki którym będziemy mogli dostować ją do swoich potrzeb.

7.1. Funkcje powłoki BASH

BASH w swojej kolekcji posiada masę funkcjonalności, których próżno szukać w innych powłokach. Dla przykładu w nowszych dystrybucjach, skrypt powłoki zastępują podstawowe narzędzia systemowe, będące kiedyś pełnoprawnymi programami.

7.1.1. Zmienne

Korzystając z powłoki możemy używać zmiennych, czyli opisanych miejsc alokowanych w pamięci do przechowywania dowolnych wartości. Do dyspozycji mamy dwa rodzaje zmiennych - zmienne lokalne oraz zmienne środowiskowe.

Zmienne lokalne są dostępne tylko w obecnej sesji powłoki. Tego typu zmienne nie będą widoczne dla uruchomionych w tej sesji skryptów czy programów, gdyż są przechowywane tylko i wyłącznie w obszarze pamięci należącym do tego procesu powłoki. Taka zmienną możemy zdefiniować podając w wierszu polecenia jej nazwę, następnie znak równości oraz wartość. Warto dodać aby nie było między tymi elementami żadnej spacji. Poniższy przykład na pewno to rozświetli.

[user@server1 ~]$ zmienna=2137

Po wykonaniu takiego polecenia, powłoka utworzy zmienną i nada jej podaną wartość, po czym zwróci znak zachęty.

Do takiej zmiennej możemy odwołać się podczas pisania polecenia wystarczy poprzedzić nazwę znakiem dolara ($).

[user@server1 ~]$ echo $zmienna
2137

Polecenie echo wypisuje wszystko co do zostanie mu podane jako argument na standardowe wyjście (o tym będzie w dalszej częsci materiału). Jak widzimy polecenie wypisało zawartość zmiennej.

Innymi zmiennymi są zmienne środowiskowe przechowują one różnego rodzaju wartość, które mogą wpływać na działanie programów. Każdy program uruchomiony na Linuksie dostaje od procesu nadrzędnego obszar pamięci zmiennych środowiskowych, które może wykorzystywać do sobie tylko znanych celów. Definicja takich zmiennych, różni się tym, że poprzedza się ją poleceniem export, którego zadaniem jest przeniesienie zmiennej lokalnej do pamięci zmiennych środowiskowych.

[user@server1 ~]$ export ZMIENNA=2137

Nazwy zmiennych środowiskowych zapisujemy za pomocą wielkich liter aby odróżnić je od zmiennych lokalnych. Dostęp do takiej zmiennej możemy uzyskać, kiedy uruchomimy jakiś program w tej powłoce, aby otrzymał on od powłoki, w której został uruchomiony (procesu nadrzędnego) obszar zmiennych środowiskowych. Najlepiej by było aby takim programem była kolejna powłoka (podpowłoka).

[user@server1 ~]$ bash
[user@server1 ~]$ echo $ZMIENNA
2137

Po uruchomieniu podpowłoki wydałem polecenie, które wyświetliło mi zawartość zmiennej, którą zdefiniowałem jako zmienną środowiskową w poprzednim przykładzie.

Wszystkie zmienne środowiskowe w systemie możemy wypisać za pomocą polecenia env lub polecenia printenv. Oto fragment listingu zmiennych środowiskowych z mojego systemu:

[user@server1 ~]$ env
...
TERM=xterm-256color
LESSOPEN=||/usr/bin/lesspipe.sh %s
USER=user
SELINUX_USE_CURRENT_RANGE=
ZMIENNA=2137
SHLVL=2
XDG_SESSION_ID=7
XDG_RUNTIME_DIR=/run/user/1000
SSH_CLIENT=192.168.122.1 34956 22
which_declare=declare -f
...

Natomiast za pomocą polecenia set możemy wypisać wszystkie zmienne dostępne w systemie, łącznie z lokalnymi zmiennymi powłoki.

[user@server1 ~]$ set
...
SHLVL=1
SSH_TTY=/dev/pts/0
TERM=xterm-256color
UID=1000
USER=user
XDG_DATA_DIRS=/home/user/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
XDG_RUNTIME_DIR=/run/user/1000
XDG_SESSION_CLASS=user
XDG_SESSION_ID=7
XDG_SESSION_TYPE=tty
ZMIENNA=2137
_=set
_backup_glob='@(#*#|*@(~|.@(bak|orig|rej|swp|dpkg*|rpm@(orig|new|save))))'
colors=/home/user/.dircolors
new_dirs=/home/user/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share
which_opt=-f
which_shell=bash
zmienna=2137
zmienna1=2137
...

Zmienne, które nie są już potrzebne możemy usunąć za pomocą polecenia unset.

[user@server1 ~]$ set | grep 'zmienna1'
zmienna1=2137
[user@server1 ~]$ unset zmienna1
[user@server1 ~]$ echo $zmienna1

Przy odwołaniu się do zmiennej, polecenie echo zwróciło pusty wiersz.

Manipulując zmiennymi w powłoce może zmienić znak zachęty, ponieważ jego definicja jest przechowywana w zmiennej PS1:

[user@server1 ~]$ echo $PS1
[\u@\h \W]\$

Te poprzedzone lewym ukośnikiem \ litery są kolejnymi elementami znaku zachęty. Opis tych elementów znajduje się na stronie podręcznika polecenia/powłoki BASH w rozdziale PROMPTING.

7.1.2. Podstawienie polecenia

Często wykorzystywaną funkcją wraz z zmiennymi jest podstawienie polecenia. Polega ono na uruchomieniu, przez podpowłokę polecenia ujętego albo w okrągły nawias poprzedzony znakiem dolara ($()) albo w ukośny cudzysłów (``). Informacja zwracana przez polecenie trafia albo do zmiennej albo bezpośrednio jako argument polecenia.

[user@server1 ~]$ export KERNEL_VERSION=$(uname -r)
[user@server1 ~]$ echo $KERNEL_VERSION 
5.14.0-70.13.1.el9_0.x86_64

Poniżej znajduje się przykład użycia podstawienia polecenia w innym poleceniu:

[user@server1 ~]$ sudo find / -name *`uname -r` -print 2> /dev/null
/boot/vmlinuz-5.14.0-70.13.1.el9_0.x86_64
/boot/System.map-5.14.0-70.13.1.el9_0.x86_64
/boot/config-5.14.0-70.13.1.el9_0.x86_64
/usr/lib/firmware/5.14.0-70.13.1.el9_0.x86_64
/usr/lib/modules/5.14.0-70.13.1.el9_0.x86_64
/usr/share/doc/kernel-keys/5.14.0-70.13.1.el9_0.x86_64

Gwiazdka (*) w tym przypadku oraz 2>, również są elementami połoki, które sobie omówimy.

7.1.3. Przekierowanie strumieni

Na Uniksach mamy doczynienia z trzema rodzajami strumieni danych, owe strumienie łączą ze sobą np. urządzenia z programami, dlatego pisząc na klawiaturze możemy wprowadzać tekst do programu, albo programy mogą wypisać wynik swojego działnia do okna terminala. Pierszy strumień to strumień standardowegp wejścia źródłem jego danych jest najczęściej klawiatura. Jego identyfikatorem jest 0 (programy używają identyfikatorów do korzystania ze strumieni, my też będziemy podłaczając jeden strumień do drugiego). Drugi strumień danych, to strumień standarowego wyjścia źródłem jego danych jest najczęściej program (jądro podczas uruchamiania programu przekazuje mu kopie strumieni), jego identyfikatorem jest 1, a podłączony jest on do terminala. Ostatnim strumieniem jest strumień błędów jego źródłem danych są komunikaty diagnostyczne programów, jest on najczęciej podłączony razem ze strumieniem wyjścia do terminala. Identyfikatorem strumienia błędów jest 2.

Przekierowania strumienia standardowego wyjścia dokonujemy w wierszu polecenia poprzez podanie znaku większości (>) a następnie podajemy miejse docelowe na dane. Na przykład wynik listowania katalogu /etc możemy umieścić w pliku.

[user@server1 ~]$ ls -l /etc > ls.out
[user@server1 ~]$ head -5 ls.out 
total 1320
drwxr-xr-x.  3 root root        28 Jun 26 13:50 accountsservice
-rw-r--r--.  1 root root        16 Jun 26 14:01 adjtime
-rw-r--r--.  1 root root      1529 Jun 23  2020 aliases
drwxr-xr-x.  3 root root        65 Jun 26 13:54 alsa

Użycie pojedynczego znaku większości przekieruje strumień w tym przypadku do pliku oraz nadpisze informacje zawarte w nim. Jeśli plik nie istnieje to zostanie on utworzony.

Nadpisywanie danych za pomocą przekierowania strumieni nazywane jest clobberingiem. Możemy zablokować tę funkcję za pomocą polecenia set -o noclobber, wówczas gdy plik będzie istnieć pokaże nam się taki komunikat jak na poniższym przykładzie.

[user@server1 ~]$ ls -l /etc > ls.out
-bash: ls.out: cannot overwrite existing file

Funkcję można włączyć z powrotem za pomocą polecenia set +o noclobber.

Chcąc dopisać do zawartości pliku, zawartość strumienia możemy użyć podwójnego znaku większości (>>). Na przykład:

[user@server1 ~]$ ls -l /var >> ls.out
[user@server1 ~]$ cat ls.out
total 4
drwxr-xr-x. 2 user4000 g1         6 Jun 26 16:06 d6
srwxrwxrwx. 1 gdm      gdm        0 Jun 30 13:59 dbus-aIxoyUxXnM
srwxrwxrwx. 1 gdm      gdm        0 Jun 30 09:38 dbus-JRmt27fCi0
drwxr-xr-x. 2 user200  dba        6 Jun 26 15:33 dir10
-rw-r--r--. 1 user4000 dba        0 Jun 26 16:06 f6
-rw-r--r--. 1 user200  user100    0 Jun 26 15:33 file10
-rw-r--r--. 1 user     user    2400 Jun 30 14:57 ioerror
...
total 16
drwxr-xr-x.  2 root root   19 Jun 26 13:54 account
drwxr-xr-x.  2 root root    6 Aug 10  2021 adm
drwxr-xr-x. 18 root root 4096 Jun 26 14:12 cache
drwxr-xr-x.  2 root root    6 Mar  7 04:37 crash
drwxr-xr-x.  3 root root   18 Jun 26 13:50 db
drwxr-xr-x.  2 root root    6 Aug 10  2021 empty
...

Z racji tego, iż znak większości służy do przekierowania jedynie wyjścia to aby przekierować strumień błedów, należy podać przez znakiem jego identyfikator.

[user@server1 ~]$ ls -l /dvd 2> err.out
[user@server1 ~]$ cat err.out 
ls: cannot access '/dvd': No such file or directory

Czasami może się zdarzyć, iż będzie potrzeba przekierowania obu tych strumieni, wówczas trzeba skorzystać z podłączenia jednego strumienia do drugiego.

[user@server1 ~]$ ls -l /tmp /sdir > ls2.out 2>&1
[user@server1 ~]$ cat ls2.out 
ls: cannot access '/sdir': No such file or directory
/tmp:
total 4
drwxr-xr-x. 2 user4000 g1         6 Jun 26 16:06 d6
srwxrwxrwx. 1 gdm      gdm        0 Jun 30 13:59 dbus-aIxoyUxXnM
srwxrwxrwx. 1 gdm      gdm        0 Jun 30 09:38 dbus-JRmt27fCi0
drwxr-xr-x. 2 user200  dba        6 Jun 26 15:33 dir10
-rw-r--r--. 1 user4000 dba        0 Jun 26 16:06 f6
-rw-r--r--. 1 user200  user100    0 Jun 26 15:33 file10
-rw-r--r--. 1 user     user    2400 Jun 30 14:57 ioerror
...

To polecenie można zapisać również w inny sposób. Na przykład: ls -l /tmp /sdir &> ls2.out. Obie formy są poprawne i obie robią to samo, która będzie dla nas lepsza musimy zdecydować sami.

Odnośnie egzaminu:
Jednym z zadań egzaminacyjnych może być wykonanie polecenia z przekierowaniem wyjścia lub/i strumienia błędów do pliku.

Kolejnym rodzajem przekierowania jest przekierowanie wejścia, jednak jest ono dość rzadko stosowane. Do tego rodzaju przekierowania stosuje się znak mniejszości (<). Jednym z przykładów jest polecenie użycie polecenia tar, jednak nie ma ono sensu, ponieważ istnieje opcja -f, która pozwala na podanie ścieżki do pliku.

[user@server1 ~]$ tar -x -C /tmp < etc.tar 
[user@server1 ~]$ ls -l /tmp
total 16
drwxr-xr-x.   2 user4000 g1         6 Jun 26 16:06 d6
srwxrwxrwx.   1 gdm      gdm        0 Jun 30 13:59 dbus-aIxoyUxXnM
srwxrwxrwx.   1 gdm      gdm        0 Jun 30 09:38 dbus-JRmt27fCi0
drwxr-xr-x.   2 user200  dba        6 Jun 26 15:33 dir10
drwxr-xr-x. 132 user     user    8192 Jun 30 09:38 etc
...

Ostatni rodzaj przekierowania, to potok, o potoku jeszcze będzie w tym rozdziale.

7.1.4. Historia poleceń

Dość istotną funkcją powłoki BASH jest historia poleceń. Pozwala ona zaoszczędzić pisania w szczególności długich poleceń czy poleceń potokowych (ang. pipelines). Posiada ona funkcję wyszukiwania oraz swobodny dostęp do poleceń umieszczonych w historii.

Aby wyświetlić polecenia z historii, należy użyć polecenia history, wyświetli ono listę poleceń z numerami na początku, każde polecenie to jedna linia. Numer porządkowy pomoże nam się odwołać do polecenia podczas jego wywoływania, ponieważ nie potrzeba szukać polecenia czy przebierać ich wszystkich za pomocą niżej opisanych klawiszy. Wystarczy znać jego numer. Poniżej znajduje się fragment listy historii poleceń z mojego systemu:

[user@server1 ~]$ history
    1  id
    2  groups 
    3  su
    4  su -
    5  cat /etc/sudoers
    6  su - root
    7  sudo cat /etc/sudoers
    8  su - root
    9  sudo cat /etc/sudoers
   10  su - root
   11  sudo yum update
   12  su - root
   13  touch file1
...

Wyświetlenie listy poleceń w historii możemy ograniczyć podajac w jako argument ilość wyświetlanych linii. Polecenie wyświetli wówczas polecenia od końca listy (ostatnio dodane).

[user@server1 ~]$ history 10
  266  rm etc.tar 
  267  sudo tar -cvf etc.tar /etc
  268  tar -x -C /tmp < etc.tar 
  269  ls -al /tmp
  270  ls -l /tmp
  271  history
  272  touch file1
  273  sudo poweroff
  274  touch file1
  275  history 10

Za pomocą opcji -d możemy usunać wybrane polecenie z historii.

[user@server1 ~]$ history 10
  266  rm etc.tar 
  267  sudo tar -cvf etc.tar /etc
  268  tar -x -C /tmp < etc.tar 
  269  ls -al /tmp
  270  ls -l /tmp
  271  history
  272  touch file1
  273  sudo poweroff
  274  touch file1
  275  history 10
[user@server1 ~]$ history -d 271
[user@server1 ~]$ history 10
  267  sudo tar -cvf etc.tar /etc
  268  tar -x -C /tmp < etc.tar 
  269  ls -al /tmp
  270  ls -l /tmp
  271  touch file1
  272  sudo poweroff
  273  touch file1
  274  history 10
  275  history -d 271
  276  history 10

Na informacji zwracanej przez wcześniejsze wywołanie polecenia history 10 w linii 271 widnieje polecenie history, natomiast na drugim wywołaniu po usunięciu tego polecenia pod tym numerem widnieje już inne.

Wśród poleceń w historii możemy poruszać się za pomocą strzełek w górę oraz w dół lub kombinacji klawiszy (co jest bardziej zalecene) Ctrl+p (poprzednie polecenie) oraz Ctrl+n (następne polecenie). Możemy je również wyszukiwać po przez wciśnięcie kombinacji klawiszy Ctrl+r i podanie fragmentu polecenia. Czasmi wyszukiwanie może nie przynieść skutku, wówczas należy nacisnać Ctrl+c i spróbować ponownie.

Do poleceń w historii odnosimy się wprowadzając w wierszu polecenia wykrzyknik (!) następnie możemy podać numer z listy. Dla przykładu użyje polecenia numer 13 z fragmentu listy z pierwszego przykładu.

[user@server1 ~]$ !13
touch file1
touch: cannot touch 'file1': Permission denied

Możemy również podać litery rozpoczynające polecenie na przykład:

[user@server1 ~]$ !tou
touch file1
touch: cannot touch 'file1': Permission denied

Ta instrukcja podstawi najczęstsze wystąpienie polecenia rozpoczynającego się o tych liter. Podobną metodą jest podanie fragmentu polecenia zawierającego jakiś ciąg znaków, na przykład:

[user@server1 ~]$ !?file?
touch file1
touch: cannot touch 'file1': Permission denied

To polecenie podstawi najczęściej występujące polecenie z historii zawierające ciąg znaków file.

Za pomocą polecenia wykrzyknika możemy odwołać się do ostatniego polecenia podając drugi wykrzyknik. Tą metodę możemy zastosować w przypadku gdy zapomnimy o sudo przy wydawaniu poleceń administracyjnych.

[user@server1 ~]$ cat /var/log/secure
cat: /var/log/secure: Permission denied
[user@server1 ~]$ sudo !!
sudo cat /var/log/secure
[sudo] password for user: 
Jun 26 14:12:08 server1 polkitd[725]: Loading rules from directory /etc/polkit-1/rules.d
Jun 26 14:12:08 server1 polkitd[725]: Loading rules from directory /usr/share/polkit-1/rules.d
Jun 26 14:12:08 server1 polkitd[725]: Finished loading, compiling and executing 12 rules
Jun 26 14:12:08 server1 polkitd[725]: Acquired the name org.freedesktop.PolicyKit1 on the system bus
...

Historię możemy wyłączyć używając polecenia set -o history. Włączenie historii wymaga polecenia set +o history. Historia kontrolowana jest za pomocą trzech zmiennych HISTSIZE, której wartość jest równoznaczna z ze zmienną HISTFILESIZE oraz HISTCONTROL. Ich opisy znajdują się na stronie podręcznika polecenia bash. Natomiast ścieżka do pliku zawierającego historie z poprzednich sesji znajduje się w zmiennej HISTFILE.

7.1.5. Edycja wiersza polecenia

Za pomocą strzałek w lewo oraz w prawo, możemy przesuwać kursor w wierszu polecenia, jednak istnieją znacznie bardziej efektywne sposoby na edycje wpisanego polecenia.

Te skróty pozwalają na sprawniejsze poruszenie się w wierszu polecenia.

7.1.6. Uzupełnianie poleceń

Chcąc przyspieszyć pisanie poleceń możemy wykorzystać uzupełnianie poleceń. W wierszu polecenia zapisujemy fragment polecenia lub ścieżki następnie naciskamy klawisz Tab. Powłoka uzupełni nasz zapis do wspólnej części z na przykład nazwami plików, katalogów czy poleceń. Po następnym naciśnięciu tego klawisza, powłoka zwróci możliwe warianty pasujące do tego co zapisaliśmy.

[user@server1 ~]$ deb

#Pierwsze naciśnięcie klawisza Tab
[user@server1 ~]$ debug

#Drugie naciśnięcie klawisza Tab
[user@server1 ~]$ debug
debugfs          debuginfod-find  
[user@server1 ~]$ debug

Kiedy dopiszemy kilka liter z jednego z wariantów, polecenie dopełni je pozostałymi lub zwróci zawężoną ilość możliwych do wyboru elementów.

7.1.7. Podstawienie tyldy

Tylda w powłoce oznacza jedno - katalog domowy użytkownika. Za jej pomocą możemy w prosty sposób odwoływać się do elementów w katalogu domowym użytkownika ale i nie tylko. Poniżej przedstawiłem trzy zastosowania podstawienia tyldy.

Podstawienie tyldy może zaoszczędzić nam czas oraz zmieniejszyć ilość pisania. Poniżej znajduje się kilka przykładów prezentujących powyższe podstawienia.

[user@server1 logs]$ echo ~+
/home/user/logs
[user@server1 logs]$ echo ~
/home/user
[user@server1 logs]$ cd /etc
[user@server1 etc]$ echo ~-
/home/user/logs

7.1.8. Aliasy

Alias to nazwa zastępcza, dzięki której możemy zastąpić długie polecenia za pomocą pojedynczego wyrazu. Aliasy definiowane są pomocą polecenia alias, jako argument przyjmuje on definicję aliasów, czyli nazwę zastępnczą, znak równości oraz polecenie w podwójnych apostrofach. Między tym elementami nie powinno być spacji. Poniżej znajduje się definicja aliasów, który szuka w katalogu domowym plików powyżej 1 GB a następnie zwraca ich atrybutu.

alias bigfiles="find ~ -size +1G -exec ls -lh {} \;"
[user@server1 ~]$ bigfiles 
-rw-r--r--. 1 user user 1.5G Jul  1 12:55 /home/user/test.img

Ważną rzeczą związna z aliasami jest fakt iż kiedy powłoka otrzymuje polecenie do interpretacji to sprawdza na początku czy nie jest ono aliasem i jeśli tak jest to wykonuje to co znajduje się pod nim. Aliasy mogą mieć takie same nazwy jak programy czy inne polecenia w systemie, dlatego też mogą je przesłaniać. Tak jest w przypadku superużytkownika z poleceniami cp, mv czy rm. Poniższy przykład dobrze to obrazuje.

[user@server1 ~]$ alias rm="rm -i"
[user@server1 ~]$ rm test.img
rm: remove regular file 'test.img'? n

Aby powłoka nie brała nazwy polecenia przy poszukiwaniu aliasów, należy poprzedzić ją znakiem lewego ukośnika czy backslashu (\).

[user@server1 ~]$ rm logs
rm: remove symbolic link 'logs'? n
[user@server1 ~]$ \rm logs

Usuwaniu aliasów służy polecenie unalias.

[user@server1 ~]$ unalias bigfiles
[user@server1 ~]$ bigfiles
bash: bigfiles: command not found...

7.1.9. Metaznaki oraz nazwy wieloznaczne

Chcąc odwołać się do wielu elementów posiadających wspólne fragmenty nazw możemy użyć tzw. metaznaków lub nazw wieloznacznych, służą one podstawianiu znaków pod używane wraz z nim nazwy. Załóżmy że mamy dwa rodzaje archiwów w katalogu jedne rozpoczynają się od nazwy backup a drugie od nazwy d kazde archiwum zawiera w nazwie po podanej części myślnik oraz datę. Chcemy wyświetlić atrybuty archiwów z nazwą zaczynającą się od d w tym celu możemy użyć nazwy wieloznacznej. Podstawiając za nazwą gwiazdkę.

[js@fujitsu windows]$ ls -l D*
-rwxr--r-- 1 windows windows 11718211581 05-01 21:09 D-2022-05-01.7z
-rwxr--r-- 1 windows windows 11718211581 05-02 21:08 D-2022-05-02.7z
-rwxr--r-- 1 windows windows 11718211581 05-03 21:08 D-2022-05-03.7z
-rwxr--r-- 1 windows windows 11718269229 05-04 21:15 D-2022-05-04.7z
-rwxr--r-- 1 windows windows 11718658739 05-05 21:14 D-2022-05-05.7z
-rwxr--r-- 1 windows windows 11718634289 05-06 21:14 D-2022-05-06.7z
-rwxr--r-- 1 windows windows 11718634289 05-07 21:08 D-2022-05-07.7z
-rwxr--r-- 1 windows windows 11718634289 05-08 21:08 D-2022-05-08.7z
-rwxr--r-- 1 windows windows 11718633900 05-09 21:15 D-2022-05-09.7z
...

Do stosowania nazw wieloznacznych, możemy stosować wiele znaków ich opis znajduje się na liście poniżej.

Stosując nazwy wieloznaczne możemy automatycznie wykonywać te same polecenia na elementach posiadających wspólne nazwy.

7.1.10. Potoki i polecenia potokowe

Omawiając przkierowania strumieni wspomniałem o potokach. Potok to przekierowanie wyjścia jednego polecenia na wyjście drugiego za pomocą znaku pionowej kreski (|).

[user@server1 ~]$ ls -l /etc | head
total 1320
drwxr-xr-x.  3 root root        28 Jun 26 13:50 accountsservice
-rw-r--r--.  1 root root        16 Jun 26 14:01 adjtime
-rw-r--r--.  1 root root      1529 Jun 23  2020 aliases
drwxr-xr-x.  3 root root        65 Jun 26 13:54 alsa
drwxr-xr-x.  2 root root      4096 Jun 26 13:58 alternatives
-rw-r--r--.  1 root root       541 Aug  9  2021 anacrontab
-rw-r--r--.  1 root root       769 Aug 28  2021 appstream.conf
-rw-r--r--.  1 root root        55 Feb 11 10:53 asound.conf
-rw-r--r--.  1 root root         1 Aug  9  2021 at.deny

Za pomocą potoków możemy łączyć polecenia w długie łańcuchy przekazując strumień wyjściowy wychodzący z innych poleceń na wejście kolejnych. Polecenia tego typu nazwyane są poleceniami potokowymi. Poniżej znajduej się jedno z nich.

[user@server1 ~]$ ls -l /etc | sed -n '15p' | awk '{printf $1}'

Powyższe polecenie wyświetli pierwszą kolumnę atrybutów pliku z 15 lini zawartości katalogu /etc. Polecenia sed oraz awk znajdują się poza zakresem merytorycznym tego materiału.

7.1.11. Cytowanie

Za tą enigmatyczną nazwą stoją mechanizmy, których możemy użyć, aby znaki specjalne w powłoce, by traktowane jako zwykłe znaki bez żadnego specjalnego znaczenia. Pierwszym z nich jest użycie lewego ukośnika (\). Jeśli na przykład stworzyślimy plik on nazwie *. To jeśli spróbujemy go usunąć bez żadnego cytowania usuniemy wszystko co znajduje się w katalogu.

[user@server1 ~]$ rm \*
rm: remove regular empty file '*'? y
[user@server1 ~]$ ls
D  err.out  etc.tar  file1  ls2.out  ls.out  test.img

Jak widać na załączonym przykładzie moim plikom nic się nie stało. Używanie lewego ukośnika może być uciążliwe jeśli do zacytowania mamy więcej niż jeden znak. Wówczas możemy się posłużyć pojedynczym apostrofem (''). Wszystko co zostanie umieszczone miedzy nimi, będzie traktowane dosłownie. Najprostszym przykładem użycia jest odwołanie się do zmiennej.

[user@server1 ~]$ zmienna='abc'
[user@server1 ~]$ echo $zmienna
abc
[user@server1 ~]$ echo '$zmienna'
$zmienna

Gdy odwołaliśmy się do zmiennej w pojedynczych cudzysłowach, znak dolara został potraktowany dosłownie, więc polecenie echo wypisało ciąg znaków $zmienna. Ostatnim rodzajem cytowania podobnym do pojedynczego apostrofu jest podwójny apostrof. Zauważalną różnicą jest to, iż podwójny apostrof pozwala na użycie specjalnego znaczenia takich znaków jak: lewy ukośnik (\), znak dolara ($) oraz pojedynczy apostrof (''), dzięki czemu wewnątrz literałów prezentowanych za pomocą podwójnych apostrofów możemy uzyskać wartości zmiennych.

[user@server1 ~]$ echo "Pierwszym literami alfabetu są litery $zmienna"
Pierwszym literami alfabetu są litery abc

7.1.12. Wyrażenia regularne

Wyrażenia regularne służą wyszukiwaniu wzorców w tekście. Wzorce składają się z ze znaków specjalnych oraz innych zwykłych znaków mających przybliżyć odnalezienie wzorca. Wyrażenia regularne w powłoce możemy obsługiwać za pomocą polecenia grep. W tym materiale również używaliśmy wyrażeń regularnych przy wyświetlaniu zawartości pliku /etc/login.defs

[user@server1 ~]$ grep -v -e '^#' -e '^$' /etc/login.defs 
MAIL_DIR	/var/spool/mail
UMASK		022
HOME_MODE	0700
PASS_MAX_DAYS	99999
PASS_MIN_DAYS	0
PASS_WARN_AGE	7
...

Na powyższym przykładzie widzimy składnie polecenia, źródłem danych nie musi być wyłącznie plik, równie dobrze mogą one pochodzić z przekierowania. Na przykład z potoku. Najpierw jednak zajmiemy się czymś prostszym, a do tego poziomu dojdziemy pod koniec. Najprostszym wyrażeniem może być zwykły ciąg znaków.

[user@server1 ~]$ grep 'pass' /etc/login.defs
# passwd command) should therefore be configured elsewhere. Refer to
#	PASS_MAX_DAYS	Maximum number of days a password may be used.
#	PASS_MIN_DAYS	Minimum number of days allowed between password changes.
#	PASS_MIN_LEN	Minimum acceptable password length.
#	PASS_WARN_AGE	Number of days warning given before a password expires.

Jak możemy zauważyć polecenie zwróciło wszystkie linie zawierające wyrażenie jakie podaliśmy przed ścieżką do pliku. Podane wyrażenie zapisaliśmy za pomocą małych liter i takie też zostało przypasowane jeśli chcemy, aby nie była rozrózniana wielkość liter należy użyć opcji -i.

[user@server1 ~]$ grep -i 'pass' /etc/login.defs
# passwd command) should therefore be configured elsewhere. Refer to
# Password aging controls:
#	PASS_MAX_DAYS	Maximum number of days a password may be used.
#	PASS_MIN_DAYS	Minimum number of days allowed between password changes.
#	PASS_MIN_LEN	Minimum acceptable password length.
#	PASS_WARN_AGE	Number of days warning given before a password expires.
PASS_MAX_DAYS	99999
PASS_MIN_DAYS	0
PASS_WARN_AGE	7

Poniżej umieściłem listę znaków wykorzystywanych do tworzenie wyrażeń regularnych wraz z opisem.

Wiecęj znaków oraz informacji znajduje się na stronie podręcznika polecenia grep man grep oraz na stronie podręcznika poświęconej wyrażeniom regularnym: man 7 regex. Teraz przedstawie kilka dodatkowych opcji polecenia grep.

Wiecej opcji znajduje się na stronie podręcznika polecenia grep. Myślę, że teraz bez problemu jesteśmy w stanie zrozumieć przykład z początku tego podrozdziału.

Odnośnie egzaminu:
Polecenie grep jest poręcznym narzędziem stosowany do wyłuskiwania informacji z pliku lub wyjścia polecenia. Wybrane informacje mogą zostać przekierowane do pliku, pozostawiając dane wejściowe nienaruszone.

7.1.13. Zarządzanie zadaniami

Uruchamiając jakiś program w terminalu, możemy go odłączyć od niego i przekazać jego wykonanie do tła. Wówczas taki program staje się zadaniem. Program kontynuje swoje wykonanie w tle, ale jest kontrolowany przez terminal, w którym został uruchomiony. W podpowłoce uruchomiłem plik test, który jest mały skryptem, który tak naprawdę nic nie robi.

[user@server1 ~]$ bash test

Użyłem kombinacji klawiszy Ctrl+z, aby zatrzymać wykonanie.

[user@server1 ~]$ bash test
^Z
[1]+  Stopped                 bash test

W ostatniej linii, na początku w nawiasie kwadratowym znajduje się numer zadania. Za pomocą tego numeru możemy odwoływać się do zadania. Teraz uruchomimy je w tle.

[user@server1 ~]$ bg %1
[1]+ bash test &

Do uruchomienia zadania w tle służy polecenie bg, Do odwołania się do niego służą numery zadań poprzedzone znakiem procentu (%). Możemy przywrócić działanie zadania na pierwszy plan przywołując je za pomocą polecenia fg, metoda odwołania się do zadania jest taka sama jak w przypadku polecenia bg. Zanim przełaczym zadanie na pierwszy plan, sprawdźmy jego status za pomocą polecenia jobs.

[user@server1 ~]$ jobs
[1]+  Running                 bash test &

Teraz możemy przełączyć polecenie, aby finalnie je zamknąć za pomocą klawiszy Ctrl + c.

[user@server1 ~]$ fg %1
bash test
^C

W ten sposób zarządza się zadaniami za pomocą mechanizmów powłoki. Zadań przekazanych do tła systemu, może być wiele. Zadania nie zostaną zakończone w momencie wylogowania się z systemu. Dlatego jeśli wykonanie jakiego zadania, będzie wymagać czasu mozemy przenieść je do tła. Przy planach użycia tła systemu warto wziąć po uwagę to, aby przekierować wyjście programów/poleceń do jakiegoś pliku lub do urządzenia /dev/null, które ma jedno zadanie. Ignorować wszelkie napływające do niego dane.

Chcąc uruchomić jakieś zadanie w tle odrazu, możemy pominąć tą całą zabawę w jego zatrzymywanie i przekazywanie do tła. Możemy uruchomić je od razu kończąc polecenie znakiem ampersendu (&).

[user@server1 ~]$ bash test &
[1] 14048
[user@server1 ~]$ jobs
[1]+  Running                 bash test &

7.2. Pliki startowe powłoki

Plik startowe powłoki mają za zadanie wykonać kilka czynności konfiguracjynych, aby użytkownik po jej uruchomieniu nie musiał tracić czasu na ustawienie na przykład ścieżki wszukiwania poleceń, czyli zmiennej PATH. Pliki te definiują również aliasy, które jak do tej pory mogły się wydawać zwykłymi poleceniami. Pliki startowe powłoki możemy podzielić na pliki globalne (mające zastosowanie dla całego systemu) oraz pliki lokalne (mające zastosowanie tylko dla jednego użytkownika).

7.2.1. Globalne pliki startowe powłoki

Do globalnych plików startowych możemy zaliczyć takie pliki jak /etc/bashrc, /etc/profile oraz katalog /etc/profile.d. Zmiany w tych plikach wpływają na konfigurację powłoki wszystkich użytkowników w systemie. Poniżej znajduje się lista ze skróconym opisem co dane pliki zawierają.

Jeśli musimy umieścić dodatkowe konfiguracje w tych plikach najlepiej umieścić je w skrypcie w katalogu /etc/profile.d.

7.2.2. Pliki startowe powłoki użytkownika

Każdy użytkownik posiada swoje pliki startowe powłoki ~/.bash_profile - odpowiadający globalnemu /etc/profile oraz ~/.bashrc - odpowiadający globalnemu /etc/bashrc. Te pliki kopiowane są z katalogu szkieletowego - /etc/skel podczas tworzenia katalogu domowego przy zakładaniu nowego użytkownika. Jeśli zajrzymy do zawartości tych katalogów, to dowiemy się że:

Pliki startowe powłoki są uruchamione określonej kolejności. W ramach eksperymentu umieściłem, krótką informacje na końcu każdego z nich a następnie wylogowałem się i zalogowałem ponownie. W poniższym bloku kodu, znajduje się wynik tego eksperymentu:

xf0r3m@inspiron-3542:~$ ssh user@rhel9-vm1 
user@rhel9-vm1's password: 
Register this system with Red Hat Insights: insights-client --register
Create an account or view all your systems at https://red.ht/insights-dashboard
Last login: Sat Jul  2 15:29:33 2022 from 192.168.122.1
Globalny /etc/bashrc
Globalny /etc/profile
Lokalny ~/.bashrc
Lokalny ~/.bash_profile

Jak mogliśmy się domyślić na początku uruchiamiane są pliki globalne a następnie lokalne.

Dodatkowym plikiem użytkownika o nieco innym znaczeniu jest ~/.bash_logout. Czynności zapisane w tym pliku są wykonywane podczas wylogowywania użytkownika.

Odnośnie egzaminu:
Jeśli jedno z zadań będzie dotyczyć dodania aliasu lub zmiennej środowiskowej, to należy wiedzieć w jakich plikach je umieścić.

Ćwiczenie 1: Zmiana symbolu zachęty

Jako zwykły użytkownik na maszynie server1 dostosuj znak zachęty aby wyglądał jak ten ujęty w podwójne apostrofy "<user@server1 in /etc >:". /etc w tym przypadku jest ścieżką do obecnego katalogu roboczego. Zapisz nową definicję znaku zachęty do odpowiedniego pliku, wyloguje się i zaloguj ponownie następnie usuń tę definicję.

Ćwiczenie 2: Przkierowanie strumieni

Jako zwykły użytkownik na maszynie server1 wyświetl zawartość katalogów /etc, /dvd oraz /var przekieruj standardowe wyjście oraz strumień błędów do pliku /tmp/ioerror Sprawdź zawartość pliku.

Podsumowanie

W tym rodziale zapoznaliśmy się z powłoką BASH. Nauczyliśmy się korzystać z jej mechanizów, poznaliśmy również podstawy wyrażeń regularnych. Na koniec dowiedzieliśmy się jak używać plików startowych powłoki, aby dostosować ją do własnych potrzeb. W następnym rodziale zajmiemy się procesami.

8. Zarządzanie procesami oraz harmonogram zadań

Zarządzanie procesami oraz obsługa harmonogramu zadań jest kolejnym z zadań, które będziemy wykonywać jako administrator systemu Linuks.

8.1. Zarządzanie procesami

Procesy są to odniesienia w pamięci do uruchomionych w systemie programów. Procesy są sposobem organizacji przydzielania zasobów komputera. Struktura procesów w systemie ma charakter hierarchiczny, każdy proces może mieć swoje podprocesy, tzw. procesy potomne. Każdy z procesów posiada swój unikalny identyfikator PID, identyfikator służy jądru do komunikacji z procesami. W systemie istnieje masa procesów, wśród nich możemy wyróżnić takie jak demony, które świadczą usługi użytkownikom oraz innym procesom. Zwykle działają one w tle, oraz pozostają w stanie bezczynności oczekując na żądania od użytkownika lub od innego procesu.

Procesy podczas wykonywania swoich czynności mogą znajdować się w 5 różnych stanach. Stan procesu zależy od tego co robi w danym momencie. Procesy mogą przyjmować następujące stany:

Procesy podczas swoje cyklu życia w systemie, zmieniają te stany w zależności od tego jaką czynność wykonują. Najczęściej będzie to lawirowanie między trzema pierwszymi. Teraz kiedy znamy charakterystykę procesów możemy przejść do praktyki, a na początku zajmiemy się wyświetlaniem informacji o procesach.

8.1.1. Polecenie ps

Polecenie ps, służy do wyświetlania procesesów w tabeli. Domyślnie to polecenie bez żadnych opcji wyświetli procesy uruchomione w tym oknie terminala.

[user@server1 ~]$ ps
    PID TTY          TIME CMD
   1452 pts/1    00:00:00 bash
   1891 pts/1    00:00:00 ps

Wynik polecenia podzielony jest na 4 kolumny. Kolumna PID zwraca identyfikator procesu, następnie kolumna TTY wyświetla powiązaną z procesem nazwę terminala, na którym został uruchomiony, później w kolumnie TIME znajduje się łączny czas procesora w wyrażony w sekundach poświęcony na wykonanie tego procesu. Na końcu w kolumnie CMD, znajduje się polecenie odpowiedzialne za ten proces. To polecenie nie zwraca zbyt wielu informacji, które mogły by nam pomóc w zarządzaniu procesami. Warto dodać do tego polecenia kilka opcji.

Jedną z nich może być opcja -e, która zwraca każdy występujący w systemie proces, następną jak warto dodać jest opcja -f, zwracająca wiecej szczegółów nt. procesu, jednak ta opcja nie jest dla nas wystarczająca, ponieważ do zarządzania procesami potrzebujemy jeszcze kilku informacji, które jest w stanie zwrócić nam opcja -l. Te wymienione trzy opcje, tworzą najbardziej powszechne użycie polecenia ps.

[user@server1 ~]$ ps -efl 
F S UID          PID    PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
...
4 S root        1443     849  0  80   0 -  4884 -      10:34 ?        00:00:00 sshd: user [priv]
5 S user        1448    1443  0  80   0 -  4884 -      10:34 ?        00:00:00 sshd: user@pts/1
0 S user        1452    1448  0  80   0 - 56082 do_wai 10:34 pts/1    00:00:00 -bash
1 I root        1828       2  0  80   0 -     0 -      13:15 ?        00:00:00 [kworker/u2:2-events_unbound]
1 I root        1838       2  0  80   0 -     0 -      13:58 ?        00:00:00 [kworker/0:1-events]
1 I root        1851       2  0  80   0 -     0 -      14:02 ?        00:00:00 [kworker/u2:1-events_unbound]
1 I root        1910       2  0  80   0 -     0 -      15:28 ?        00:00:00 [kworker/0:0-kdmflush/253:0]
1 I root        1931       2  0  80   0 -     0 -      15:37 ?        00:00:00 [kworker/0:2-kdmflush/253:0]
0 R user        1932    1452  0  80   0 - 56375 -      15:38 pts/1    00:00:00 ps -efl

Polecenie to zwraca bardzo dużo informacji o procesach. Najważniejsze dla nas są kolumny: S, UID, PID, PPID, PRI, NI oraz CMD, w których znajdują się takie informacje jak:

Jako ciekawostkę podam, iż procesy, które w kolumnie TTY posiadają znak zapytania (?) pochodzą prawdopodbnie od któregoś z demonów.

Polecenie ps daje możliwość wyświetlenia tylko tych informacji, które potrzebujemy dzięki opcji -o. Możemy użyć polecenia poniżej:

[user@server1 ~]$ ps -eo user,s,pid,command
USER     S     PID COMMAND
root     S       1 /usr/lib/systemd/systemd rhgb --switched-root --system --deserialize 31
root     S       2 [kthreadd]
root     I       3 [rcu_gp]
root     I       4 [rcu_par_gp]
root     I       6 [kworker/0:0H-events_highpri]
root     I       9 [mm_percpu_wq]
root     S      10 [rcu_tasks_kthre]
root     S      11 [rcu_tasks_rude_]
root     S      12 [rcu_tasks_trace]
root     S      13 [ksoftirqd/0]
root     I      14 [rcu_preempt]

Za pomocą ps, możemy wyszukać proces na podstawie polecenia. Do tego wykorzystamy opcje -C.

[user@server1 ~]$ ps -o user,s,pid,command -C sshd
USER     S     PID COMMAND
root     S     849 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root     S    1974 sshd: user [priv]
user     S    1979 sshd: user@pts/0
root     S    2098 sshd: user [priv]
user     S    2103 sshd: user@pts/1

Polecenie ps zawiera ogromną ilość opcji. Ich opisy znajdują się na stronie podręcznika polecenia.

8.1.2. Polecenie top

Polecenie z poprzednie podrozdziału służyło do wyświetlania procesów. Za pomocą tego przedstawionego tutaj możemy je monitorować w czasie rzeczywistym. Polecenie top, może posłużyć do sprawdzenia czy system nie jest przeciążony.

[user@server1 ~]$ top
top - 16:31:38 up  6:00,  1 user,  load average: 0.00, 0.00, 0.00
Tasks: 174 total,   1 running, 173 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :    960.5 total,    123.2 free,    464.1 used,    373.2 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.    335.9 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND 
      1 root      20   0  106212  15976  10328 S   0.0   1.6   0:01.96 systemd  
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kthreadd 
      3 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 rcu_gp 
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 rcu_par_gp 
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/0:0H-events_highpri 
      9 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 mm_percpu_wq 
     10 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_tasks_kthre 
     11 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_tasks_rude_ 
     12 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_tasks_trace 
     13 root      20   0       0      0      0 S   0.0   0.0   0:00.36 ksoftirqd/0 
     14 root      20   0       0      0      0 I   0.0   0.0   0:00.26 rcu_preempt 
     15 root      rt   0       0      0      0 S   0.0   0.0   0:00.05 migration/0
     16 root      20   0       0      0      0 S   0.0   0.0   0:00.00 cpuhp/0 
     18 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kdevtmpfs
     19 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 netns 
     20 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 inet_frag_wq 

Informacje zwracane przez to polecenie możemy podzielić na dwie części jedną z nich jest część, w której znajduje się podsumowanie. W pierszej linii znajduje się linia identyczna z informacją zwracaną przez polecenie uptime, następne linie to: ilość liczbowa procesów w systemie, informacje o zużyciu procentowym procesora, zużyciu pamięci operacyjnej oraz przestrzeni wymiany. Ostatnia kolumna z ostatniego rzędu zwraca rzeczywistą ilość dostępnej pamięci operacyjnej bez wliczania przestrzeni wymiany.

Drugą częścią jest tabelaryczne przedstawienie listy procesów dostępnych w systemie. W tym przypadku procesy, które zużywają najwięcej zasobów komputera wyświetlane są w pierwszych wierszach tabeli. Przez co można stwierdzić, że tabela jest posortowana względem wartości procentowej zużycia procesora. Polecenie top jest bardzo elastyczne, posiadamy możliwość dostosowania kolumn wyświetlanych w tabeli oraz części z podsumowaniem, informacje na ten temat znajdują się na stronie podręcznika.

8.1.3. Priorytet procesu

Podczas wykonywania procesów, każdy z procesów dostaje dostęp do procesora na ułamek sekundy, wówczas proces zmienia swój stan na Running, po upływie tego czasu proces w zależności od czynności przechodzi albo w Waiting albo w Sleeping. Kiedy proces przebywa w tych stanach wykonywane są inne procesy, później proces znów zostaje wznowiony i tak w kółko. Tego typu działanie nazywane jest wielozadaniowością (a przynajmniej się takie wydaje). Każdy z procesów otrzymuje określoną ilość czasu, jednak są procesy, które mogą otrzymać go więcej lub mniej. Czasami zmiana tych wartość jest to niezbędna do funkcjonowania systemu. Czynnikiem który wpływa na tę decyzje jest priorytet - kolumna PRI przy poleceniu ps. Priorytety są już z góry określone podczas uruchamiania procesu. Najwyszym priorytetem (proces otrzyma najwięcej czasu procesora) jest 0, a najniższym (proces otrzyma najmniej czasu procesora) jest 39. Administratorzy mają wpływ na priorytet procesu za pomocą wartości nice. Kiedy administrator chce zmienić wartość priorytetu, ustala wartość nice dla procesu a ona jest do niego dodawana. Wartość nice, może być w zakresie od -20 do 19. Jeśli chcemy aby proces posiadał w momencie uruchomienia inny priorytet uruchamiamy go za pomocą polecenia nice.

[user@server1 ~]$ ps -efl | grep $(pidof top)
0 S user        2152    1980  0  80   0 - 56480 do_sel 17:24 pts/0    00:00:00 top
[user@server1 ~]$ nice -n 19 top
[user@server1 ~]$ ps -efl | grep $(pidof top)
0 S user        2137    1980  0  99  19 - 56480 do_sel 17:20 pts/0    00:00:00 top

Kolumna numer 7 zawiera wartość priorytetu, a 8 kolumna zawiera wartość nice. Zwróćmy uwagę na rozbierzności w wyświetlaniu priorytetów procesów między poleceniami top oraz ps. Nie mniej jednak wartość nice sprawiła zmianę priorytetu procesu top.

Jeśli proces jest już uruchomiony to również możemy zmienić jego priorytet, za pomocą polecenie renice, jednak najpierw poznamy dwa polecenia, które ułatwią nam pracę z poleceniem renice oraz każdym innym, które potrzebuje identyfikatora procesu. Tymi poleceniami są polecenia pidof oraz pgrep.

[user@server1 ~]$ pidof top
2170
[user@server1 ~]$ pgrep top
2170

Polecenia bez żadnych opcji zwracają to samo - identyfikator procesu i tymi poleceniami będziemy się wspomagać. Oczywiście więcej informacji na ich temat znajduje się na stronach podręcznika. Wracajac do wartości nice, zmienie teraz priorytet działającego procesu za pomocą polecenia renice.

[user@server1 ~]$ sudo renice -n -10 $(pidof top)
[sudo] password for user: 
2170 (process ID) old priority 0, new priority -10
[user@server1 ~]$ ps -efl | grep $(pidof top)
0 S user        2170    1980  0  70 -10 - 56480 do_sel 17:43 pts/0    00:00:00 top

Priorytet procesu zmienił się. Zwróćmy uwagę na to, iż polecenie renice wymaga uprawnień superużytkownika do działania.

8.1.4. Zarządzanie procesami za pomocą syganałów

Działanie procesów, może być kontrolowane za pomocą sygnałów, które są do nich wysyłane przez jądro systemu. Te sygnały mogą zatrzymać wykonanie procesu, przenieść proces na do tła, poprosić go o zakończenie działania lub gdy jest dość oporny na poprzedni sygnał poprostu unicestwić. Każdy z sygnałów posiada swoją nazwę oraz numer. My możemy używać dowolnej formy. Poniżej znajduje się lista najczęściej wykorzystywanych sygnałów wraz z numerami.

Do wysyłania sygnałów do procesów służy polecenie kill lub pkill. Różnią się one tym, iż poleceniu kill podaje się PID, natomiast pkill nazwę procesu lub polecenie. Numery sygnałów podjemy po myślniku przed PID-em lub nazwą. Jeśli chcemy użyć nazw sygnałów należy podać je po opcji -s (w przypadku polecenia kill) lub --signal (w przypadku polecenia pkill). Listę dostępnych sygnałów możemy wyświetlić za pomocą poniższego polecenia:

[user@server1 ~]$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

W ramach przykładu zakończymy działania procesu top.

[user@server1 ~]$ pkill top
[user@server1 ~]$ kill $(pidof top)

Do kończenia pracy procesów możemy użyć również polecenia killall, które kończy pracę wszystkich procesów, które pasują do określonych kryteriów.

8.2. Harmonogram zadań

Harmonogram zadań służy do planowania wykonania poleceń czy uruchomienia programów w przyszłości. Na Linuksie możemy to zrobić na dwa sposóby, w zależności od potrzeby powtórzenia danego zadania. Na Linuksie zadania z harmonogramu moga być uruchomione raz w określonym czasie lub wykonywane cyklicznie co jakiś okres czasu. Narzędziami odpowiedzialnymi za obsługę funkcji harmonogramu zadań na Linuksie są at oraz cron. W RHEL możemy również spotkać się z mechanizmem kontroli dostępu do tych programów.

8.2.1. Kontrola dostępu do harmongramu

W RHEL stosowany jest mechanizm kontroli dostępu do harmonogramu zdań zarówno dla obu narzędzi. Ten mechanizm składa się z dwóch plików, jeden z rozszerzeniem *.allow oraz jeden z rozszerzeniem *.deny. W tych plikach umieszczamy wpis z nazwą użytkownika w zależności od tego czy użytkownik ma mieć lub nie mieć dostępu do tych narzędzi. Sama obecność tych plików w systemie ma wpływ na to czy użytkownicy będą mogli korzystać z harmonogramu zadań. Każdy program posiada swoją parę plików. W poniższej tabeli znajdują się rozpisane zależności, które obrazują w jaki sposób obecność tych plików wpływa na dostęp do narzędzi harmonogramu zadań.

Obecność pliku *.allow Obecność pliku *.deny Wpływ na dostęp do at oraz cron
Istnieje, posiada wpisy użytkowników. Nie musi istnieć. Wszyscy użytkownicy w pliku posiadają dostęp.
Istnieje, ale jest pusty. Nie musi istnieć. Nikt nie ma dostępu.
Nie istnieje. Istnieje, posiada wpisy użytkowników. Wszyscy użytkownicy, poza tymi zapisanym w pliku *.deny mają dostęp.
Nie istnieje. Istnieje, ale jest pusty. Wszyscy użytkownicy posiadają dostęp.
Nie istnieje. Nie istnieje. Nikt nie ma dostępu.

Powyższe zasady nie obowiązują oczywiście superużytkownika, nawet mimo umieszczenia jego nazwy w plik *.deny. Domyślnie w RHEL plik *.allow nie istnieje, natomiast plik *.deny istnieje, ale jest pusty.

Odnośnie egzaminu:
Definiując wpisy w tych plikach, należy pamiętać o tym aby jedna linia zwierała jednego użytkownika. To jedyna poprawna metoda.

Po wpisaniu nazwy użytkownika do pliku, jeśli będzie on chciał skorzystać z at lub cron zobaczy poniższe komunikaty.

[user@server1 ~]$ at
You do not have permission to use at. 
[user@server1 ~]$ crontab -e
You (user) are not allowed to use this program (crontab)
See crontab(1) for more information

Oczywiście dla przykładu zablokowałem dostęp do obu narzędzi.

8.2.2. Wykonanie zadań w przyszłości - polecenie at

Polecenie at służy do planowania pojedynczego wykonania zadania w przyszłości. Podczas wydawania polecenia podajemy czas oraz ewentualnie datę kiedy zadanie ma zostać wykonane. Czas możemy podać na kilka różnych sposobów:

Po wydaniu polecenia z datą, polecenie uruchomi tryb interaktywyny, gdzie w każdej linii będziemy mogli zapisać polecenie, które wykona się o podanym czasie. Po zakończeniu zapisywania poleceń naciskamy kombinację Ctrl+d, aby zakończyć działanie programu i zapisać polecenia, wówczas po znaku zachęty pojawi się znak <EOT> oznaczający zakończenie wprowadzania poleceń.

[user@server1 ~]$ at midnight
warning: commands will be executed using /bin/sh
at> date 
at> pwd
at> whoami
at> who
at> w
at> <EOT>
job 2 at Wed Jul  6 00:00:00 2022

Polecenia zapisywane są w odpowiednich pliku w katalogu /var/spool/at. Dostęp do katalogu wymaga uprawnień administratora.

[user@server1 ~]$ sudo ls -l /var/spool/at
[sudo] password for user: 
total 4
-rwx------. 1 user user 3365 Jul  5 09:28 a0000201a56968

Programem odpowiedzialnym za wykonanie poleceń jest demon atd. Za pomocą opcji polecenia at możemy na przykład wyświetlić listę zdefiniowanych zadań (-l):

[user@server1 ~]$ at -l 
2	Wed Jul  6 00:00:00 2022 a user

W pierwszej kolumnie znajduje się numer zadania. Jest on istotny jeśli chcemy na przykład wyświetlić jego plik za pomocą opcji -c.

[user@server1 ~]$ at -c 2
#!/bin/sh
# atrun uid=1000 gid=1000
# mail user 0
umask 22
SHELL=/bin/bash; export SHELL
HISTCONTROL=ignoredups; export HISTCONTROL
HISTSIZE=1000; export HISTSIZE
HOSTNAME=server1.example.com; export HOSTNAME
PWD=/home/user; export PWD
LOGNAME=user; export LOGNAME
XDG_SESSION_TYPE=tty; export XDG_SESSION_TYPE
MOTD_SHOWN=pam; export MOTD_SHOWN
HOME=/home/user; export HOME
LANG=en_US.UTF-8; export LANG
...
${SHELL:-/bin/sh} << 'marcinDELIMITER29478f5e'
date
pwd
whoami
who
w
marcinDELIMITER29478f5e

Zadanie możemy też usunąć za pomocą opcji -r lub -d, plik z katalogu /var/spool/at również zostanie usunięty.

[user@server1 ~]$ at -d 2
[user@server1 ~]$ at -l

Do usunięcia potrzebny jest numer zadania, który znajduje się pierwszej kolumnie listy zadań wyświetlonej za pomocą polecenia at -l. Obie przestawione powyżej opcje są aliasami do poleceń atq oraz atrm.

Działanie narzędzi harmonogramu zadań jest rejestrowane, a ich komunikaty diagnostyczne są zapisywane w /var/log/cron. Mimo iż nazwa pliku wskazuje iż dotyczy on głównie crond to komunikaty atd również są tam zapisywane. Aby wyświetlić jego zawartość potrzebne są uprawnienia administratora:

[user@server1 ~]$ sudo tail /var/log/cron
[sudo] password for user: 
Jul  5 09:01:01 server1 anacron[1651]: Jobs will be executed sequentially
Jul  5 09:01:01 server1 run-parts[1640]: (/etc/cron.hourly) finished 0anacron
Jul  5 09:01:01 server1 CROND[1639]: (root) CMDEND (run-parts /etc/cron.hourly)
Jul  5 09:21:01 server1 anacron[1651]: Job `cron.daily' started
Jul  5 09:21:01 server1 anacron[1651]: Job `cron.daily' terminated
Jul  5 09:21:01 server1 anacron[1651]: Normal exit (1 job run)
Jul  5 10:01:01 server1 CROND[1941]: (root) CMD (run-parts /etc/cron.hourly)
Jul  5 10:01:01 server1 run-parts[1941]: (/etc/cron.hourly) starting 0anacron
Jul  5 10:01:01 server1 run-parts[1941]: (/etc/cron.hourly) finished 0anacron
Jul  5 10:01:01 server1 CROND[1940]: (root) CMDEND (run-parts /etc/cron.hourly)

8.2.3. Definiowanie powtarzalnych zadań - program cron

Na Uniksach, bo nie tylko na Linuksie, istnieje program służący do definiowania powtarzalnych zadań uruchamianych co jakiś czas. Takim programem, a raczej demonem jest cron. Działanie tego programu opiera się o pliki zwane tablicami cron lub crontabami. Każdy użytkownik może korzystać z tego narzędzia, chyba, że występuje on w pliku /etc/cron.deny. Użytkownicy do kontroli harmonogramu zadań, używają polecenia crontab. Każdy z nich zapisuje zadania w swojej tablicy znajdującej się w katalogu /var/spool/cron. Zapis zadań w tablicy wymaga ściśle określonej składni. Wpis składa się z 5 pól czasu, ewentualnego pola zawierającego nazwę użytkownika (pole to występuje tylko w systemowej tablicy cron /etc/crontab), z którego uprawnieniami ma zostać wykonane to zadanie, oraz reszty linii zawierającej polecenie do wykonania. Poniżej znajduje się przykład wpis, który sobie przeanalizujemy.

15 13 4 7 * echo "Hello World" > /dev/pts/1

Każdy wpis zadania w tablicy cron rozpoczyna się od 5 pól określających kiedy owe zadanie ma zostać wykonane. Pola od lewej definiują kolejno:

Pola czasu przyjmują takie wartości jak listy (określajace konkretne godziny, dni itd. Na przykład 15,16), zakresy (określające, którego do którego polecenia ma zostać wykonane np. 10-20) oraz wartości skokowe, które definiują co ile minut, godzin lub dni wykonać to zadanie. Listy oraz zakresy można łączyć ze sobą zazwyczaj 10-15,21 może to oznacząć uruchomienie zadania od 10-15 oraz 21 dnia miesiąca. Więcej informacji na temat można znaleźć na stronie podręcznika pliku /etc/crontab.

Ostatnie pole, jest miejscem do umieszaczania poleceń. Jeśli polecenie zwraca istotne dane wyjściowe to najlepiej jest je przekierować.

Odnośnie egzamin:
Wykonując zadania administracyjne, powinniśmy mieć opanowaną składnie wpisów zadań oraz rozumienie jej poszczególnych części.

Do edycji tablicy cron służy polecenie crontab z opcją -e. Polecenie to uruchomi edytor, w którym to zapiszemy wpisy harmonogramu, po zakończeniu edycji i zapisaniu zmian polecenie utworzy plik w /var/spool/cron o nazwie użytkownika. Ten plik staje się teraz jego tablicą cron. Po zapisaniu pliku tablica zostaje zainstalowana w pamięci demona. W ten sposób definiuje się wpisy harmonogramu zadań cron.

Za pomocą opcji -l możemy wyświetlić obecną tablicę cron.

[user@server1 log]$ crontab -l
15 13 4 7 * echo "Hello World" > /dev/pts/1

Za pomocą opcji -r możemy usunąć obecną tablicę cron:

[user@server1 log]$ crontab -r
[user@server1 log]$ crontab -l
no crontab for user

8.2.4. Wykonanie pominiętych zadań - program Anacron

Nie które komputery z zainstalownym Linuksem, nie są właczone 24/7. Za tem cześć zadań zdefiniowana w systemowym harmonogramie w plikach /etc/cron.* nie jest wykonywana. Pliki te dotyczą czynność systemowych zdefiniowanych co godzinę, dzień, tydzień czy miesiąc. Ta przypadłość najczęściej dotyczy komputerów osobistych. Dlatego też, aby uruchomić te zdania używa się demona anacron. Aby demon rozpoczął uruchmianie niewykonanych zadań muszą zostać w systemie spełnione trzy czynniki. Pliki tablicy anacron muszą istnieć w katalogu /var/spool/anacron, musi upłynąć odpowiednia ilość czasu (zdefiowana w pliku systemowej tablicy /etc/anacrontab) oraz komputer musi być podłączony do stałego źródła zasilania. Poniżej znajduje sie plik konfiguracyjny anacron.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
RANDOM_DELAY=45
START_HOURS_RANGE=3-22
1	        5	cron.daily		  nice run-parts /etc/cron.daily
7	       25	cron.weekly		  nice run-parts /etc/cron.weekly
@monthly 45	cron.monthly		nice run-parts /etc/cron.monthly

W pierwszej kolumnie znajduje się okres (w dniach) jaki musi upłynąć od ostatniego uruchomienia tych zdań. W drugiej znajduje się opóźnienie które musi minąć od uruchomienia systemu, to opóźnienie jest zazwyczaj powiększone o wartość zdefiniowaną w zmiennej RANDOM_DELAY. W następnej kolumnie znajduje się identyfikator zdania, a ostatnia kolumna to już pole polecenia. Dość istotną rolę gra tutaj wartość zmiennej START_HOURS_RANGE. Określa ona w jakich godzinach anacron może działać.

Działanie demona możemy wymusić poprzez wydanie polecenia anacron.

[user@server1 ~]$ sudo anacron
[sudo] password for user: 
[user@server1 ~]$ 

Ćwiczenie 1: Wartość nice procesu

Na maszynie server1, uruchomy dwa termiale (mogą być to dwa połączenia SSH z tą maszyną) następnie w jednym z terminali uruchom program top, na drugim terminalu za pomocą polecenia ps lub pgrep ustal PID oraz wartość nice dla wcześniej uruchomionego polecenia top. Zakończ działanie programu top, następnie uruchom je ponownie z priorytetem zmniejszonym o 8 (+8). Potwierdź zmianę priorytet na drugim terminalu. Następnie zwiększ priorytet o 10 (-10) nie zamykając programu top i potwierdź to w drugim terminalu.

Ćwiczenie 2: Konfiguracja tablicy cron użytkownika

Na maszynie server1 jako superużytkownik zapewnij dostęp do cron dla zwykłego użytkownika, na którego się logujesz. Jako zwykły użytkownik sprawdź nazwę aktualnego terminala (polecenie tty) oraz obecną datę i godzinę (polecenie date). Za pomocą harmonogramu zadań cron ustaw wykonanie polecenia echo "Hello World" > nazwa_terminala (za nazwa_terminala podstawiamy nazwę uzyskaną z polecenia tty) za 3 minuty od bierzącego czasu. Zaczekaj na wykonanie zadania.

Podsumowanie

W tym rodziale zapoznaliśmy się z procesami, wyświetaliśmy ich listę korzystaliśmy z polecenia top, zmienialiśmy ich priorytet oraz zarządzaliśmy nimi za pomocą sygnałów. W drugiej części zajęliśmy się narzędziami harmonogramu zadań. Nauczyliśmy się korzystać z takich programów jak at czy cron. Na koniec poznaliśmy specyficzny dla dystrybucji klasy enterprise demon Anacron. W następnym rozdziale zapoznamy się podstawami zarządzania pakietami oprogramowania na RHEL.

9. Podstawy zarządzania pakietami oprogramowania

Pakiety inaczej zwane paczkami czy ogólnie oprogramowanie dystrybucji, to archiwa przechowywujące zazwyczaj skompilowane oprogramowanie przeznaczone dla systemu, który używamy. Wewnątrz pakietów znajduje się cała strukturktura katalogowa z plikami pakietu, tak by podczas instalacji wystarczyło je tylko rozpakować. Wewnątrz znajdują się również metadane zawierające informacje o pakiecie.

Większość pakietów posiada długie nazwy, które nie są dokońca rozumiałe. Weźmy na przykład taką nazwę jak:

openssl-1.1.1-8.el8.x86_64.rpm
Zawiera ona takie elementy jak:

Kompilacja programów ze źródeł wymaga odpowiedniego środowiska (w ten sposób tworzy się binarne wersje programów udostępnionych w pakietach). Aby oprogramowanie z pakietu mogło działać tak samo, jak na w systemie, w którym zostało skompilowane wymaga identycznego środowiska. To środowisko jest przedstawiane za pomocą zależności pakietu, czyli wszystkich innych pakietów, wymaganych do działania tego programu, który chcemy zainstalować. Informacje o zależnościach znajdują się w metadanych pakietu.

Systemy, w których dystrybucja oprogramowania jest oparta o pakiety musi posiadać bazę danych pakietów. W przypadku RHEL znajduje się ona w katalogu /var/lib/rpm. Na podstawie danych umieszczonych w tym katalogu oprogramowanie do zarządzania pakietami pobiera informacje o zainstalowanych w systemie pakietach.

Aby móc zarządzać pakietami potrzebne są do tego odpowiednie programy. Red Hat posiada takie polecenie jak rpm (ang. Red Hat Packet Manager) i jest to podstawowe narzędzie do instalacji, aktualizacji, wyszukiwania informacji czy usuwania oprogramowania z systemu. Rpm jest bardzo prostym narzędziem jednak posiada jedną wadę, otóż nie rozwiązuje samodzielnie zależności. Dlatego też pojawił się taki program jak yum, który pełni takie same role jak rpm jednak nie posiada wspomnianej wady. Od wersji 8 RHEL, yum został zastąpiony przez dnf, który rozszerza funkcjonalność yum. W tym rodziale zajmiemy się wyłącznie rpm. W następnym zaś skupimy się na dnf.

9.1. Red Hat Packet Manager - RPM

9.1.1. Pobieranie danych z pakietu

Jedną z podstawowych czynności jakie możemy wykonać przy użyciu tego narzędzia jest pobieranie informacji o pakietach, za pomocą opcji -q podając jako argument nazwę programu możemy dowiedzić się czy takowy jest zainstalowany w systemie.

[user@server1 ~]$ rpm -q zsh
package zsh is not installed
[user@server1 ~]$ rpm -q bash
bash-5.1.8-4.el9.x86_64

Pakiet zsh nie jest zainstalowany w moim systemie. Natomiast drugie wywołanie potwierdza, że systemie zainstalowano powłokę BASH.

Z racji tego, iż rpm to nie tylko narzędzie ale i format danych za pomocą tego polecenia, możemy wyświetlić pliki konfiguracyjne programu (-qc), który zawiera pakiet czy pliki dokumentacji (-qd).

#Plik dokumentacji:
[user@server1 ~]$ rpm -qd bash
/usr/share/doc/bash/FAQ
/usr/share/doc/bash/INTRO
/usr/share/doc/bash/RBASH
/usr/share/doc/bash/README
/usr/share/doc/bash/bash.html
/usr/share/doc/bash/bashref.html
...

#Pliki konfiguracji:
[user@server1 ~]$ rpm -qc bash
/etc/skel/.bash_logout
/etc/skel/.bash_profile
/etc/skel/.bashrc

Wszystkie pliki pakietu możemy wyświetlić za pomocą opcji -ql. Generalnie opcja -q, jest modyfikatorem odpowiedzialnym za pobieranie danych z bazy lub z samego pliku pakietu.

[user@server1 ~]$ rpm -ql bash
/etc/skel/.bash_logout
/etc/skel/.bash_profile
/etc/skel/.bashrc
/usr/bin/alias
/usr/bin/bash
/usr/bin/bashbug
/usr/bin/bashbug-64
/usr/bin/bg
/usr/bin/cd
...

Za pomocą opcji -qf możemy wyszukać pakietu, z którego pochodzi podany plik.

[user@server1 ~]$ rpm -qf /etc/bashrc
setup-2.13.7-6.el9.noarch

Polecenie zwróci nazwę pakietu. Zwróćmy uwagę na to, że nazwy zainstalowanych pakietów w RHEL różnią się od nazw plików tym, że nie posidają rozszerzenia. Jeśli nie wiemy czym jest ten pakiet przedstawiowny na poprzednim przykładzie za pomocą opcji -qi możemy sprawdzić do czego służy.

[user@server1 ~]$ rpm -qi setup-2.13.7-6.el9.noarch
Name        : setup
Version     : 2.13.7
Release     : 6.el9
Architecture: noarch
Install Date: Sun 26 Jun 2022 01:48:46 PM CEST
Group       : System Environment/Base
Size        : 726478
License     : Public Domain
Signature   : RSA/SHA256, Sat 20 Nov 2021 04:26:39 PM CET, Key ID 199e2f91fd431d51
Source RPM  : setup-2.13.7-6.el9.src.rpm
Build Date  : Tue 10 Aug 2021 06:16:15 PM CEST
Build Host  : ppc64le-03.build.eng.rdu2.redhat.com
Packager    : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
Vendor      : Red Hat, Inc.
URL         : https://pagure.io/setup/
Summary     : A set of system configuration and setup files
Description :
The setup package contains a set of important system configuration and
setup files, such as passwd, group, and profile.

Przed przejściem do kolejnego zaganienia musimy umieścić płytę instalacjyną z RHEL w napędzie maszyny. Z niej będziemy pobierać pakiety do dalszych ćwiczeń, gdyż nasze maszyny nie są podłączone do sieci Red Hat. Po włożeniu płyty do napędu dodajmy poniższy wpis do pliku /etc/fstab, aby płyta posiadała prosty i stały punkt montowania. Na płycie znajdują się dwa katalogi BaseOS/Packages oraz AppStream/Packages, gdzie znajdują się pakiety.

/dev/sr0  /mnt  iso9660 ro  0 0

Po zapisaniu zmian w pliku wywołujemy polecenie sudo mount -a, aby zamontować płytę w podkatalogu /mnt. Kiedy płyta jest już zamontowana w systemie, możemy wyświetlić informacje z metadanych pliku pakietu za pomocą opcji -qip.

[user@server1 ~]$ rpm -qip /mnt/BaseOS/Packages/bash-completion-2.11-4.el9.noarch.rpm 
Name        : bash-completion
Epoch       : 1
Version     : 2.11
Release     : 4.el9
Architecture: noarch
Install Date: (not installed)
Group       : Unspecified
Size        : 1057184
License     : GPLv2+
Signature   : RSA/SHA256, Sat 20 Nov 2021 11:52:53 AM CET, Key ID 199e2f91fd431d51
Source RPM  : bash-completion-2.11-4.el9.src.rpm
Build Date  : Mon 09 Aug 2021 09:47:13 PM CEST
Build Host  : s390-070.build.eng.bos.redhat.com
Packager    : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
Vendor      : Red Hat, Inc.
URL         : https://github.com/scop/bash-completion
Summary     : Programmable completion for Bash
Description :
bash-completion is a collection of shell functions that take advantage
of the programmable completion feature of bash.

Polecenie zwraca taką samą ilość informacji jak polecenie rpm wraz z opcją -qi.

9.1.2. Sprawdzanie pakietów

Za pomocą jednego z programów związanych z rpm oraz za pomocą samego polecenia rpm możemy sprawdzić poprawność oraz wiarygodność pakietów, które zamierzamy zainstalować, jak i sprawdzić co zostało zmienione przez nas w plikach programów, które są już zainstalowane w systemie względem oryginalnego pakietu.

Walidację pakietu, który mamy zainstalować możemy przeprowadzić względem dwóch kryteriów, jednym z nich jest poprawność plików znajdujących się w pakiecie. Ta metoda polega na sprawdzeniu sumy kontrolnej MD5.

[user@server1 ~]$ rpmkeys -K /mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm --nosignature
/mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm: digests OK

Drugim kryterium jest wiarygodność. Każdy pakiet zawiera podpis twórcy/dostawcy, w naszym przypadku jest to dostawca którym jest firma Red Hat, ten podpis dokonywany jest za pomoca klucza kryptograficznego. Część tego klucza odpowiedzialna za szyfrowanie danych jest instalowana w naszym systemie i pozostaje do naszej dyspozycji. Poza omówią funkcją ta część klucza może również pomoć nam sprawdzić czy pakiet który pobraliśmy z internetu rzeczywiście został stworzony przez firmę Red Hat, za nim zaczniemy używać klucza musimy go na początku zaimportować, importu dokonujemy za pomocą polecenia poniżej, ścieżka do klucza również znajduje się na przykładzie.

[user@server1 ~]$ sudo rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release 

Import klucza, jak i sprawdzenie wiargodności pakietów wymaga uprawnień administratora.

[user@server1 ~]$ rpmkeys -K /mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm
/mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm: digests signatures OK

Teraz polecenie sprawdziło nie tylko poprawność plików (digests), ale także ich wiarygodność (signatures).

Za pomocą polecenia rpm możemy sprawdzić zainstalowane już pakiety pod kątem zmian względem plików znajdujących w oryginalnym pakiecie. Służy temu opcja -V, jeśli nie ma żadnych zmian to polecenie nic nie zwróci. Możemy oczywiście zwiększyć ilość komunikatów generowanych podczas pracy programu z pomocą opcji -v, wówczas polecenie zwróci informacje na temat każdego sprawdzonego pliku pakietu.

[user@server1 ~]$ sudo rpm -V at
[sudo] password for user: 
.......T.  c /etc/at.deny

Informacja zwracana przez to polecenie składa się z trzech kolumn, w pierwszej kolumnie znajdują się flagi, które mowią nam co uległo zmianie, każda z 9-ciu kropek to jedna flaga, w drugiej kolumnie znajdują się rodzaj pliku w tym przypadku c. Ostatnia kolumna zawiera ścieżkę bezwzględną do pliku. Poniżej znajdują się listy z flagami oraz typami plików. Kolejność na liście z flagami oznacza konkretną kropkę.

  1. S - różnica w wielkości pliku,
  2. M - różnica w uprawnieniach,
  3. 5 - różnica w sumie kontrolnej pliku,
  4. D - różnica w numerze głównym i pobocznym urządzenia, urządzenie zostało zmienione,
  5. L - zmieniona ścieżka dowiązania symbolicznego,
  6. U - zmieniony właściciel pliku,
  7. G - zmieniona grupa pliku,
  8. T - różnica w czasie modyfikacji,
  9. P - różnica w POSIX Capabilities

Kropka (.) oznacza brak zmian. Poniżej znajduje się lista zawierająca typy plików.

9.1.3. Instalacja i reinstalacja

Aby jak najlepiej wykorzystać polecenie rpm do instalacji pakietów wykorzystuje się trzy opcje: -i - oznaczającą samą instalacje pakietu, -v - zwiększającą ilość generowanych przez polecenie komunikatów, -h - wyświetlająca pasek postępu w postaci serii krzyżyków (#), po zapisaniu tych opcji podajemy ścieżkę do pliku pakietu. Uwaga, tutaj pojawia się omawiana wcześniej wada: brak rozwiązywania zależność, jeśli spróbujemy zainstalować jakiś pakiet, które zależności nie są rozwiązane (spełnione), rpm nie podejmie w ogóle instalacji, zwraca błąd o niespełnionych zależnościach. Na przykładzie poniżej znajduje się instalacja powłoki zsh.

[user@server1 ~]$ sudo rpm -ivh /mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm 
[sudo] password for user: 
Verifying...                          ################################# [100%]
Preparing...                          ################################# [100%]
Updating / installing...
   1:zsh-5.8-9.el9                    ################################# [100%]

Teraz powłoka zsh jest zainstalowana w systemie. Pakiety możemy również reinstalować podając po -i długą opcję --reinstall.

[user@server1 ~]$ sudo rpm -i --reinstall -vh /mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm 
Verifying...                          ################################# [100%]
Preparing...                          ################################# [100%]
Updating / installing...
   1:zsh-5.8-9.el9                    ################################# [ 50%]
Cleaning up / removing...
   2:zsh-5.8-9.el9                    ################################# [100%]

9.1.4. Aktualizacje pakietów

Aktualizacji pakietu możemy dokonać na dwa sposoby, pierwszym z nich jest aktualizacja przy użyciu opcji -U, jeśli podany pakiet nie istnieje w systemie to zostanie on automatycznie zainstalowany. Opcji -U używa się wraz innymi opcjami instalacji -v oraz -h. Spróbujemy zaktualizować naszą powłokę zsh.

[user@server1 ~]$ sudo rpm -Uvh /mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm 
[sudo] password for user: 
Verifying...                          ################################# [100%]
Preparing...                          ################################# [100%]
	package zsh-5.8-9.el9.x86_64 is already installed

W tym przykładzie użyłem tej samego pakietu do aktualizacji, co wygerowało monit o tym, że taki pakiet jest już zainstalowany w systemie.

Inną opcją aktualizacji jest odświerzenie (ang. freshening), ta metoda wymaga aby pakiet w starszej wersji był już zainstalowany w systemie. Odświerzenie wymaga opcji -F i używa się jej wraz z pozostałymi opcjami wymienionymi podczas instalacji oraz aktualizacji.

[user@server1 ~]$ sudo rpm -Fvh /mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm 
[sudo] password for user: 

Polecenie nie zrobiło nic poniważ podałem ten sam pakiet, które jest już zainstalowany.

9.1.5. Usuwanie pakietów

Do usuwania pakietów z systemu służy opcja -e, używa się jej wraz z opcją -v, aby polecenie zróciło chociaż jakiś komunikat, po usunięciu pakietu. Poniżej znajdują się polecenia pokazujące usunięcie pakietu z systemu.

[user@server1 ~]$ rpm -q zsh
zsh-5.8-9.el9.x86_64
[user@server1 ~]$ sudo rpm -ev zsh
Preparing packages...
zsh-5.8-9.el9.x86_64
[user@server1 ~]$ rpm -q zsh
package zsh is not installed

Na podstawie informacji zwracanej przez ostatnie polecenie możemy wywnioskować, iż pakiet został usunięty.

9.1.6. Pozostałe czynności wykonywane na pakietach.

Do pozostałych czynności jakie możemy wykonać zaliczamy nadpisanie pakietu. Jeśli podejrzewany, że mogły wystąpić błędy podczas instalacji możemy nadpisać pakiety dodając do opcji instalacji długą opcję --replacepkgs.

[user@server1 ~]$ sudo rpm -ivh --replacepkgs /mnt/BaseOS/Packages/zsh-5.8-9.el9.x86_64.rpm 
[sudo] password for user: 
Verifying...                          ################################# [100%]
Preparing...                          ################################# [100%]
Updating / installing...
   1:zsh-5.8-9.el9                    ################################# [100%]

Wynik działania nadpisania jest identyczny jak w przypadku instalacji.

Czasami może się zdarzyć taka potrzeba, aby wyodrębnić jeden z plików znajdujących wewnątrz pakietu, możemy wówczas taki pakiet rozpakować do katalogu, a następnie przekopiować interesujące nas pliki. Załóżmy że potrzebujemy pliku /etc/bashrc. Za pomocą opcji -qf możemy ustalić z jakiego pakietu pochodzi.

[user@server1 ~]$ rpm -qf /etc/bashrc
setup-2.13.7-6.el9.noarch

Pobieramy plik z internetu lub możemy załadować go z płyty. Do polecenia rpm2cpio podajemy ścieżkę do pliku, następnie wyjście polecenia przekierowujemy za pomocą potoku do polecenia cpio z następującymi opcjami -i (wypakuj), -m (zachowaj oryginalny czas modyfikacji plików), -d (utwórz katalogi poprzedzające pliki). Takie wypakowanie nalepiej jest zrobić w odrębnym katalogu na przykład w /tmp.

[user@server1 ~]$ mkdir /tmp/setup.rpm
[user@server1 ~]$ cd /tmp/setup.rpm/
[user@server1 setup.rpm]$ rpm2cpio /mnt/BaseOS/Packages/setup-2.13.7-6.el9.noarch.rpm | cpio -imd
1430 blocks
[user@server1 setup.rpm]$ ls
etc  run  usr
[user@server1 setup.rpm]$ ls -l etc/bashrc 
-rw-r--r--. 1 user user 3019 Jun 23  2020 etc/bashrc

Teraz możemy przekopować potrzebny nam pliki.

Ćwiczenie 1: Instalacja pakietu zsh

Na maszynie server1, jak użytkownik z mozliwością podniesienia uprawnień zamontuj płytę/obraz płyty w systemie. Za pomocą polecenia rpm wyświetl informacje (podstawowe) o pakiecie zsh, sprawdź jego poprawność oraz wiarygodność, a następnie zainstaluj go w systemie. Na koniec sprawdź czy występują jakieś róznice między plikami zainstalowanymi, a plikami oryginalnymi występującymi w pakiecie.

Ćwiczenie 2: Pobieranie informacji oraz usunięcie pakietu

Na maszynie server1, jak użytkownik z mozliwością podniesienia uprawnień zamontuj płytę/obraz płyty w systemie. Za pomocą polecenia rpm sprawdzić czy zainstalowany jest pakiet setup i wyświetl jego pliki konfiguracyjne. Wyświetl informacje na temat pakietu zlib-devel znajdującego się na obrazie płyty. Zainstaluj ponownie pakiet zsh a następnie go usuń.

Podsumowanie

W tym rozdziale zapoznaliśmy się z pojęciem pakietu oprogramowania oraz z podstawowymi metodami na zarządzanie nim w RHEL w praktyce. Dowiedzieliśmy się jak instalować, usuwać oraz wydobywać informacje o i z pakietów. W następnym rodziale zajmiemy się bardziej zaawansowanym zarządzeniem pakietami przy użyciu polecenia dnf.

10. Zaawansowane zarządzanie pakietami.

W poprzednim rozdziale zapoznaliśmy się z podstawą zarządania pakietami jaka nie wątpliwie jest program rpm. Jego główną wadą jest to, iż nie rozwiązuje on zależności instalowanych pakietów. Z tego powodu potrzebne było lepsze narzędzie do obsługi oprogramowania w systemie, dlatego też twórcy RHEL wdrożyli program yum, który przez lata sprawdzał się postaci menedżera pakietów, a od wersji 8 Red Hat Enterprise Linux został on zastąpiony programem dnf, który ma nieco większe możliwości. Zanim przejdziemy do zarządzania pakietami, trochę teorii.

Dnf wprowadza do zarządzania pakietami takie pojęcia jak grupy pakietów - są tematyczne grupy aplikacji, którymi możemy wzbogacić nasz system. Powiedzmy, że potrzebujemy w systemie obsługi kart inteligentnych to zamiast szukać odpowiednich pakietów, możemy zainstalować wszystkie niezbędne programy podając poleceniu dnf nazwę grupy.

Wraz z dnf pojawiają się takie pojęcia jak moduł aplikacji oraz strumień aplikacji. Moduł aplikacji, to podobnie do grupy jest zbiór pakietów, ale odpowiedzialnych za jedną aplikację. W module zawarte są wszystkie pakiety odpowiedzialne za działanie aplikacji, poza zależnościami mogą znajdować się tam także biblioteki, generalnie całe środowisko. Moduły posiadają swoje strumienie, czyli konkretne wersje pakietów aplikacji, które dostarczają. W systemie może być zainstalowany tylko jeden strumień danej aplikacji. Między strumieniami można się przełączać. będziemy to omawiać pod koniec tego rozdziału. Moduły w strumieniach posiadają jeszcze profile. Profile są to zbiory pakietów, programów określanych przez moduł zbudowane z myślą o przeznaczeniu aplikacji. Na przykład systemy bazodanowe mogą posiadać pakiety przeznaczone dla serwera oraz dla klienta. Jeśli baza danych znajduje się na innym serwerze niż aplikacja, która z niej korzysta, to w systemie aplikacji nie ma sensu instalować wszystkich pakietów związanych z cała bazą wystarczą pakiety, które będą udostępniać tylko i wyłącznie możliwość połączenia się z bazą.

RHEL w wersji 8 dzieli również repozytorium (bazę pakietów) na dwie części. Piewsza część BaseOS, zawiera jak sama nazwa wskazuje pakiety bazowe, które zawierają większość pakietów niezbędnych do działania systemu operacyjnego. Druga część AppStream zawiera wszystkie dodatkowe aplikacje. Tego rodzaju podział został wprowadzony z względu na to, że w momencie przezprowadzania aktualizacji systemu, aktualizowane było również zainstalowane na nim oprogramowanie co mogło nie być porządane i wprowadzać nie potrzebne zamieszanie lub destabilizować cały systemu.

Kolejną rzeczą wartą wspomnienia są repozytoria. Nasze systemy, których używamy do wypróbowywania przykładów oraz do rozwiązywania ćwiczeń, nie są zarejestrowane na naszych kontach Red Hat. Rejestracja systemu daje możliwość aktualizacji pakietów zawartych na płytach oraz instalowania ich bezpośrednio z sieci. Oprócz repozytoriów firmy Red Hat mamy rownież inne repozytoria dostępne w sieci. Takie jak na przykład RPM Fusion. Bez tych repozytoriów korzystanie z RHEL 9 w codziennym użyciu na desktopie nie miało by sensu. Przynajmniej w moim wypadku ;). Konfigurację repozytoriów przeprowadza się zapisując je w pliku w katalogu /etc/yum.repos.d, w jednym pliku może znajdować się kilka repozytoriów, sposób organizacji zależy od nas.

Na koniec, kilka spraw organizacyjnych. W RHEL 9 poki co brakuje modułów. W tym odnośniku znajduje się dowód, że na moduły przyjdzie na poczekać do wersji 9.1. https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/managing_software_with_the_dnf_tool/index#con_modules_assembly_distribution-of-content-in-rhel-9 Tak więc wszystkie przykłady jakie będę wykonywać w tym rodziale będą realizowane za pomocą Red Hat Enterprise Linux w wersji 8.6. Po zarejestrowaniu się możemy pobrać RHEL aż do wersji 8.0. Także możemy stworzyć środowisko maszyny server1 na podstawie RHEL 8.

10.1. Konfiguracja repozytorium dnf

Jak już wspomniałem repozytorium konfigurujemy za pomocą plików złożonych w katalogu /etc/yum.repos.d. Na początku jednak musimy dodać płytę do pliku /etc/fstab. Opisywałem ten proces na początku 9 rozdziału o tutaj. Dlatego też w przykładach przyjmę iż płyta jest montowana w podkatalogu /mnt. Jak pamiętamy z poprzedniego rozdziału na płycie znajdują się dwa katalogi z pakietami: BaseOS/Packages oraz AppStream/Packages. W konfiguracji podajemy tylko te pierwsze człony tych ścieżek, ponieważ w niej wskazujemy katalog bazowy. Konfiguracja każdego z repozytorium wymaga minimum 4 wierszy. Poniższy przykład przedstawia zawartość pliku konfiguracjynego repozytoriów z płyty instalacjynej RHEL 8.

[BaseOS]
name=BaseOS
baseurl=file:///mnt/BaseOS
gpgcheck=0

[AppStream]
name=AppStream
baseurl=file:///mnt/AppStream
gpgcheck=0

W nawiasach kwadratowych znajdują się unikatowe identyfikatory repozytoriów ([BaseOS]), następnie dyrektywa name zawiera ich opisy w przypadku środowisk testowych nie musimy się wysilać z opisami czy sprawdzać autentyczności pakietów przy użyciu GPG (gpgcheck=0). Wyłączenie sprawdzania pakietów usprawni proces ich instalacji, w przeciwym razie za każdą czynnością opartą o repozytorium otrzymywalibyśmy monit. Istotną funkcję pełni tym przypadku dyrektywa baseurl, która wskazuje na miejsce przechowywania pakietów, tym przypadku jest katalog zatem protokołem poprzedzającym ścieżkę będzie file, jak w każdym adresie URL następuje ://, a następnie (w tym przypadku) bezwzględna ścieżka do katalogu.

Odnośnie egzaminu:
Umiejętnośc konfigurowania repozytoriów yum/dnf z wykorzystaniem URL może być istotna do wykonania pewnych zadań egzaminacyjnych. Konstruując URL możemy użyć takich protokołów jak FTP, HTTP czy HTTPS (zapis w URL zawsze małymi literami) po czym następuje dwukropek z dwoma slashami :// a następnie adres repozytorium.

Po zdefiniowaniu repozytoriów zapisujemy zmiany w pliku. Aby zweryfikować poprawność konfiguracji wydajemy polecenie, które odświerza pamięć podręczną pakietów.

[user@rhel8-vm1 ~]$ sudo dnf makecache
[sudo] password for user: 
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered with an entitlement server. You can use 
subscription-manager to register.
BaseOS                                              1.6 MB/s | 2.7 kB     00:00    
AppStream                                           2.3 MB/s | 2.8 kB     00:00 
Metadata cache created.

Metadane zostały załadowane do pamięci podręcznej. Za pomocą poniższego polecenia możemy wyświetlić informacje na temat zdefiniowanych w naszym systemie repozytoriów.

[user@rhel8-vm1 mnt]$ sudo dnf repoinfo -v
...
DNF version: 4.7.0
cachedir: /var/cache/dnf
Ostatnio sprawdzono ważność metadanych: 0:00:20 temu w dniu nie, 10 lip 2022, 22:51:36.
Identyfikator repozytorium        : AppStream
Nazwa repozytorium                : AppStream
Wersja repozytorium               : 1650471026
Aktualizacje repozytorium         : śro, 20 kwi 2022, 18:10:27
Pakiety repozytorium              : 6 609
Dostępne pakiety repozytorium     : 5 345
Rozmiar repozytorium              : 8.6 G
Podstawowy adres URL repozytorium : file:///mnt/AppStream
Wygaszenie repozytorium           : 172 800 s (ostatnio: nie, 10 lip 2022, 22:51:36)
Nazwa pliku repozytorium          : /etc/yum.repos.d/local.repo

Identyfikator repozytorium        : BaseOS
Nazwa repozytorium                : BaseOS
Wersja repozytorium               : 1650471057
Aktualizacje repozytorium         : śro, 20 kwi 2022, 18:10:59
Pakiety repozytorium              : 1 714
Dostępne pakiety repozytorium     : 1 712
Rozmiar repozytorium              : 1.3 G
Podstawowy adres URL repozytorium : file:///mnt/BaseOS
Wygaszenie repozytorium           : 172 800 s (ostatnio: nie, 10 lip 2022, 22:51:34)
Nazwa pliku repozytorium          : /etc/yum.repos.d/local.repo
Razem pakietów: 8 323

Tak zdefiniowanych repozytoriów będziemy używać w przykładach oraz do rozwiązywania zadań w ćwiczeniach.

10.2. Zarządzanie pojedynczymi pakietami

Program dnf jest stanie wykonywać identyczne czynności jak polecenie rpm. Z racji tego, iż skonfigurowaliśmy wcześniej repozytorium nie musimy podawać żadnych ścieżek wystarczy nazwa pakietu, a ją możemy poznać wyświetlając listy pakietów.

10.2.1. Listy pakietów

Z pomocą poniższego polecenia możemy wyświetlić listę wszystkich pakietów. Domyślnie lista pakietów jest podzielona na dwie części: zainstalowane oraz dostępne pakiety.

[user@rhel8-vm1 ~]$ sudo dnf list
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. 
You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 9:22:00 temu w dniu nie, 10 lip 2022, 22:51:36.
Zainstalowane pakiety
GConf2.x86_64                  3.2.6-22.el8                           @AppStream
ModemManager.x86_64            1.18.2-1.el8                           @anaconda 
ModemManager-glib.x86_64       1.18.2-1.el8                           @anaconda 
NetworkManager.x86_64          1:1.36.0-4.el8                         @anaconda 
NetworkManager-adsl.x86_64     1:1.36.0-4.el8                         @anaconda 
NetworkManager-bluetooth.x86_64
                               1:1.36.0-4.el8                         @anaconda 
NetworkManager-config-server.noarch
                               1:1.36.0-4.el8                         @anaconda 
NetworkManager-libnm.x86_64    1:1.36.0-4.el8                         @anaconda 
NetworkManager-team.x86_64     1:1.36.0-4.el8                         @anaconda 
NetworkManager-tui.x86_64      1:1.36.0-4.el8                         @anaconda 
NetworkManager-wifi.x86_64     1:1.36.0-4.el8                         @anaconda 
NetworkManager-wwan.x86_64     1:1.36.0-4.el8                         @anaconda
...
Dostępne pakiety
CUnit.i686                     2.1.3-17.el8                           AppStream 
CUnit.x86_64                   2.1.3-17.el8                           AppStream 
GConf2.i686                    3.2.6-22.el8                           AppStream 
HdrHistogram.noarch            2.1.11-3.module+el8.5.0+12798+cbad756b AppStream 
HdrHistogram-javadoc.noarch    2.1.11-3.module+el8.5.0+12798+cbad756b AppStream 
HdrHistogram_c.i686            0.9.13-2.el8                           AppStream 
HdrHistogram_c.x86_64          0.9.13-2.el8                           AppStream 
Judy.x86_64                    1.0.5-18.module+el8+2765+cfa4f87b      AppStream 
LibRaw.i686                    0.19.5-3.el8                           AppStream 
LibRaw.x86_64                  0.19.5-3.el8                           AppStream 
ModemManager-glib.i686         1.18.2-1.el8                           BaseOS    
NetworkManager-cloud-setup.x86_64
                               1:1.36.0-4.el8                         AppStream 
NetworkManager-config-connectivity-redhat.noarch
                               1:1.36.0-4.el8                         BaseOS    
NetworkManager-dispatcher-routing-rules.noarch

Ilość wyświetlonych pakietów możemy zmniejszyć za pomocą odpowiednich opcji, wyświetlając tylko listę pakietów zainstalowanych (--installed) lub listę pakietów dostępnych (--available).

[user@rhel8-vm1 ~]$ sudo dnf list --installed
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. 
You can use subscription-manager to register.

Zainstalowane pakiety
GConf2.x86_64                              3.2.6-22.el8               @AppStream
ModemManager.x86_64                        1.18.2-1.el8               @anaconda 
ModemManager-glib.x86_64                   1.18.2-1.el8               @anaconda 
NetworkManager.x86_64                      1:1.36.0-4.el8             @anaconda 
NetworkManager-adsl.x86_64                 1:1.36.0-4.el8             @anaconda 
NetworkManager-bluetooth.x86_64            1:1.36.0-4.el8             @anaconda 
NetworkManager-config-server.noarch        1:1.36.0-4.el8             @anaconda 
NetworkManager-libnm.x86_64                1:1.36.0-4.el8             @anaconda 

[user@rhel8-vm1 ~]$ sudo dnf list --available
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. 
You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 9:33:18 temu w dniu nie, 10 lip 2022, 22:51:36.
Dostępne pakiety
CUnit.i686                     2.1.3-17.el8                            AppStream
CUnit.x86_64                   2.1.3-17.el8                            AppStream
GConf2.i686                    3.2.6-22.el8                            AppStream
HdrHistogram.noarch            2.1.11-3.module+el8.5.0+12798+cbad756b  AppStream
HdrHistogram-javadoc.noarch    2.1.11-3.module+el8.5.0+12798+cbad756b  AppStream
HdrHistogram_c.i686            0.9.13-2.el8                            AppStream
HdrHistogram_c.x86_64          0.9.13-2.el8                            AppStream
Judy.x86_64                    1.0.5-18.module+el8+2765+cfa4f87b       AppStream
LibRaw.i686                    0.19.5-3.el8                            AppStream
LibRaw.x86_64                  0.19.5-3.el8                            AppStream
ModemManager-glib.i686         1.18.2-1.el8                            BaseOS   

Listy są podzielone na trzy kolumny w pierwsze z nich znajduje się nazwa pakietu, w drugie wersja, a w trzeciej nazwa repozytorium, które zawiera ten pakiet lub z które został on zainstalowany. W przypadku wartości @anaconda, są to pakiety zainstalowane podczas instalacji systemu, w przypadku pakietów instalowanych za pomocą polecenia rpm w trzeciej kolumnie będzie widnieć napis @System.

Przydatną opcją jeśli korzystamy z repozytoriów sieciowych może być updates wydawane po podpoleceniu list. W dnf opcje często posiadają formę podpoleceń, natomiast to co my możemy uważać za opcję jest modyfikatorem. Podpolecenie updates wyświetla pakiety możliwe do zaktualizowania.

10.2.2. Zapytania do repozytorium

Dnf w przeciwieństwie do rpm pozwala nam przeszukiwać repozytorium pod względem występowania na przykład pakietu lub możemy podać ścieżkę do pliku znajdującego się w systemie a program zwrócić nam informacje z jakiego pakietu pochodzi.

[user@rhel8-vm1 ~]$ sudo dnf repoquery zsh
[sudo] hasło użytkownika user: 

Ostatnio sprawdzono ważność metadanych: 0:03:42 temu w dniu pon, 11 lip 2022, 08:36:18.
zsh-0:5.5.1-9.el8.x86_64

[user@rhel8-vm1 ~]$ sudo dnf repoquery /etc/bashrc
Ostatnio sprawdzono ważność metadanych: 0:04:02 temu w dniu pon, 11 lip 2022, 08:36:18.
setup-0:2.12.2-6.el8.noarch

Podpolecenie repoquery pozwala na sprecyzowanie repozytorium, do którego będą kierowane zapytania, za pomocą opcji --repo.

[user@rhel8-vm1 ~]$ sudo dnf repoquery --repo BaseOS zsh
Ostatnio sprawdzono ważność metadanych: 0:16:51 temu w dniu pon, 11 lip 2022, 08:36:18.
zsh-0:5.5.1-9.el8.x86_64

Podpolecenie repoquery posiada całą masę modyfikatorów dzięki nim możemy na przykład sprawdzić zależności pakietów. Aby ujawnić listę modyfikatorów możemy użyć podpolecenia repoquery wraz z --help.

[user@rhel8-vm1 ~]$ sudo dnf repoquery --help
Repoquery command-specific options:
  -a, --all             Odpytuje wszystkie pakiety (skrót do „repoquery '*'”
                        lub repoquery bez parametru)
  --show-duplicates     Odpytuje wszystkie wersje pakietów (domyślnie)
  --arch [arch], --archlist [arch]
                        wyświetla tylko wyniki dla tej ARCHITEKTURY
  -f FILE [FILE ...], --file FILE [FILE ...]
                        wyświetla tylko wyniki posiadające PLIK
...

10.2.3. Pobieranie informacji o pakietach

Polecenie dnf pozwala uzyskać informacje na temat pakietu. Za pomocą podpolecenia info możemy pobrać informacje o na przykład wersji, numerze wydania, rozmiarze czy repozytorium w jakim jest przechowywane.

[user@rhel8-vm1 ~]$ sudo dnf info php
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 0:40:02 temu w dniu pon, 11 lip 2022, 08:36:18.
Dostępne pakiety
Nazwa        : php
Wersja       : 7.2.24
Wydanie      : 1.module+el8.2.0+4601+7c76a223
Architektura : x86_64
Rozmiar      : 1.5 M
Źródło       : php-7.2.24-1.module+el8.2.0+4601+7c76a223.src.rpm
Repozytorium : AppStream
Podsumowanie : PHP scripting language for creating dynamic web sites
Adres URL    : http://www.php.net/
Licencja     : PHP and Zend and BSD and MIT and ASL 1.0
Opis         : PHP is an HTML-embedded scripting language. PHP attempts to make
             : it easy for developers to write dynamically generated web pages.
             : PHP also offers built-in database integration for several
             : commercial and non-commercial database management systems, so
             : writing a database-enabled webpage with PHP is fairly simple. The
             : most common use of PHP coding is probably as a replacement for
             : CGI scripts.
             : 
             : The php package contains the module (often referred to as
             : mod_php) which adds support for the PHP language to Apache HTTP
             : Server.

Teraz kiedy wiemy jak możemy uzyskać informacje na temat interesujących nas pakietów możemy przejść do podstawowych czynności z nimi związanych, czyli: instalacji, aktualizacji oraz usuwania.

10.2.4. Instalacja pakietów

Instalacja pakietów jest banalnie prosta w przypadku narzędzia dnf wymaga ona użycia podpolecenia install a następnie podania nazwy pakietu.

[user@rhel8-vm1 ~]$ sudo dnf install mc
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:07:02 temu w dniu pon, 11 lip 2022, 08:36:18.
Rozwiązano zależności.
================================================================================
 Pakiet      Architektura    Wersja                    Repozytorium       Rozm.
================================================================================
Instalowanie:
 mc          x86_64          1:4.8.19-9.el8            AppStream          1.9 M

Podsumowanie transakcji
================================================================================
Instalacja  1 pakiet

Całkowity rozmiar: 1.9 M
Rozmiar po zainstalowaniu: 6.8 M
W porządku? [t/N]: 
...

Program wyświetli w tabeli wszystkie instalowane pakiety, następnie całkowity rozmiar pobieranych pakietów oraz ilość miejsca na dysku zabranego na potrzeby instalacji. Po zapoznaniu się z tym możemy potwierdzić za pomocą t (w polskiej wersji językowej) lub y lub przerwać na przykład naciskając Enter, ponieważ opcja N (nie, no) jest domyślna. Po zatwierdzeniu pakietów rozpocznie się ich pobieranie oraz instalacja.

...
Pobieranie pakietów:
Wykonywanie sprawdzania transakcji
Pomyślnie ukończono sprawdzanie transakcji.
Wykonywanie testu transakcji
Pomyślnie ukończono test transakcji.
Wykonywanie transakcji
  Przygotowywanie                :                                          1/1 
  Instalowanie                   : mc-1:4.8.19-9.el8.x86_64                 1/1 
  Wykonywanie skryptu            : mc-1:4.8.19-9.el8.x86_64                 1/1 
  Sprawdzanie                    : mc-1:4.8.19-9.el8.x86_64                 1/1 
Installed products updated.

Zainstalowano:
  mc-1:4.8.19-9.el8.x86_64                                                      

Ukończono.
[user@rhel8-vm1 ~]$ sudo dnf list --installed mc
[sudo] hasło użytkownika user: 

Zainstalowane pakiety
mc.x86_64                       1:4.8.19-9.el8                        @AppStream

Dnf daje nam możliwość przeprowadzanie lokalnej instalacji podając ścieżkę do pliku .rpm. Ta czynność wymaga użycia podpolecenia localinstall.

[user@rhel8-vm1 ~]$ sudo dnf localinstall /mnt/AppStream/Packages/dcraw-9.27.0-9.el8.x86_64.rpm 
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:23:58 temu w dniu pon, 11 lip 2022, 08:36:18.
Rozwiązano zależności.
================================================================================
 Pakiet        Architektura   Wersja                 Repozytorium         Rozm.
================================================================================
Instalowanie:
 dcraw         x86_64         9.27.0-9.el8           @commandline         267 k

Podsumowanie transakcji
================================================================================
Instalacja  1 pakiet

Całkowity rozmiar: 267 k
Rozmiar po zainstalowaniu: 548 k
W porządku? [t/N]: t 
Pobieranie pakietów:
Wykonywanie sprawdzania transakcji
Pomyślnie ukończono sprawdzanie transakcji.
Wykonywanie testu transakcji
Pomyślnie ukończono test transakcji.
Wykonywanie transakcji
  Przygotowywanie                :                                          1/1 
  Instalowanie                   : dcraw-9.27.0-9.el8.x86_64                1/1 
  Wykonywanie skryptu            : dcraw-9.27.0-9.el8.x86_64                1/1 
  Sprawdzanie                    : dcraw-9.27.0-9.el8.x86_64                1/1 
Installed products updated.

Zainstalowano:
  dcraw-9.27.0-9.el8.x86_64                                                     

Ukończono.

10.2.5. Aktualizacje pakietów

Aktualizacja pakietów za pomocą dnf wymaga użycia podpolecenia update. Aktualizacje pojedynczych pakietów przeprowadzamy podając ich nazwę po podpoleceniu. Ta funkcjonalność wymaga jednak aby pakiet był już zainstalowany w systemie. Jeśli wydamy to podpolecenie bez żadnego argumentu polecenie sprawdzi wszystkie zainstalowane pakiety w systemie, czy występują dla nich jakieś aktualizacje następnie spyta czy je zainstalować.

[user@rhel8-vm1 ~]$ sudo dnf update autofs
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:34:19 temu w dniu pon, 11 lip 2022, 08:36:18.
Pakiet autofs jest dostępny, ale nie jest zainstalowany.
Brak wyników dla parametru: autofs
Błąd: Brak pakietów oznaczonych do aktualizacji.

[user@rhel8-vm1 ~]$ sudo dnf update
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:38:26 temu w dniu pon, 11 lip 2022, 08:36:18.
Rozwiązano zależności.
Nie ma nic do zrobienia.
Ukończono.

10.2.6. Usuwanie pakietów

Usuwanie pakietów w przypadku dnf wymaga użycia podpolecenia remove. Polecenie usunie zainstalowany pakiet z systemu wraz ze wszystkimi jego plikami.

[user@rhel8-vm1 ~]$ sudo dnf remove mc
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Rozwiązano zależności.
================================================================================
 Pakiet      Architektura    Wersja                   Repozytorium        Rozm.
================================================================================
Usuwanie:
 mc          x86_64          1:4.8.19-9.el8           @AppStream          6.8 M

Podsumowanie transakcji
================================================================================
Usunięcie  1 pakiet

Zwolnione miejsce: 6.8 M
W porządku? [t/N]: t
Wykonywanie sprawdzania transakcji
Pomyślnie ukończono sprawdzanie transakcji.
Wykonywanie testu transakcji
Pomyślnie ukończono test transakcji.
Wykonywanie transakcji
  Przygotowywanie                :                                          1/1 
  Usuwanie                       : mc-1:4.8.19-9.el8.x86_64                 1/1 
  Wykonywanie skryptu            : mc-1:4.8.19-9.el8.x86_64                 1/1 
  Sprawdzanie                    : mc-1:4.8.19-9.el8.x86_64                 1/1 
Installed products updated.

Usunięto:
  mc-1:4.8.19-9.el8.x86_64                                                      

Ukończono.

W przypadku instalowania pakietu polecenie przekazywało nam informacje o użytym na potrzeby instalacji miejscu na dysku. Tak teraz informuje o jego zwolnieniu.

10.2.7. Więcej informacji na temat pakietów

Innym sposobem na uzyskanie informacji o tym jaki pakiet zawiera określony plik jest użycie podpolecenia provides. Sprawdza ono występowanie plików w pakietach zainstalowanych w systemie jak i tych możliwych do instalacji.

[user@rhel8-vm1 ~]$ sudo dnf provides /etc/passwd
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 2:04:16 temu w dniu pon, 11 lip 2022, 08:36:18.
setup-2.12.2-6.el8.noarch : A set of system configuration and setup files
Repozytorium       : @System
Dopasowano z:
Nazwa pliku : /etc/passwd

setup-2.12.2-6.el8.noarch : A set of system configuration and setup files
Repozytorium       : BaseOS
Dopasowano z:
Nazwa pliku : /etc/passwd

Po za opisaną metodą na wyszukiwanie informacji o pakietach, do dyspozycji posiadamy jeszcze podpolecenie search, które wyszukuje słów kluczowych w podsumowaniu opisu pakietu oraz jego nazwie.

[user@rhel8-vm1 ~]$ sudo dnf search setup
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 2:20:48 temu w dniu pon, 11 lip 2022, 08:36:18.
==================== Dopasowano Nazwa i Podsumowanie: setup ====================
setup.noarch : A set of system configuration and setup files
cryptsetup-libs.x86_64 : Cryptsetup shared library
cryptsetup-libs.i686 : Cryptsetup shared library
ibus-setup.noarch : IBus setup utility
initial-setup-gui.x86_64 : Graphical user interface for the initial-setup
                         : utility
...
=========================== Dopasowano Nazwa: setup ============================
NetworkManager-cloud-setup.x86_64 : Automatically configure NetworkManager in
                                  : cloud
cryptsetup.x86_64 : A utility for setting up encrypted disks
cryptsetup-devel.i686 : Headers and libraries for using encrypted file systems
cryptsetup-devel.x86_64 : Headers and libraries for using encrypted file systems
cryptsetup-reencrypt.x86_64 : A utility for offline reencryption of LUKS
                            : encrypted disks.
...
======================== Dopasowano Podsumowanie: setup ========================
rhsm-gtk.x86_64 : GTK+ widgets used by subscription-manager-gui and
                : initial_setup

Jak możemy zauważyć polecenie wyświetla znalezione informacje w trzech grupach.

10.3. Grupy pakietów

Pośród innych sposobów na zarządzanie pakietami możemy wymienić grupy pakietów. Są to zestawy pakietów odpowiedzialne za zapewnienie systemowi operacyjnemu pewnej funkcjonalności np. obsługi wirtualizacji i maszyn wirtualnych. Grupy pakietów obsługuje się identycznie jak pakiety indywidualne, możemy je wyświetlić, zainstalować, zaktualizować, wyświetlić informacje o nich oraz usunąć. Przjdziemy sobie przez te wszystkie zagadnienia.

10.3.1. Listy grup

Do obsługi grupy służy podpolecenie group, które posiada kolejne znane nam już z obsługi pojedynczych pakietów podpolecenia takie jak np list, którego zadaniem jest wyświetlenie grup pakietów.

[user@rhel8-vm1 ~]$ sudo dnf group list
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 2:39:54 temu w dniu pon, 11 lip 2022, 08:36:18.
Dostępne grupy środowisk:
   Server
   Minimalna instalacja
   Workstation
   Custom Operating System
   Gospodarz wirtualizacji
Zainstalowane grupy środowisk:
   Serwer z graficznym interfejsem użytkownika
Zainstalowane grupy:
   Container Management
   Headless Management
Dostępne grupy:
   Zgodność z przestarzałym systemem UNIX
   Narzędzia programistyczne
   .NET Core Development
   Graficzne narzędzia administracyjne
   Serwery sieciowe
   RPM Development Tools
   Obsługa aplikacji naukowych
   Narzędzia bezpieczeństwa
   Obsługa smart card
   Narzędzia systemowe

Jak możemy zawuważyć grupy dzielą się dwa rodzaje pierwszym z nich są grupy środowisk, drugim są zwykłe grupy. Z grupami mogliśmy się już spotkać podczas instalacji systemu, wybieraliśmy rodzaj instalacji Server with GUI. Podczas wyboru oprogramowania mogliśmy wybrać dowolną z nich do zainstalowania. Teraz za pomocą dnf możemy doinstalować dodatkowe grupy lub usunąć nie potrzebne. Podpolecenie list wyświetla również te obecnie zainstalowane oraz nie wyświetla wszystkich dostępnych w systemie grup. Wszystkie grupy będą wyświetlone jeśli uzupełnimy nasze polecenie o modyfikator --hidden. W ten sposób możemy wybrać znacznie węższą grupę pakietów. Możemy również używać modyfikatorów takich jak --installed lub --available

Po za taką długą listą możemy wyświetlić podsumowanie, które zwróci informacje o ilości dostępnych oraz zainstalowanych grup.

[user@rhel8-vm1 ~]$ sudo dnf group summary
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 3:08:10 temu w dniu pon, 11 lip 2022, 08:36:18.
Zainstalowane grupy: 2
Dostępne grupy: 10

10.3.2. Informacje o grupie

Za wyświetlenie informacji o grupie odpowiedzialne jest podpolecenie info, zwraca ono listę pakietów obowiązkowych, które muszą być zainstalowane, aby system został rozszerzony o funkcjonalność przypisaną grupie oraz listę pakietów opcjonalnych.

[user@rhel8-vm1 ~]$ sudo dnf group info "Platforma wirtualizacji"
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 2:58:21 temu w dniu pon, 11 lip 2022, 08:36:18.

Grupa: Platforma wirtualizacji
 Opis: Dostarcza interfejs do uzyskiwania dostępu i kontrolowania wirtualizowanych gości i kontenerów.
 Pakiety obowiązkowe:
   libvirt
   libvirt-client
   virt-who
 Pakiety opcjonalne:
   fence-virtd-libvirt
   fence-virtd-multicast
   fence-virtd-serial
   perl-Sys-Virt

10.3.3. Instalacja grup

Instalacja grup odbywa się na tej samej zasadzie, z drobnym wyjątkiem. Otóż jeśli w systemie będą występować jakieś składniki grupy, to jeśli będzie taka możliwość zostaną one zaktualizowane. Instalacja grupy odbywa się za pomocą podpolecenia install podpolecenia group. Jako argument podajemy nazwę grupy.

[user@rhel8-vm1 ~]$ sudo dnf group install "Obsługa smart card"
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 3:37:36 temu w dniu pon, 11 lip 2022, 08:36:18.
Rozwiązano zależności.
================================================================================
 Pakiet               Architektura Wersja                 Repozytorium    Rozm.
================================================================================
Instalowanie pakietów grupy/modułu:
 esc                  x86_64       1.1.2-22.el8_4         AppStream       180 k
 pcsc-lite-ccid       x86_64       1.4.29-5.1.el8_4       BaseOS          316 k
Instalowanie zależności:
 opensc               x86_64       0.20.0-4.el8           BaseOS          1.3 M
 pcsc-lite            x86_64       1.9.5-1.el8            BaseOS          110 k
 pcsc-lite-libs       x86_64       1.9.5-1.el8            BaseOS           43 k
Instalowanie grup:
 Smart Card Support                                                            

Podsumowanie transakcji
================================================================================
Instalacja  5 pakietów

Całkowity rozmiar: 1.9 M
Rozmiar po zainstalowaniu: 6.0 M
W porządku? [t/N]: t
Pobieranie pakietów:
Wykonywanie sprawdzania transakcji
Pomyślnie ukończono sprawdzanie transakcji.
Wykonywanie testu transakcji
Pomyślnie ukończono test transakcji.
Wykonywanie transakcji
  Przygotowywanie                :                                          1/1 
  Instalowanie                   : pcsc-lite-libs-1.9.5-1.el8.x86_64        1/5 
  Wykonywanie skryptu            : pcsc-lite-libs-1.9.5-1.el8.x86_64        1/5 
  Instalowanie                   : pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   2/5 
  Wykonywanie skryptu            : pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   2/5 
  Instalowanie                   : pcsc-lite-1.9.5-1.el8.x86_64             3/5 
  Wykonywanie skryptu            : pcsc-lite-1.9.5-1.el8.x86_64             3/5 
  Instalowanie                   : opensc-0.20.0-4.el8.x86_64               4/5 
  Wykonywanie skryptu            : opensc-0.20.0-4.el8.x86_64               4/5 
  Instalowanie                   : esc-1.1.2-22.el8_4.x86_64                5/5 
  Wykonywanie skryptu            : esc-1.1.2-22.el8_4.x86_64                5/5 
  Sprawdzanie                    : opensc-0.20.0-4.el8.x86_64               1/5 
  Sprawdzanie                    : pcsc-lite-1.9.5-1.el8.x86_64             2/5 
  Sprawdzanie                    : pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   3/5 
  Sprawdzanie                    : pcsc-lite-libs-1.9.5-1.el8.x86_64        4/5 
  Sprawdzanie                    : esc-1.1.2-22.el8_4.x86_64                5/5 
Installed products updated.

Zainstalowano:
  esc-1.1.2-22.el8_4.x86_64            opensc-0.20.0-4.el8.x86_64               
  pcsc-lite-1.9.5-1.el8.x86_64         pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   
  pcsc-lite-libs-1.9.5-1.el8.x86_64   

Ukończono.

Za pomocą dnf zainstalowałem w grupę pakietów odpowiedzialną za obsługę kart inteligentych.

10.3.4. Aktualizacja grupy

Aktualizacja grupy różni się podaniem innego podpolecenia. Zamiast install podajemy update.

[user@rhel8-vm1 ~]$ sudo dnf group update "Obsługa smart card"
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 3:47:16 temu w dniu pon, 11 lip 2022, 08:36:18.
Rozwiązano zależności.
================================================================================
 Pakiet            Architektura     Wersja              Repozytorium      Rozm.
================================================================================
Aktualizowanie grup:
 Smart Card Support
                                                                               

Podsumowanie transakcji
================================================================================

W porządku? [t/N]: t
Ukończono.

10.3.5. Usuwanie grupy

Tak samo jak w przypadku pojedynczych pakietów, możemy usunąć i grupy. Do usunięcia grupy potrzebne będzie podpolecenie remove oraz nazwa grupy.

[user@rhel8-vm1 ~]$ sudo dnf group remove "Obsługa smart card"
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Rozwiązano zależności.
================================================================================
 Pakiet              Arch.       Wersja                   Repozytorium    Rozm.
================================================================================
Usuwanie:
 esc                 x86_64      1.1.2-22.el8_4           @AppStream      516 k
 pcsc-lite-ccid      x86_64      1.4.29-5.1.el8_4         @BaseOS         1.4 M
Usuwanie nieużywanych zależności:
 opensc              x86_64      0.20.0-4.el8             @BaseOS         3.8 M
 pcsc-lite           x86_64      1.9.5-1.el8              @BaseOS         218 k
 pcsc-lite-libs      x86_64      1.9.5-1.el8              @BaseOS          50 k
Usuwanie grup:
 Smart Card Support                                                            

Podsumowanie transakcji
================================================================================
Usunięcie  5 pakietów

Zwolnione miejsce: 6.0 M
W porządku? [t/N]: t
Wykonywanie sprawdzania transakcji
Pomyślnie ukończono sprawdzanie transakcji.
Wykonywanie testu transakcji
Pomyślnie ukończono test transakcji.
Wykonywanie transakcji
  Przygotowywanie                :                                          1/1 
  Wykonywanie skryptu            : esc-1.1.2-22.el8_4.x86_64                1/1 
  Usuwanie                       : esc-1.1.2-22.el8_4.x86_64                1/5 
  Usuwanie                       : opensc-0.20.0-4.el8.x86_64               2/5 
  Wykonywanie skryptu            : opensc-0.20.0-4.el8.x86_64               2/5 
  Wykonywanie skryptu            : pcsc-lite-1.9.5-1.el8.x86_64             3/5 
  Usuwanie                       : pcsc-lite-1.9.5-1.el8.x86_64             3/5 
  Wykonywanie skryptu            : pcsc-lite-1.9.5-1.el8.x86_64             3/5 
  Usuwanie                       : pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   4/5 
  Wykonywanie skryptu            : pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   4/5 
  Usuwanie                       : pcsc-lite-libs-1.9.5-1.el8.x86_64        5/5 
  Wykonywanie skryptu            : pcsc-lite-libs-1.9.5-1.el8.x86_64        5/5 
  Sprawdzanie                    : esc-1.1.2-22.el8_4.x86_64                1/5 
  Sprawdzanie                    : opensc-0.20.0-4.el8.x86_64               2/5 
  Sprawdzanie                    : pcsc-lite-1.9.5-1.el8.x86_64             3/5 
  Sprawdzanie                    : pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   4/5 
  Sprawdzanie                    : pcsc-lite-libs-1.9.5-1.el8.x86_64        5/5 
Installed products updated.

Usunięto:
  esc-1.1.2-22.el8_4.x86_64            opensc-0.20.0-4.el8.x86_64               
  pcsc-lite-1.9.5-1.el8.x86_64         pcsc-lite-ccid-1.4.29-5.1.el8_4.x86_64   
  pcsc-lite-libs-1.9.5-1.el8.x86_64   

Ukończono.

Po wykonaniu tej czynności grupa została usunięta z systemu.

10.4. Zarządzanie modułami pakietów

Moduły to bardziej rozległe pakiety zawierające nie tylko samą aplikacje ale także jej całe środowisko do działania. Moduły posiadają strumienie, które są odrębnym zestawem pakietów przygotowanym z myslą o konkretnej wersji dostarczanego przez moduł programu. Każdy z modułów posiada jeszcze profil, który określa podzbiór pakietów przygotowany z myślą o przeznaczeniu aplikacji. Aby skorzystać z pewnej jej funkcji nie trzeba instalować gigabajtów pakietów. Do obsługi modułów wykorzystuje się podpolecenie module, a pracę z nimi rozpoczeniemy od wyświetlenia listy modułów.

10.4.1. Wyświetlanie modułów

Do wyświetlania list modułów służy podpolecenie list. Na poniższym przykładzie znajduje się fragment listy dostępnych w lokalnym repozytorium modułów.

[user@rhel8-vm1 ~]$ sudo dnf module list
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 0:12:51 temu w dniu pon, 11 lip 2022, 12:31:38.
AppStream
Name                 Stream          Profiles Summary                                      
389-ds               1.4                      389 Directory Server (base)                  
ant                  1.10 [d]        common [d] Java build tool                              
container-tools      rhel8 [d][e]    common [d] Most recent (rolling) versions of podman, bui
                                                ldah, skopeo, runc, conmon, runc, conmon, CRI
                                                U, Udica, etc as well as dependencies such as
                                                container-selinux built and tested together,
                                                and updated as frequently as every 12 weeks.
...

Modułów dostępnych w systemie jest kilkadziesiąt. Wyświetlenie listy nie posiada zbyt poręcznego formatu, aby zmieszczać pełny listing tego polecenia. Tak jak w przypadku pozostałych elementów, możemy podać podpoleceniu list argument w postaci ogólnej nazwy np. php.

[user@rhel8-vm1 ~]$ sudo dnf module list php
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 0:17:20 temu w dniu pon, 11 lip 2022, 12:31:38.
AppStream
Name         Stream         Profiles                          Summary                      
php          7.2 [d]        common [d], devel, minimal        PHP scripting language       
php          7.3            common [d], devel, minimal        PHP scripting language       
php          7.4            common [d], devel, minimal        PHP scripting language       
php          8.0            common [d], devel, minimal        PHP scripting language       

Wskazówka: [d]omyślne, [e]włączone, [x]wyłączone, [i]zainstalowane

Jak możemy zauważyć na powyższym przykładzie do dyspozycji mamy kilka strumieni. Domyślnym strumieniem jest 7.2, a domyślnym profilem dla tych strumieni jest common. Domyślny strumień oraz profil zostanie wybrany w momencie gdy nie sprecyzujemy tego podczas instalacji. W ostatniej linii znajduje się legenda, litery w nawiasach kwadartowych zdradzają stan modułu zainstalowanego w systemie. Za pomocą modyfikatora --enabled możemy wyświetlić wszystkie włączone strumienie modułów.

10.4.2. Informacje na temat modułu

Za pomocą podpolecenia info możemy wyświetlić na przykład jakie pakiety zawiera moduł. Podczas wyświetlania tego typu informacji warto podać interesujący nas strumień, aby zmniejszyć ilość generowanych danych. Strumień podajemy wraz z nazwą modułu oddzielając go dwukropkiem.

[user@rhel8-vm1 ~]$ sudo dnf module info php:7.2
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 0:47:02 temu w dniu pon, 11 lip 2022, 12:31:38.
Name             : php
Stream           : 7.2 [d][a]
Version          : 8020020191108065827
Context          : 2c7ca891
Architecture     : x86_64
Profiles         : common [d], devel, minimal
Default profiles : common
Repo             : AppStream
Summary          : PHP scripting language
Description      : php 7.2 module
Requires         : httpd:[2.4]
                 : nginx:[]
                 : platform:[el8]
Artifacts        : apcu-panel-0:5.1.12-2.module+el8.1.0+3202+af5476b9.noarch
                 : libzip-0:1.5.1-2.module+el8.1.0+3202+af5476b9.src
                 : libzip-0:1.5.1-2.module+el8.1.0+3202+af5476b9.x86_64
                 : libzip-debuginfo-0:1.5.1-2.module+el8.1.0+3202+af5476b9.x86_64
                 : libzip-debugsource-0:1.5.1-2.module+el8.1.0+3202+af5476b9.x86_64
                 : libzip-devel-0:1.5.1-2.module+el8.1.0+3202+af5476b9.x86_64
                 : libzip-tools-0:1.5.1-2.module+el8.1.0+3202+af5476b9.x86_64
                 : libzip-tools-debuginfo-0:1.5.1-2.module+el8.1.0+3202+af5476b9.x86_64
                 : php-0:7.2.24-1.module+el8.2.0+4601+7c76a223.src
                 : php-0:7.2.24-1.module+el8.2.0+4601+7c76a223.x86_64
                 : php-bcmath-0:7.2.24-1.module+el8.2.0+4601+7c76a223.x86_64
...

Lista pakietów występuje w wierszu Artifacts.

10.4.3. Instalacja modułów

Instalacja modułów wygląda podobnie do instalacji takich elementów jak grupy czy pojedyncze pakiety, używamy podpolecenia install podpolecenia przeznaczonego do obsługi tego elementu. Podając moduł możemy równocześnie wybrać zarówno strumień jak i profil korzystając ze składni: moduł:strumień/profil. Dla przykładu zainstaluje moduł języka perl w domyślnym strumieniu oraz domyślnym profilu. Nie będe zmieszczał tutaj informacji zwracanej przez polecenie, ponieważ jest to moduł, który zawiera ponad 100 pakietów.

[user@rhel8-vm1 ~]$ sudo dnf module install perl

Za pomocą podpolecenia list sprawdzimy jak wygląda na liście zainstalowany przed chwilą w systemie moduł.

[user@rhel8-vm1 ~]$ sudo dnf module list perl
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:17:20 temu w dniu pon, 11 lip 2022, 12:31:38.
AppStream
Name    Stream         Profiles                  Summary                                   
perl    5.24           common [d], minimal       Practical Extraction and Report Language  
perl    5.26 [d][e]    common [d] [i], minimal   Practical Extraction and Report Language  
perl    5.30           common [d], minimal       Practical Extraction and Report Language  
perl    5.32           common [d], minimal       Practical Extraction and Report Language  

Wskazówka: [d]omyślne, [e]włączone, [x]wyłączone, [i]zainstalowane

Na powyższym przykładzie możemy zobaczyć, że po zainstalowaniu modułu perl z domyślnym strumieniem oraz domyślnym profilem. Strumień 5.26 został włączony a profil common został zainstalowany. Warto też dodać, że jeśli jakiś z komponentów był już zainstalowany to podczas instalacji modułu zostanie sprawdzony czy nie wymaga aktualizacji.

10.4.4. Aktualizacja modułu

Aktualizacji modułu, dokonuje tak samo jak aktualizacji grupy czy pojedynczego pakietu za pomocą podpolecenia update, tylko, że tym przypadku będzie to podpolecenie podpolecenia module.

[user@rhel8-vm1 ~]$ sudo dnf module update perl -y

W tym przypadku użyłem flagi -y, która automatycznie potwierdza wykonanie czynności. Dnf nie będzie nas o nic pytać.

10.4.5. Usuwanie modułu

Usuwanie modułów wymaga już dwóch czynności piersza klasyczna wykonywana również na innych elementach. Drugą czynnością jest przywrócenie modułu wykonywane za pomocą podpolecenia reset. Usuniemy teraz moduł perl.

[user@rhel8-vm1 ~]$ sudo dnf module remove perl -y

Wydałem powyższe polecenie a następnie sprawdziłem jak wygląda lista stumieni modułu perl za pomocą poniższego polecenia.

[user@rhel8-vm1 ~]$ sudo dnf module list perl
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:54:15 temu w dniu pon, 11 lip 2022, 12:31:38.
AppStream
Name     Stream          Profiles               Summary                                    
perl     5.24            common [d], minimal    Practical Extraction and Report Language   
perl     5.26 [d][e]     common [d], minimal    Practical Extraction and Report Language   
perl     5.30            common [d], minimal    Practical Extraction and Report Language   
perl     5.32            common [d], minimal    Practical Extraction and Report Language   

Wskazówka: [d]omyślne, [e]włączone, [x]wyłączone, [i]zainstalowane

Jak widzimy ten strumień jest nadal włączony mimo, iż domyślny profil został odinstalowany. Dlatego też musimy przywrócić moduł.

[user@rhel8-vm1 ~]$ sudo dnf module reset perl -y
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:58:36 temu w dniu pon, 11 lip 2022, 12:31:38.
Rozwiązano zależności.
===========================================================================================
 Pakiet               Architektura        Wersja                Repozytorium         Rozm.
===========================================================================================
Przywracanie modułów:
 perl                                                                                     

Podsumowanie transakcji
===========================================================================================

Ukończono.

[user@rhel8-vm1 ~]$ sudo dnf module list perl:5.26
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:59:21 temu w dniu pon, 11 lip 2022, 12:31:38.
AppStream
Name      Stream       Profiles                Summary                                     
perl      5.26 [d]     common [d], minimal     Practical Extraction and Report Language    

Wskazówka: [d]omyślne, [e]włączone, [x]wyłączone, [i]zainstalowane

Na powyższym przykładzie przywróciłem moduł i sprawdziłem jak wygląda jego status w systemie. Teraz strumień nie jest włączony.

10.4.6. Przełączanie się miedzy strumieniami

Załóżmy taką sytuacje, że musimy zmień wersję php na serwerze z najnowszej (8.0) na niższą. Aby to zrobić musimy przełączyć się między strumieniami. Niestety nie ma lepszej metody niż ta opisana tutaj.

Na początku musimy przeprowadzić klasyczne usunięcie modułu, tak jak we wcześniejszym podrozdziale, ale na początku wyświetlimy sobie listę modułów php.

[user@rhel8-vm1 ~]$ sudo dnf module list php
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 2:26:58 temu w dniu pon, 11 lip 2022, 12:31:38.
AppStream
Name        Stream        Profiles                             Summary                     
php         7.2 [d]       common [d], devel, minimal           PHP scripting language      
php         7.3           common [d], devel, minimal           PHP scripting language      
php         7.4           common [d], devel, minimal           PHP scripting language      
php         8.0 [e]       common [d] [i], devel, minimal       PHP scripting language      

Wskazówka: [d]omyślne, [e]włączone, [x]wyłączone, [i]zainstalowane

[user@rhel8-vm1 ~]$ sudo dnf module remove php:8.0 -y
[user@rhel8-vm1 ~]$ sudo dnf module reset php:8.0

Następnie instalujemy nowy strumień modułu z dodatkową opcją pozwalającą na usunięcie pakietów, aby rozwiązać zależności.

[user@rhel8-vm1 ~]$ sudo dnf module install php:7.4 --allowerasing -y

Przełączenie gotowe. Teraz możemy to zweryfikować. Wyświetlając jeszcze raz listę modułów.

[user@rhel8-vm1 ~]$ sudo dnf module list php
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 2:30:25 temu w dniu pon, 11 lip 2022, 12:31:38.
AppStream
Name        Stream        Profiles                             Summary                     
php         7.2 [d]       common [d], devel, minimal           PHP scripting language      
php         7.3           common [d], devel, minimal           PHP scripting language      
php         7.4 [e]       common [d] [i], devel, minimal       PHP scripting language      
php         8.0           common [d], devel, minimal           PHP scripting language      

Wskazówka: [d]omyślne, [e]włączone, [x]wyłączone, [i]zainstalowane

Ćwiczenie 1: Konfiguracja dostępu do repozytorium

Na maszynie server1 z RHEL 9 skonfiguruj dostęp do lokalnego repozytorium wykorzystując do tego płyte/obraz płyty z RHEL 9. Plik nazwij local.repo. Uwaga, repozytorium musi być dostępne odrazu po ponownym uruchomioniu maszyny.

Ćwiczenie 2: Instalacja i zarządzanie pojedynczymi pakietami

Na maszynie z RHEL 8 wykorzystując użytkownika z możliwością podniesienia uprawnień, użyj polecenia dnf do wyświetlenia oddzielnie pakietów możliwych do instalacji oraz pakietów zainstalowanych. Wyświetl informacje na temat pakietu httpd, pokaż jego zależności, następnie zainstaluj go, potwierdź tę czynność wyświetlając plik dziennika /var/log/dnf.log. na koniec usuń pakiet.

Ćwiczenie 3: Instalacja i zarządzanie grupami pakietów

Na maszynie z RHEL 8 wykorzystując użytkownika z możliwością podniesienia uprawnień, użyj polecenia dnf do wyświetlenia zainstalowanych i możliwych do zainstalowania grup. Zainstaluj grupę "Security Tools" i "Scientific Support". Potwierdź to wyświetlając plik dziennika narzędzia. Pokaż pakiety zawarte w grupie "Scientific Support", a następnie usuń tę grupę.

Ćwiczenie 4: Instalacja i zarządzanie modułami

Na maszynie z RHEL 8 wykorzystując użytkownika z możliwością podniesienia uprawnień, użyj polecenia dnf do wyświetlenia listy modułów w systemie. Zainstaluj php w domyślnym strumieniu o profilu rozwojowym. Zweryfikuj instalacje, następnie usuń moduł.

Ćwiczenie 5: Przełączanie się miedzy strumieniami.

Na maszynie z RHEL 8 wykorzystując użytkownika z możliwością podniesienia uprawnień, użyj polecenia dnf do zainstalowania modułu postgresql, w domyslnym strumieniu i domyślnym profilu. Następnie dokonaj przełączenia między strumieniami na starszą wersję o profilu klienta.

Podsumowanie

Tym rodziałem zakończyliśmy zarządzanie pakietami. Ten rodział pokazał nam jak zarządzać pakietami za pomocą bardziej zaawansowanego narzędzia jakim jest dnf. Nauczyliśmy się zarządać pojednyczymi pakietami, grupami oraz modułami. W następnym rodziale zajmiemy się uruchamianiem systemu operacyjnego.

11. Proces ładowania systemu, GRUB oraz jądro

Proces ładowania systemu następuje w momencie gdy naciskamy przycisk zasilania komputera. Pierwsza faza - faza oprogramowania układowego jest niezależna od stosowanego systemu operacyjnego. Jej zadaniem jest przeprowadzenie tzw. POST-u, czyli sprawdzenia wszystkich komponentów sprzętowych przed uruchomieniem komputera po to aby nadawał się on do stablinej pracy. Faza pierwsza przeprowadzana jest przez oprogramowanie układowe, które może występować dwóch postaciach: BIOS oraz UEFI. UEFI jest oprogramowaniem występującym w wszystkich nowszych komputerach, posiada ono znacznie większe możliwości niż tradycyjny BIOS. Po teście następuję poszukiwanie urządzenia pamięci masowej przechowywującego system operacyjny. Teraz przechodzimy do drugiej fazy rozruchu - fazy programu ładującego. Program ładujący rezyduje w pierwszym sektorze dysku jest on odpowiedzialny za odnalezienie jądra na dysku, rozpakowanie go, załadowanie do pamięci i uruchomienie. Programy rozruchowe (a przynajmniej te wykorzystywane z dystrybucjami Linuks) pozwalają użytkownikowi komputera na wybór jądra oraz zmianę domyślnych parametrów uruchomieniowych. Kiedy jądro jest uruchamiane wówczas rozpoczyna się start systemu operacyjnego. Zadaniami jądra w odniesieniu do procesu ładowania jest zainicjowanie wszystkich urządzeń oraz uruchomienie programu typu init, tym samym przechodząc do czwartej ostatniej fazy - fazy inicjalizacji w niej program typu init uruchamia wszystkie usługi, uruchamiajac tym samym przestrzeń użytkownika, pod koniec tej fazy użytkownik ma przed sobą gotowy do działania system. Stan systemu operacyjnego pozwalający na zalogowanie się użytkownika do niego kończy cały proces uruchamiania.

11.1. Program rozruchowy GRUB

Dla dystrybucji Linuksa istnieje kilka programów rozruchowych. Tutaj jednak skupimy sie na jednym z nich, który jest stosowany w RHEL a jest nim GRUB. Jest on najczęsciej stosowany wśród wiodących dystrybucji. Styczność z nim mamy już na samym początku uruchomienia naszego systemu, po jego uruchomieniu wyświetla on menu zawierające wpisy uruchomieniowe naszego systemu. Poniżej znajduje się obrazek przedstawiający to menu.

tabela-grub-rhel8

Na obrazku możemy zobaczyć dwa wpisy i jeden jest zaznaczony na biało. Jest on domyślną opcją rozruchową. Jeśli nie dokonamy żadnych zmian po upływie czasu odmierzanego w ostatniej linii tekstu pod menu, zaznaczona opcja zostanie automatycznie zatwierdzona i rozpocznie się uruchamianie systemu. Po menu możemy poruszać się za pomocą strzałek w górę oraz w dół.

Opcje uruchomieniowe kryjące się za wpisami w menu możemy edytować zaznaczając wybrany wpis, a następnie naciskając klawisz e. GRUB daje nam możliwość debugowania rozruchu za pomocą wbudowanej powłoki. Powłokę możemy uruchomić naciskając klawisz c podczas wyświetlania menu. W powłoce kryje się około 100 poleceń do wykorzystania.

grub-shell-rhel8

11.1.1. Konfiguracja programu GRUB

Za wyświetlanie menu odpowiedzialny jest plik konfiguracjyny grub.cfg znajdujący się w katalogu /boot/grub2. Jednak plik ten nie nadaje się do modyfikacji, gdyż aktualizacja jądra nadpisze wszystkie dokonane w nim zmiany, ponieważ jest generowany automatycznie przez specjalnie przygotowane do tego narzędzie.

Zmian w GRUB możemy dokonać edytując inny pliki, taki jak /etc/default/grub. Na poniższym przykładzie znajduje się jego zawartość:

[user@rhel8-vm1 ~]$ nl /etc/default/grub 
     1	GRUB_TIMEOUT=5
     2	GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
     3	GRUB_DEFAULT=saved
     4	GRUB_DISABLE_SUBMENU=true
     5	GRUB_TERMINAL_OUTPUT="console"
     6	GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet"
     7	GRUB_DISABLE_RECOVERY="true"
     8	GRUB_ENABLE_BLSCFG=true

Gdzie każda z tych linii oznacza m.in.:

W tym pliku możemy dokonywać zmian, jednak najpierw warto zaponać się bliżej z GRUB, rozszerzając wiedzę z tego rodziału o np. dokumentację Red Hat, ostatni wpis jest dość specyficzyny i używany w nowszych dystrybucjach klasy enterprise. Jeśli już dokonamy w nim zmian, zmiany te trzeba zatwierdzić po przez wygenerowanie nowego pliku grub.cfg a służy do tego polecenie grub2-mkconfig po opcji -o należy podać ścieżkę docelową, gdzie polecenie ma zapisać nową konfigurację w przeciwnym wypadku zwróci ją na standardowe wyjście.

[user@rhel8-vm1 ~]$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg
[sudo] hasło użytkownika user: 
Generating grub configuration file ...
done

Poza plikiem /etc/default/grub, wyżej przedstawione polecenie wykorzystuje również skrypty pomocnicze znajdujące się w katalogu /etc/grub.d. Poniżej znajduje się listing jego zawartości:

[user@rhel8-vm1 ~]$ sudo ls -l /etc/grub.d/
[sudo] hasło użytkownika user: 
razem 96
-rwxr-xr-x. 1 root root  8958 03-28 16:12 00_header
-rwxr-xr-x. 1 root root  1043 02-09 12:13 00_tuned
-rwxr-xr-x. 1 root root   232 03-28 16:12 01_users
-rwxr-xr-x. 1 root root   832 03-28 16:12 08_fallback_counting
-rwxr-xr-x. 1 root root 14088 03-28 16:12 10_linux
-rwxr-xr-x. 1 root root   830 03-28 16:12 10_reset_boot_success
-rwxr-xr-x. 1 root root   889 03-28 16:12 12_menu_auto_hide
-rwxr-xr-x. 1 root root 11696 03-28 16:12 20_linux_xen
-rwxr-xr-x. 1 root root  2559 03-28 16:12 20_ppc_terminfo
-rwxr-xr-x. 1 root root 10670 03-28 16:12 30_os-prober
-rwxr-xr-x. 1 root root  1412 03-28 16:12 30_uefi-firmware
-rwxr-xr-x. 1 root root   700 04-01 11:49 35_fwupd
-rwxr-xr-x. 1 root root   214 03-28 16:12 40_custom
-rwxr-xr-x. 1 root root   216 03-28 16:12 41_custom
-rw-r--r--. 1 root root   483 03-28 16:12 README

Plików w tym katalogu jest całkiem sporo. Istotną rzeczą w tych plikach są liczby na początku nazw. Te liczby prócz znaczeń opisanych w pliku README okreslają kolejność uruchamiania tych skryptów. Każdy pliki wykonuje jakąś czynność podczas generowania pliku konfiguracyjnego, np. 00_header ładuje plik /etc/default/grub; 10_linux odpowiada za generowanie wpisów uruchamiających dystrybucje, jednak jak wiemy w RHEL odpowiada za to inny mechanizm; 30_os-prober odpowiada za poszukiwanie zainstalowanych na dyskach komputera innych systemów operacyjnych, np. MS Windows; 40_custom oraz 41_custom pozwalają na zapisanie swojej konfiguracji.

11.1.2. Ładowanie systemu do określonego trybu

Edytując wpis ładujący system z poziomu menu GRUB możemy uruchomić system w trybie ratunkowym, odzyskiwania lub przerwać jego ładowanie w momencie gdy głównym system plików jest jeszcze intrd (mały obraz systemu plików ładowany do pamięci przez jądro aby zapewnić mu dostęp do sterowników dysków, żeby mógł zamontować właściwy system plików z katalogiem głównym). Za pomocą takiego uruchomienia systemu możemy zmienić zapomniane hasło superużytkownika. Aby zapoznać się z tym trybem przywrócimy utracone hasło root. Do tego ćwiczenia potrzebny będzie RHEL 8, gdyż w RHEL 9 nie ważne jaki tryb uruchominy, nadal wymagane będzie hasło aby przejść do powłoki. Zatem jedyną opcją na odzyskanie dostępu w RHEL 9 jest podmontowanie systemu plików w środowisku powłoki instalatora.

Po uruchomieniu maszyny, podczas wyświetlania menu GRUB naciskamy e, następnie w linii rozpoczynającej się od linux dopisujemy na końcu rd.break. Następnie naciskamy Ctrl+x, aby uruchomić tak zmodyfikowany wpis. Po uruchomieniu się systemu uzyskamy poniższy znak zachęty:

switch_root:/#

W tym momencie musimy zmienić katalog główny ramdysku na katalog główny znajdujący się na dysku. Kernel po odnalezieniu dysku z katalogiem głównym montuje go w katalogu /sysroot środowiska ramdysku. Do przełączenia katalogu głównego posłuży nam polecenie chroot:

switch_root:/# chroot /sysroot

Po przełączeniu się uzyskamy taki znak zachęty jak ten poniżej.

sh-4.4#

Z zamontowanym w tym momencie katalogiem głównym jest pewien problem, otóż na tym etapie uruchomienia systemu jest on zamontowany w trybie tylko do odczytu, a zmiana hasła wymaga zapisania informacji w pliku /etc/shadow, dlatego też musimy zamontować katalog główny tym razem w pełnym dostępie.

sh-4.4# mount -o remount,rw /

Teraz możemy wydawać polecenie passwd i ustalić nowe hasło dla superużytkownika. Następnie musimy stworzyć pusty plik w katalogu głównym o nazwie .autorelabel. Ponieważ SELinux musi wytworzyć nowe etykiety, jeśli tego nie zrobimy zmiany nie przyniosą skutku.

sh-4.4# passwd
sh-4.4# touch .autorelabel

Po wykonaniu tych czynności możemy opuścić środowisko zmienionego katalogu a następnie zrestartować maszynę uruchamiając ją już normalnie.

sh-4.4# exit
switch_root:/# reboot

Po uruchomieniu systemu nastąpi nadawanie nowych etykiet, po tym system może zostać ponownie uruchomiony, kiedy będzie gotowy do pracy możemy zalować się nowym hasłem na super użytkownika.

Odnośnie egzaminu:
Musimy wiedzieć jak załadować system do określonego trybu z poziomu menu GRUB2, aby na przykład móc zmodyfikować plik /etc/fstab, lub zresetować hasło superużytkownika.

11.2. Jądro systemu Linuks

Jądro to rdzeń każdej dystrybucji, odpowiada za wiele apektów działania systemów oraz udostępnia masę funkcji, bez których dystrybucjom ciężko było by być systemami operacyjnymi. Jedną z takich funkcji jest zarządzanie urządzeniami. Do komunikacji z nimi jądro potrzebuje sterowników. Sterownik w odniesieniu do jądra nazywane są modułami część modułów wymaganych przez jądro może być już w nie wbudowana a część jest ładowana w razie potrzeby (do tego też służy initrd). RHEL 8.6, którego używam również i w tym rozdziale jest dostarczany z jądrem 4.18.0-372.9.1.el8.x86_64, tę informację można uzyskać za pomocą polecenia uname -r. I to jądro wystarcza dla potrzeb systemu operacyjnego.

Warto jednak pamiętać o tym, że rozwój technologiczny komputerów oraz całej dziedziny informatyki nie stoi w miejscu i jądro Linuks próbuje za nim nadąrzyć. W związku z tym funkcjonalność jądra również ulega zmianie, więc jeśli odbiorcy chcą skorzystać z tych zmian muszą przebudować jądra używane na ich komputerach. Najprostszą i za razem bezpieczną metodą jest instalacja jednego jądra obok drugiego. Oczywiście tylko jedno jądro może działać w tym samym czasie. Ta metoda w względu na przechowywanie dotychczas używanego jądra pozwala wrócić do niego, gdy nowe jądro okaże się nie odpowiednie dla naszego środowiska. Rozwój jądra Linuks to nie tylko dokładanie nowych funkcji ale również usuwanie już niewspieranych technologii, chociaż dystrybucje mogą działajać na różnym sprzęcie, który może mieć lata świetności dawno za sobą, stąd też modułowa budowa jądra. Rózwój jądra nie jest jedym powodem do jego przebudowania, nie które aplikacje lub systemy bazodanowe mogą wymagać nowszej jego wersji, aby zapewnić maksimum wydajności. Tym zajmiemy się w tym podrozdziale: przebudowa jądra. Omówimy sobie również gdzie znajduje się jądro w systemie.

11.2.1. Pliki jądra w systemie

Jądro to zwykły program, którego zadaniem jest wykonanie czynności i zapewnienie funkcji, które przez większość z nas uznałaby za "systemowe". Jednak to nadal program, więc musi gdzieś w systemie plików istnieć jego plik. Pliki jądra najczęściej znajdują się w katalogu /boot. Plik jądra zazwyczaj nazywa się vmlinuz-*, część po myślniku już zależy od dystrybucji. Obok pliku jądra w tym samym katalogu znajduje sie również plik ramdysku. W przypadku RHEL w katalogu /boot znajdują się podkatalogi: grub - zawierający konfigurację programu ładującego, loaders - zawierający pliki blscfg zawierajace wpisy menu programu ładującego oraz katalog efi przechowujący program ładujący dla komputerów uruchamianych w trybie UEFI. W tym katalogu znajdują się również pliki pomocnicze jądra takie jak config-* oraz System.map-*. Gwiazdki (*) w nazwach plików oznaczczają wersję jądra. Poniżej znajduje się listing zawartości tego katalogu.

[user@rhel8-vm1 ~]$ ls -l /boot
razem 213492
-rw-r--r--. 1 root root    195982 04-16 04:33 config-4.18.0-372.9.1.el8.x86_64
drwxr-xr-x. 3 root root        17 07-10 17:27 efi
drwx------. 4 root root        83 07-14 07:33 grub2
-rw-------. 1 root root 112548052 07-10 17:50 initramfs-0-rescue-1ad7389b6ff44aac9cfcab2485444bc8.img
-rw-------. 1 root root  53300132 07-10 17:57 initramfs-4.18.0-372.9.1.el8.x86_64.img
-rw-------. 1 root root  27283456 07-10 18:00 initramfs-4.18.0-372.9.1.el8.x86_64kdump.img
drwxr-xr-x. 3 root root        21 07-10 17:42 loader
lrwxrwxrwx. 1 root root        49 07-10 17:45 symvers-4.18.0-372.9.1.el8.x86_64.gz -> /lib/modules/4.18.0-372.9.1.el8.x86_64/symvers.gz
-rw-------. 1 root root   4359450 04-16 04:33 System.map-4.18.0-372.9.1.el8.x86_64
-rwxr-xr-x. 1 root root  10460528 07-10 17:47 vmlinuz-0-rescue-1ad7389b6ff44aac9cfcab2485444bc8
-rwxr-xr-x. 1 root root  10460528 04-16 04:34 vmlinuz-4.18.0-372.9.1.el8.x86_64

Katalog /boot nie jest jedynym miejscem, w którym znajdują się pliki jądra. Dynamicznie ładowane modułu znajdują sie w katalogu /usr/lib/modules. Katalog zawiera cała strukturę katalogową, w której znajdują sie najróżniejsze moduły z podziałem na wersję jądra dostępne w systemie.

[user@rhel8-vm1 ~]$ ls -l /usr/lib/modules
razem 4
drwxr-xr-x. 3 root root   19 07-10 17:31 4.18.0-372.2.1.el8.x86_64
drwxr-xr-x. 6 root root 4096 07-10 17:55 4.18.0-372.9.1.el8.x86_64

W wewnątrz tej struktury znajdują się moduły sterowników:

[user@rhel8-vm1 ~]$ ls -l /usr/lib/modules/4.18.0-372.9.1.el8.x86_64/kernel/drivers/ | head -25
razem 48
drwxr-xr-x.  5 root root  239 07-10 17:30 acpi
drwxr-xr-x.  2 root root  169 07-10 17:30 ata
drwxr-xr-x.  3 root root   20 07-10 17:30 base
drwxr-xr-x.  2 root root   24 07-10 17:30 bcma
drwxr-xr-x.  3 root root  180 07-10 17:30 block
drwxr-xr-x.  2 root root  272 07-10 17:30 bluetooth
drwxr-xr-x.  3 root root   17 07-10 17:30 bus
drwxr-xr-x.  2 root root   25 07-10 17:30 cdrom
drwxr-xr-x.  6 root root  192 07-10 17:30 char
drwxr-xr-x.  2 root root   50 07-10 17:30 counter
drwxr-xr-x.  2 root root  143 07-10 17:30 cpufreq
drwxr-xr-x.  2 root root   36 07-10 17:30 cpuidle
drwxr-xr-x.  6 root root  107 07-10 17:30 crypto
drwxr-xr-x.  4 root root   54 07-10 17:30 dax
drwxr-xr-x.  2 root root   23 07-10 17:30 dca
drwxr-xr-x.  5 root root   60 07-10 17:30 dma
drwxr-xr-x.  2 root root 4096 07-10 17:30 edac
drwxr-xr-x.  2 root root  113 07-10 17:30 firewire
drwxr-xr-x.  2 root root   69 07-10 17:30 firmware
drwxr-xr-x.  2 root root  107 07-10 17:30 gpio
drwxr-xr-x.  3 root root   17 07-10 17:30 gpu
drwxr-xr-x.  5 root root 4096 07-10 17:30 hid
drwxr-xr-x.  2 root root   74 07-10 17:30 hv
drwxr-xr-x.  3 root root 4096 07-10 17:30 hwmon
...

Poza zwykłymi plikami jądra, w systemie istnieje interfejs przez nie udostępniany, a jest nim katalog /proc. Ten katalog zawiera masę podkatalogów odpowiadających uruchomionym w systemie procesom oraz kilkdziesiąt plików pomocniczych. Z tego katalogu korzystają takie narzędzia jak: ps, top, mem czy lscpu. Poniżej znajduje się listing podkatalogów procesów:

[user@rhel8-vm1 ~]$ ls -l /proc | head 
razem 0
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 1
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 10
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 1000
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 1010
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 1011
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 1015
dr-xr-xr-x.  9 root           root                         0 07-14 07:29 1017
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 1020
dr-xr-xr-x.  9 root           root                         0 07-14 07:28 11

Informacje na temat procesora oraz pamięci możemy uzyskać bezpośrednio od jądra korzystając takich plików jak /proc/cpuinfo oraz /proc/meminfo.

Plik zawierający informacje o procesorze:

[user@rhel8-vm1 ~]$ cat /proc/cpuinfo 
processor	: 0
vendor_id	: GenuineIntel
cpu family	: 6
model		: 60
model name	: Intel Core Processor (Haswell, no TSX)
stepping	: 1
microcode	: 0x1
cpu MHz		: 1696.073
cache size	: 16384 KB
physical id	: 0
siblings	: 1
core id		: 0
cpu cores	: 1
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
...

Plik zawierający informacje o pamięci:

[user@rhel8-vm1 ~]$ cat /proc/meminfo 
MemTotal:         824024 kB
MemFree:           92668 kB
MemAvailable:     285968 kB
Buffers:              20 kB
Cached:           284704 kB
SwapCached:         1628 kB
Active:           167040 kB
Inactive:         396240 kB
...

11.2.2. Pakiety jądra

Jądro do dystrybucji dostarczane jest za pomocą zwykłych pakietów z oprogramowaniem, które mozemy zainstalować z repozytorium. Jądro rozbite jest na kilka pakietów, których lista oraz opis zawartości znajdują się poniżej.

Nie wszystkie z tych pakietów muszą występować w systemie, aby jądro normalnie funkcjonowało, oraz nie wszystkie one są potrzebne do jego przebudowania. Za pomoca polecenia poniżej możemy sprawdzić jakie pakiety jądra są zainstalowane w naszym systemie.

[user@rhel8-vm1 ~]$ rpm -qa | grep 'kernel'
kernel-tools-libs-4.18.0-372.9.1.el8.x86_64
kernel-tools-4.18.0-372.9.1.el8.x86_64
kernel-4.18.0-372.9.1.el8.x86_64
kernel-core-4.18.0-372.9.1.el8.x86_64
kernel-modules-4.18.0-372.9.1.el8.x86_64

Te pakiety trzeba będzie pobrać, kiedy bedziemy chcieli przebudować jądro. Zanim jednak to nastąpi przeanalizujemy wersje jądra aby móc wybrać to najbardziej odpowiednie.

11.2.3. Wersja jądra

Wersje jądra możemy sprawdzić za pomocą polecenia uname -r. Dzieli się ona na kilka części, które mogą pomóc nam w zidentyfikowaniu nowego jądra odpowiedniego dla naszego systemu:

[user@rhel8-vm1 ~]$ uname -r
4.18.0-372.9.1.el8.x86_64

Po za zapoznaniu się z wersją, możemy przejść do przebudowy jądra na naszych maszynych.

11.2.4. Przebudowa jądra

Przebudowę jądra rozpoczniemy od ustalenia jego obecnej wersji oraz wybrania odpowiedniej dla naszych rozwiązań. Z racji tego, iż jest to tylko przykład wybierzemy kolejny build dystrybucji.

[user@rhel8-vm1 ~]$ uname -r
4.18.0-372.9.1.el8.x86_64

W wiec w naszym przypadku najlepiej będzie zainstalować wersje np. 373 lub inną podobną. Teraz musimy ustalić źródło, skąd pobierzemy pakiety potrzebne do zainstalowania nowego jądra. Opcje mamy dwie: strona Red Hat (ponieważ nasze system nie są zarejestrowane w sieci Red Hat, musimy pobrać pakiety logując się na stronie a następnie wybrać z sekcji pobierania odpowiednie pakiety) lub strona rpmfind.net, która jest wyszukiwarką pakietów wśród wielu serwerów lustrzanych różnych dystrybucji klasy enterprise. Skutkiem użycia pakietów ze strony rpmfind jest to, iż poźniej nasze jądro może widnieć jako np. CentOS w menu GRUB, więc najlepszym rozwiazaniem jest skorzystanie z strony Red Hat. Pakiety możemy pobrać na maszynie za pomocą zainstalowanej przeglądarki lub na komputerze który ją hostuje i następnie przesłać zbiorcze archiwum za pomocą sftp (patrz: rozdział 19). Listę pakietów niezbędnych do zainstalowania nowego jądra możemy wyświetlić za pomocą polecenia:

[user@rhel8-vm1 ~]$ rpm -qa | grep 'kernel'
kernel-tools-libs-4.18.0-372.9.1.el8.x86_64
kernel-tools-4.18.0-372.9.1.el8.x86_64
kernel-4.18.0-372.9.1.el8.x86_64
kernel-core-4.18.0-372.9.1.el8.x86_64
kernel-modules-4.18.0-372.9.1.el8.x86_64

Na komputerze na którym hostuje maszynę wirtualną pobrałem z strony Red Hat najnowsze pakiety jądra przeznaczone dla RHEL 8. Stworzyłem z nich archiwum nastepnie przesłałem do wcześniej utworzonego podkatalogu w katalogu /tmp na maszynie z RHEL 8. Poniżej znajduje się lista pobranych pakietów:

[user@rhel8-vm1 kernel]$ ls -l 
razem 97736
-rw-r--r--. 1 user user  8436304 07-14 10:22 kernel-4.18.0-372.16.1.el8_6.x86_64.rpm
-rw-r--r--. 1 user user 41240076 07-14 10:22 kernel-core-4.18.0-372.16.1.el8_6.x86_64.rpm
-rw-r--r--. 1 user user 33296868 07-14 10:22 kernel-modules-4.18.0-372.16.1.el8_6.x86_64.rpm
-rw-r--r--. 1 user user  8654240 07-14 10:23 kernel-tools-4.18.0-372.16.1.el8_6.x86_64.rpm
-rw-r--r--. 1 user user  8445172 07-14 10:23 kernel-tools-libs-4.18.0-372.16.1.el8_6.x86_64.rpm

Teraz instalacja jest już banalna, wystarczy użyć polecenia dnf, a ono wykona wszystkie czynności za nas.

[user@rhel8-vm1 kernel]$ sudo dnf install kernel-*
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Rozwiązano zależności.
================================================================================
 Pakiet               Arch.     Wersja                    Repozytorium    Rozm.
================================================================================
Instalowanie:
 kernel               x86_64    4.18.0-372.16.1.el8_6     @commandline    8.0 M
Aktualizowanie:
 kernel-tools         x86_64    4.18.0-372.16.1.el8_6     @commandline    8.3 M
 kernel-tools-libs    x86_64    4.18.0-372.16.1.el8_6     @commandline    8.1 M
Instalowanie zależności:
 kernel-core          x86_64    4.18.0-372.16.1.el8_6     @commandline     39 M
 kernel-modules       x86_64    4.18.0-372.16.1.el8_6     @commandline     32 M

Podsumowanie transakcji
================================================================================
Instalacja    3 pakiety
Aktualizacja  2 pakiety

Całkowity rozmiar: 95 M
W porządku? [t/N]: 

Po zatwierdzeniu rozpocznie się instalacja. Po skończonej instalacji musimy uruchomić ponownie maszynę. W menu GRUB nowe jądro będzie na pierwszej domyślnej pozycji. Po załadowaniu systemu wersją jądra używana przez ten system to:

[user@rhel8-vm1 ~]$ uname -r
4.18.0-372.16.1.el8_6.x86_64

Jak możemy zauważyć to jądro różni się od porzedniego drobnymi zmianami wprowadzonymi przez dystrybucję, gdyż zmianie uległ numer rewizji w numerze buildu.

Odnośnie egzaminu:
Zawsze należy instalować nowe jądro, a nie aktualizować stare. Aktualizacja usuwa każde istniejące jądro i zastępuje je nowym w przypadku problemów po instalacji z nowym jądrem nie będzie możliwości powrotu do poprzedniego działającego poprawnie jądra.

Ćwiczenie 1: Właczenie komunikatów podczas ładowania systemu

Jako użytkownik z możlwością podniesienia uprawnień na maszynie z RHEL 8, usuń wartość quiet, z opcji GRUB_CMDLINE_LINUX w pliku /etc/default/grub. Następnie uruchom polecenie grub2-mkconfig aby wygenerować nowy plik konfiguracyjny GRUB. Na koniec uruchom ponownie system, aby zobaczyć komunikaty generowane przez jądro.

Ćwiczenie 2: Przywrócenie hasła superużytkownika

Na maszynie z RHEL 8, przerwij ładowanie systemu i zmień hasło superużytkownika. Po ponownym uruchomieniu maszyny zaloguj się na jego konto i ustaw hasło takie jakie było do tej pory (chyba, że go nie pamiętasz).

Ćwiczenie 3: Instalacja nowego jądra

Na maszynie z RHEL 8, jako użytkownik z możliwością podniesienia uprawnień, sprawdź wersję jądra domyślnie używaną w w systemie, następnie ustal jakie pakiety jądra są zainstalowane. Pobierz nowszą wersję jądra z Red Hat Customer Portal lub rpmfind.net i przeprowadź instalacje nowego jądra. Po czym zrestartuj system i sprawdź obecną jego wersję.

Podsumowanie

W tym rodziale zapoznaliśmy się z procesem ładowania systemu. Poznaliśmy po krótce każdą z jego faz. Przyjrzeliśmy się bliżej z programowi ładującemu GRUB jego plikom konfiguracyjnymi oraz narzędziom z nim związanym. Nauczyliśmy się uruchamiać system w trybie awaryjnym aby przywrócić hasło superużytkownika lub poprawić wpisy w pliku /etc/fstab. Na koniec poruszyliśmy temat jądra, dowiedzieliśmy sie gdzie znajdują się pliki jądra, jakie pakiety odpowiadają za nie w systemie oraz nauczyliśmy się poprawnie interpretować jego wersje jądra aby móc zainstalować nową jego wersję. Następnym rodziale przyjrzymy się ostatniej fazie rozruchu.

12. Inicjalizacja systemu, pliki dziennika oraz dostarajanie systemu

W tym rozdziale zajmiemy się czwartą ostatnią fazą uruchamiania systemu czyli jego inicjalizacją. Dla dystrybucji Linuksa istnieje wiele programów typu init. Większość z wiodących dystrybucji używa Systemd (ang. System daemon), jednak wielu programistów uważa, że systemd jest sprzeczne z filozofią Uniksa, dlatego też wśród społeczności powstało wiele rozwidleń, będących odpowiednikami dystrybucji wiodących ale dających końcowemu użytkownikowi wybór z jakiego (poza systemd) z programów typu init chce korzystać. W niektórych przypadkach decyzje podejmujemy sciągając odpowiedni obraz płyty, a w niektórych wyboru dokonujemy go podczas instalacji. Nie mniej jednak RHEL korzysta z systemd i to nim się zajmiemy, dlatego też odniesienia jako programu lub procesu typu init będą odnośić się do systemd.

Jak zapewne zdajemy sobie sprawę wiele programów generuje komunikaty diagnostyczne. Nie które z nich są wyświetlane na naszych terminalach, inne te których nie widzimy, albo nie są generowane albo są zbierane przez jednego z demonów systemowych i zapisywane w jednym z systemowych katalogów. W jednym z nich znajdują się właśnie pliki dziennika.

Poza instalacją i konfiguracją oprogramowania, zarządzaniem plikami, katalogami czy użytkownikami być może będziemy musieli dostoswać nasz system, aby był bardziej wydajny co może spowodować większy pobór energii elektrycznej lub będziemy zmuszeni, aby nasz system był bardziej energooszczędny, to bedziemy mogli zrealizować za pomocą jednego z demonów, którego będzie trzeba zainstalować.

12.1. Inicjalizacja systemu - systemd

W większości wiodących dystrybucji, wybiera systemd jak program typu init dla swoich systemów. Mimo sprzeciwu części społeczności, to systemd nie jest programem, który jest pozbawiony zalet. Ma ich bardzo wiele i są one dość istotne. Systemd jest to program typu init, którego głównymi zadaniami jest przygotowanie systemu do pracy oraz zarządanie usługami. W fazie inicjalizacji uruchamiana jest większość usług, które do czasu wdrożenia systemd uruchamiane są jedna po drugiej co wydłużało proces uruchamiania systemu. Natomiast nowy program postawił na współbieżność uruchamiania - wszystkie usługi startują w tym samym czasie. Systemd może również opóźnić uruchomienie konkretej usługi kiedy będzie ona rzeczywiście wymaga zaoszczędzając przy tym zasoby systemu. Kolejną rzeczą, którą wprowadza systemd jest zarządzanie zasobami, którymi operuja usługi. Program za pomocą oczywiście odpowiedniej konfiguracji jest wstanie stworzyć zasób, który potrzebują inne usługi, następnie uruchomić demona, który taki zasób powinien udostępniać w momencie gdy demon z zasobem jeszcze się uruchamia, inne te które go wymagają widzą że jest on dostępny i wysyłają zapytania do tego demona o dostęp do zasobu. Te zapytania są buforowane do momentu jego pełnego uruchomienia, na koniec te zapytania wraz kontrolą nad zasobem są przekazywane do niego. Jest to kolejna cecha, która przyspiesza ładowanie systemu. Kolejną cechą systemd jest zmiana administracji usług. Teraz nie są wymgane już skrypty wystarczy krótki plik, kilka, kilkanaście linijek i usługa jest już zdefiniowana.

12.1.1. Jednostki

Jednostki są to pliki za pomocą których opisuje się różne komponenty systemu dla systemd. Pliki jednostek są to zwykłe pliki tekstowe, podzielone na dwie, trzy sekcje. Pierwsza sekcją służy do opisu jednostki, druga jest zależna od typu jednostki i zawiera specyficzne dla niego dyrektywy. Trzecia sekcja służy w zazwyczaj od określania włączania i wyłączania jednostki (Uwaga, tutaj włączanie i wyłączanie tyczy się angielskich słów enable oraz disable. Jednostki systemd się aktywuje lub uruchamia [ang. start]). Poniżej znajduje się plik jednostki demona ssh.

[user@server1 ~]$ cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.target
Wants=sshd-keygen.target

[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

Ten plik przestawia jednostkę usługi. Omawianie dyrektyw wykracza poza ramy tego materiału, dlatego nie będzie tutaj to realizowane. Więcej wiedzy znajduje się na stronie podręcznika man systemd.unit. Poniżej znajduje się lista typów jednostek oraz krótki opis za co odpowiadają w systemie.

12.1.2. Jednostki celu

Cel w ujęciu systemd jest logiczna grupą jednostek różnego rozdzaju, które mogą na przykład składać się na jeden z komponentów systemu operacyjnego, na przykład środowisko graficzne. Często cele mogą odpowiadać klasycznym poziomom uruchomienia systemu. W systemie jest zdefiniowanych kilka celów, o to one:

Cele wykorzystywane są do uruchamiania wielu jednostek zapisanych w ich zależnościach.

12.1.3. Polecenie systemctl

Polecenie służącym do zarządzania systemd jest systemctl, za jego pomocą możemy wykonywać proste czyności administracyjne związane z jednostkami oraz celami. Polecenie to zawiera duża ilość różnego rodzaju podpoleceń oraz modyfikatorów, są one opisane na stronie podręcznika tego polecenia. My za poznamy się z nim w praktyce.

Wydając polecenie systemctl bez żadnej opcji polecenie wyświetli table przedstawiącą wszystkie aktywne jednostki wraz z ich stanem oraz krótkim opisem. Poniżej znajduje się jej fragment

UNIT                                LOAD   ACTIVE SUB       DESCRIPTION
...
rhsmcertd.service                   loaded active running   Enable periodic update of ...
rsyslog.service                     loaded active running   System Logging Service
rtkit-daemon.service                loaded active running   RealtimeKit Scheduling Policy ...
spice-vdagentd.service              loaded active running   Agent daemon for Spice guests
sshd.service                        loaded active running   OpenSSH server daemon
switcheroo-control.service          loaded active running   Switcheroo Control Proxy service
systemd-journal-flush.service       loaded active exited    Flush Journal to Persistent ...
...

Tego nie widać na przykładzie ale jeśli wywołamy w terminalu polecenie, możemy zobaczyć, że poszczególne typy jednostek są oddzielone od siebie za pomocą poziomej linii. W drugiej kolumnie znajduje się stan jednostki. Ta kolumna podzielona jest na trzy mniejsze kolumny. Pierwsza określa czy jednostka jest załadowana, druga określa tzw. stan wyższego poziomu aktywacji. Jest to ogólny stan aktywacji i może przybierać on różne wartości takim najważniejszymi są active, failed, inactive. Trzecia zaś określa niższy poziom aktywacji, może on zawierać informacje specyficzne dla typu jednostki lub demona.

Powyższy przykład zawiera tylko załadowane jednostki, ale jeśli chcielibyśmy zobaczyć wszystkie jednostki dostępne w systemie musimy dodać do polecenia modyfikator --all. Innym przydatnym modyfikatorem jest -t pozwalający podać typ jednostki.

  UNIT                          LOAD      ACTIVE   SUB     DESCRIPTION                                                                  
  accounts-daemon.service       loaded    active   running Accounts Service
  alsa-restore.service          loaded    inactive dead    Save/Restore Sound Card State
  alsa-state.service            loaded    active   running Manage Sound Card State (restore and store)
  atd.service                   loaded    active   running Deferred execution scheduler
  auditd.service                loaded    active   running Security Auditing Service
● auto-cpufreq.service          not-found inactive dead    auto-cpufreq.service
● autofs.service                not-found inactive dead    autofs.service
  avahi-daemon.service          loaded    active   running Avahi mDNS/DNS-SD Stack
  blk-availability.service      loaded    inactive dead    Availability of block devices
  chronyd.service               loaded    active   running NTP client/server
  colord.service                loaded    active   running Manage, Install and Generate Color Profiles
  cpupower.service              loaded    inactive dead    Configure CPU power related settings
  crond.service                 loaded    active   running Command Scheduler
  cups.service                  loaded    active   running CUPS Scheduler
  dbus-broker.service           loaded    active   running D-Bus System Message Bus
  dm-event.service              loaded    inactive dead    Device-mapper event daemon

Kolejnym dość istonym modyfikatorem, który pozwoli man namierzyć jednostki, których aktywacja zakończyła się niepowodzeniem. Jest --failed.

[user@server1 ~]$ systemctl --failed
  UNIT          LOAD   ACTIVE SUB    DESCRIPTION                 
● kdump.service loaded failed failed Crash recovery kernel arming

LOAD   = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB    = The low-level unit activation state, values depend on unit type.
1 loaded units listed.

Pod tabelą znajduje się również krótki opis kolumn ze stanem.

Za pomocą podpolecenia list-dependencies możemy wyświetlić zależności jednostki.

[user@server1 ~]$ systemctl list-dependencies tuned.service
tuned.service
● ├─dbus-broker.service
● ├─dbus.socket
● ├─polkit.service
● ├─system.slice
● └─sysinit.target
●   ├─dev-hugepages.mount
●   ├─dev-mqueue.mount
●   ├─dracut-shutdown.service
○   ├─iscsi-onboot.service
●   ├─kmod-static-nodes.service
○   ├─ldconfig.service
●   ├─lvm2-lvmpolld.socket
●   ├─lvm2-monitor.service
○   ├─multipathd.service
...

Koła oznaczają aktywną jednostkę, a okręgi nieaktywną jednostkę.

12.1.4. Zarządzanie jednostkami

Poza podpolecaniami służacymi wyświetlaniu informacji o jednostkach, systemctl posiada dużą ilość podpoleceń związanych z zarządzaniem jednostkami. Jednostki możemy aktywować, dezaktywować, włączać, wyłączać, wyświetlić podsumowanie na ich temat czy zablokwać zmianę stanu. Pierwszą rzeczą jak zrobimy z będzie wyświetlenie informacji o jednostce, a służy do tego podpolecenie status.

[user@server1 ~]$ sudo systemctl status atd.service
[sudo] password for user: 
● atd.service - Deferred execution scheduler
     Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-07-17 07:40:58 CEST; 1h 34min ago
       Docs: man:atd(8)
   Main PID: 934 (atd)
      Tasks: 1 (limit: 5760)
     Memory: 300.0K
        CPU: 5ms
     CGroup: /system.slice/atd.service
             └─934 /usr/sbin/atd -f

Jul 17 07:40:58 server1.example.com systemd[1]: Started Deferred execution scheduler.

Na podstawie tych informacji możemy wywnioskować, że jednostka jest załadowana, jest aktywna a jej demon nadal działa. Znamy także stronę podręcznika opisującą tego demona oraz jego PID. Ważną rzeczą podczas wyświetlania status jednostki jest wykonanie tego z uprawnieniami administratora, wówczas na samym dole zostaną nam wyświetlone komunikaty diagnostyczne, pobrane z plików dziennika systemd, te komunikaty pomagają zlokalizować usterki, które mogą być odpowiedzialne za problem z aktywacją jednostki.

Jednostkę możemy zatrzymać oraz uruchomić za pomocą podpoleceń stop oraz start.

[user@server1 ~]$ sudo systemctl stop atd.service
[sudo] password for user: 
[user@server1 ~]$ sudo systemctl status atd.service
○ atd.service - Deferred execution scheduler
     Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Sun 2022-07-17 09:23:11 CEST; 2s ago
       Docs: man:atd(8)
    Process: 934 ExecStart=/usr/sbin/atd -f $OPTS (code=exited, status=0/SUCCESS)
   Main PID: 934 (code=exited, status=0/SUCCESS)
        CPU: 5ms

Jul 17 07:40:58 server1.example.com systemd[1]: Started Deferred execution scheduler.
Jul 17 09:23:11 server1.example.com systemd[1]: Stopping Deferred execution scheduler...
Jul 17 09:23:11 server1.example.com systemd[1]: atd.service: Deactivated successfully.
Jul 17 09:23:11 server1.example.com systemd[1]: Stopped Deferred execution scheduler.

[user@server1 ~]$ sudo systemctl start atd.service
[user@server1 ~]$ sudo systemctl status atd.service
● atd.service - Deferred execution scheduler
     Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-07-17 09:23:44 CEST; 2s ago
       Docs: man:atd(8)
   Main PID: 1576 (atd)
      Tasks: 1 (limit: 5760)
     Memory: 260.0K
        CPU: 4ms
     CGroup: /system.slice/atd.service
             └─1576 /usr/sbin/atd -f

Jul 17 09:23:44 server1.example.com systemd[1]: Started Deferred execution scheduler.

Do ponownego uruchomienia jednostki służy inne podpolecenie niż stop, start, a jest nim restart.

[user@server1 ~]$ sudo systemctl restart atd.service
[sudo] password for user: 
[user@server1 ~]$ sudo systemctl status atd.service
● atd.service - Deferred execution scheduler
     Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-07-17 09:41:38 CEST; 3s ago
       Docs: man:atd(8)
   Main PID: 1595 (atd)
      Tasks: 1 (limit: 5760)
     Memory: 264.0K
        CPU: 3ms
     CGroup: /system.slice/atd.service
             └─1595 /usr/sbin/atd -f

Jul 17 09:41:38 server1.example.com systemd[1]: Started Deferred execution scheduler.

Do włączanie i wyłączania jednostek służą podpolecenia takie jak enable oraz disable.

[user@server1 ~]$ sudo systemctl disable atd.service
[sudo] password for user: 
Removed /etc/systemd/system/multi-user.target.wants/atd.service.
[user@server1 ~]$ sudo systemctl status atd.service
● atd.service - Deferred execution scheduler
     Loaded: loaded (/usr/lib/systemd/system/atd.service; disabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-07-17 09:41:38 CEST; 6min ago
       Docs: man:atd(8)
   Main PID: 1595 (atd)
      Tasks: 1 (limit: 5760)
     Memory: 264.0K
        CPU: 3ms
     CGroup: /system.slice/atd.service
             └─1595 /usr/sbin/atd -f

Jul 17 09:41:38 server1.example.com systemd[1]: Started Deferred execution scheduler.

W linii Loaded: za ścieżką do pliku jednostki, status zmienił się z enabled na disabled. Jeśli użyjemy podpolecenia enable, status wróci do poprzedniej wartości.

[user@server1 ~]$ sudo systemctl enable atd.service
Created symlink /etc/systemd/system/multi-user.target.wants/atd.service → /usr/lib/systemd/system/atd.service.
[user@server1 ~]$ sudo systemctl status atd.service
● atd.service - Deferred execution scheduler
     Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-07-17 09:41:38 CEST; 9min ago
       Docs: man:atd(8)
   Main PID: 1595 (atd)
      Tasks: 1 (limit: 5760)
     Memory: 264.0K
        CPU: 3ms
     CGroup: /system.slice/atd.service
             └─1595 /usr/sbin/atd -f

Jul 17 09:41:38 server1.example.com systemd[1]: Started Deferred execution scheduler.

Włączenie jednostki powoduje utworzenie dowiązania symbolicznego w katalogu zależności celu, natomiast wyłączenie usuwa to dowiązanie.

Za pomocą podpoleceń mask oraz unmask możemy zablokować włączenie oraz aktywację jednostki. Użycie podpolecenia mask tworzy dowiązanie do pliku /dev/null i zaznacza w systemd, że jednostka jest zablokowana. Podpolecenie umask odwraca działania podpolecenia mask. Usuwa stworzone dowiązanie oraz zmienia status jednostki w systemd.

[user@server1 ~]$ sudo systemctl mask atd.service
[sudo] password for user: 
Created symlink /etc/systemd/system/atd.service → /dev/null.
[user@server1 ~]$ sudo systemctl status atd.service 
● atd.service
     Loaded: masked (Reason: Unit atd.service is masked.)
     Active: active (running) since Sun 2022-07-17 09:41:38 CEST; 28min ago
   Main PID: 1595 (atd)
        CPU: 3ms
     CGroup: /system.slice/atd.service
             └─1595 /usr/sbin/atd -f

Jul 17 09:41:38 server1.example.com systemd[1]: Started Deferred execution scheduler.
Jul 17 10:09:58 server1.example.com systemd[1]: atd.service: Current command vanished from the unit file, execution of the command list ...

[user@server1 ~]$ sudo systemctl unmask atd.service
Removed /etc/systemd/system/atd.service.
[user@server1 ~]$ sudo systemctl status atd.service 
● atd.service - Deferred execution scheduler
     Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-07-17 09:41:38 CEST; 30min ago
       Docs: man:atd(8)
   Main PID: 1595 (atd)
      Tasks: 1 (limit: 5760)
     Memory: 264.0K
        CPU: 3ms
     CGroup: /system.slice/atd.service
             └─1595 /usr/sbin/atd -f

Jul 17 09:41:38 server1.example.com systemd[1]: Started Deferred execution scheduler.
Jul 17 10:09:58 server1.example.com systemd[1]: atd.service: Current command vanished from the unit file, execution of the command list ...

12.1.5. Zarządzanie jednostkami celów

Zarządzaniem jednostkami celów, różni się od zarządzania jednostkami usług czy innych typów. Jednostki celów przedstawiają różne stany uruchomienia systemu operacyjnego, więc aby miało to jakiś sens zarządzanie nim sprowadza się głównie do dwóch czynności, jedną z nich jest określenie oraz ustawienie domyślnego stanu, w którym to system operacyjny jest zdatny do użycia. W tym przypadku określa to jednostka celu - default. Za pomocą podpoleceń możemy sprawdzić która z dostarczonych z systemem jednostek jest wskazywana przez to dowiązanie. Możemy to określić za pomocą podpolecenia get-default.

[user@server1 ~]$ sudo systemctl get-default
[sudo] password for user: 
graphical.target

Tę wartość możemy zmienić, wpływając również na sposób ładowania systemu. Jeśli zmienię domyślny cel z graphical.target na multi-user.target, wówczas system zatrzyma ładowanie systemu przed uruchomieniem środowiska graficznego. Zmianie domyślnego celu służy podpolecenie set-default.

[user@server1 ~]$ sudo systemctl set-default multi-user.target
[sudo] password for user: 
Removed /etc/systemd/system/default.target.
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.
[user@server1 ~]$ sudo systemctl get-default
multi-user.target

Jeśli zrestartujemy teraz maszynę, uruchomi się ona bez środowiska graficznego.

Odnośnie egzaminu:
Może być tak, że na egzaminie będzie trzeba zmienić domyślny cel ładowanie systemu.

Pomiędzy celami możemy się przełączać w trakcie działania systemu. Służy do tego podpolecenie isolate.

[user@server1 ~]$ sudo systemctl isolate multi-user.target

Po wydaniu tego polecenia, jeśli otworzymy okno maszyny zobaczymy, że środowisko graficzne zostało wyłączone. Aby włączyć je z powrotem użyjemy tego samego polecenia, jednak wskazując inny cel.

[user@server1 ~]$ sudo systemctl isolate graphical.target
[sudo] password for user: 

Tym że akcentem zakończyliśmy omawianie systemd. Oczywiście to zagadnienia jest tak obszerne, że można by napisać około 200, 300 stronicową książke na ten temat bazując na samej dokumentacji programu.

12.2. Prowadzenie plików dzienników systemowych

Zbieranie informacji o tym co się dzieje w systemie to bardzo ważna czynność wykonywana przez systemy operacyjne. Nie które z komponentów instalowanych w nich zwracają bardzo ubogie w informacje komunikaty o błędach lub ostrzeżenia z których nic nie wynika. Najczęściej ma to na celu poprawienie wyglądu oraz doświadczeń użytkownika (UI/UX Design), przez co wielu użytkowników zostaje na lodzie chcąc samodzielnie rozwiązać problemy. Ta cecha nie tyczy się tylko systemów MS Windows. Wiodące środowiska graficzne w dystrybucjach również mają z tym problem. Ale w Uniksach mamy dobrze zorganizowane pliki dzienników, dzieki którym w możemy znaleźć przyczynę, nie których problemów.

12.2.1. Rejestrator systemowy - rsyslog

Rsyslog jest odpowiedzialny za zbieranie komunikatów diagnostycznych i umieszczenie ich w przeznaczonych dla nich miejscach. Nie które komunikaty mogą trafiać do plików inne mogą być wyświetlane w terminalach pracujących na serwerze użytkowników. Te reguły są zapisane w pliku konfiguracyjnym demona /etc/rsyslog.conf. Program wspiera dostoswane pliki konfiguracyjne użytkowników przechowywane w /etc/rsyslog.d. Plik konfiguracyjny rsyslog wygląda następująco:

[user@server1 ~]$ grep -v -e '^#' -e '^$' /etc/rsyslog.conf 
global(workDirectory="/var/lib/rsyslog")
module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat")
include(file="/etc/rsyslog.d/*.conf" mode="optional")
module(load="imuxsock" 	  # provides support for local system logging (e.g. via logger command)
       SysSock.Use="off") # Turn off message reception via local log socket; 
			  # local messages are retrieved through imjournal now.
module(load="imjournal" 	    # provides access to the systemd journal
       StateFile="imjournal.state") # File to store the position in the journal
*.info;mail.none;authpriv.none;cron.none                /var/log/messages
authpriv.*                                              /var/log/secure
mail.*                                                  -/var/log/maillog
cron.*                                                  /var/log/cron
*.emerg                                                 :omusrmsg:*
uucp,news.crit                                          /var/log/spooler
local7.*                                                /var/log/boot.log

Ostatnie linie na końcu zawierają zasady, które określaja jakie komunikaty należy zbierać i co z nimi zrobić. Zasady dzielą się na dwie części na selektor, określający komunikaty diagostyczne oraz akcje definiującą dla nich miejsce docelowe. Poza tym selektor również możemy rozłożyć na funkcje - wskazującą na demona lub komponent od którego należy przechwycić komunikaty oraz priorytet określający ważność komunikatu. Funkcję od priorytetu oddziela się kropką. Spis funkcji oraz priorytetów znajduje się na stronie podręcznika pliku /etc/rsyslog.conf. Gwiazdka to symbol wieloznaczny wskazujący zarówno wszystkie priorytety jak i wszystkie funkcje w zależności gdzie został użyty. Priorytet none wskazuje aby nie przechwytywać komunikatów z tej funkcji. Jak możemy zobaczyć w pierwszej zasadzie na jedną akcje może przypadać wiele selektorów. Selektory oddziela się średnikiem (;). W polu akcji zazwyczaj znajdują się ścieżki do plików wskazujących gdzie należy zapisać komunikaty diagnostyczne, jedyny wyjątkiem są komunikaty o najwyższym priorytecie za pomocą wewnętrzengo modułu są one wyświetlane w terminalach jako wiadomości do użytkowników.

Jak możemy zauważyć większość plików dzienników znajduje się w katalogu /var/log. A prawie wszystkie komunikaty diagostyczne trafiają do jednego pliku /var/log/messages. Ten plik jest głównym plikiem dziennika w systemie. Każda linia reprezentuje jeden z komunikatów. Taki plik najlepiej wyświetlać za pomocą polecenia less lub more.

Odnośnie egzaminu:
Używanie polecenie tail wraz z opcją -f może okazać się przydatne podczas śledzenia komunikatów diagnostycznych w trakcie uruchamiania usług lub testowania, aby wychwycić wszysktie problemy.

Jeśli będziemy chcieli coś zmienić w konfiguracji rsyslog, warto przed uruchomieniem ponowym usługi sprawdzić poprawność konfiguracji za pomocą polecenia rsyslog -N 1. Gdzie 1 oznacza ilość wyświetlanych przez polecenie komunikatów.

12.2.2. Obrót plików dziennika

Pliki dziennika to zwykłe pliki tekstowe, które potrafią bardzo szybko przyrastać w zależności od tego co się dzieje w systemie. Taki nie kontrolowany przyrost plików może szybko wypełnić dostępne miejsce dysku. Zastosowano więc mechanizm rotacji plików dziennika. Stare pliki są usuwane robiąc miejsce nowym. Tym zadaniem zajmuje się wywoływany w codziennym katalogu cron (/etc/cron.daily) skrypt logrotate. Skrypt ten posiada swój plik konfiguracyjny /etc/logrotate.conf.

[user@server1 ~]$ grep -v -e '^$' /etc/logrotate.conf 
# see "man logrotate" for details
# global options do not affect preceding include directives
# rotate log files weekly
weekly
# keep 4 weeks worth of backlogs
rotate 4
# create new (empty) log files after rotating old ones
create
# use date as a suffix of the rotated file
dateext
# uncomment this if you want your log files compressed
#compress
# packages drop log rotation information into this directory
include /etc/logrotate.d
# system-specific logs may be also be configured here.

Na podstawie tego pliku możemy dowiedzieć się, że rotacja jest dokonywana co tydzień, pozostawione mają zostać pliki dziennika na cztery tygodnie wstecz oraz po usunięciu starych, następnie zmianie nazw obecnym skrypt utworzy nowe puste pliki dla nowych komunikatów zapisywanych w plikach dzienika. Definicje zachowania dla rotacji plików dziennika dostarczane wraz z programami znajdują się w katalogu /etc/logrotate.d. Oto zawartość tego katalogu.

[user@server1 ~]$ ls -l /etc/logrotate.d/
total 56
-rw-r--r--. 1 root root  91 Mar 31  2021 bootlog
-rw-r--r--. 1 root root 130 Oct 14  2019 btmp
-rw-r--r--. 1 root root 160 May 12  2021 chrony
-rw-r--r--. 1 root root  88 Oct 21  2021 dnf
-rw-r--r--. 1 root root  93 Nov 23  2021 firewalld
-rw-r--r--. 1 root root 172 Jul 29  2021 iscsiuiolog
-rw-r--r--. 1 root root 162 Apr 14 19:04 kvm_stat
-rw-r--r--. 1 root root 312 Nov  1  2021 psacct
-rw-r--r--. 1 root root 226 Jan 19 16:31 rsyslog
-rw-r--r--. 1 root root 155 Mar 18 10:32 samba
-rw-r--r--. 1 root root 237 Jan 17 20:46 sssd
-rw-r--r--. 1 root root  88 Apr 13 17:00 subscription-manager
-rw-r--r--. 1 root root 100 Feb  4 15:58 wpa_supplicant
-rw-r--r--. 1 root root 145 Oct 14  2019 wtmp

Zawartość takiego pliku może zdradzać, w jaki sposób logrotate zachowa się wobec takiego pliku, nadpisując tym samym konfigurację z pliku, z wcześniejszego przykładu (globalnego pliku konfiguracji).

12.2.3. Plik dziennika rozruchu

W katalogu /var/log znajduje się plik boot.log przedstawiający uruchamianie ostatniej fazy rozruchu. W tym pliku możemy podejrzeć czy wszystkie usługi wystartowały czy też nie. Uwaga, plik nie nadaje się do przeglądania za pomocą polecenie less.

[user@server1 ~]$ sudo head /var/log/boot.log
[  OK  ] Started User Login Management.
[  OK  ] Started Daemon for power management.
[  OK  ] Started Authorization Manager.
         Starting Modem Manager...
         Starting firewalld - dynamic firewall daemon...
[  OK  ] Finished Rotate log files.
[  OK  ] Started Accounts Service.
[  OK  ] Started Power Profiles daemon.
[  OK  ] Started Modem Manager.
[  OK  ] Started Disk Manager.
...
[FAILED] Failed to start Crash recovery kernel arming.
See 'systemctl status kdump.service' for details.

W nawiasach kwadratowych wyświetla on OK lub FAILED.

12.2.4. Własne komunikaty diagnostyczne

Czasami możemy chcieć uzupełnić systemowy plik dziennika /var/log/messages o kilka komunikatów, które będą wyznaczać początek i koniec testowanego oprogramowania lub dodwać kilka notatek wyjaśniających takie a nie inne komunikaty. Dzięki rsyslog za pomocą jednego z jego wewnętrznych modułów możemy dokonać wpisów do tego pliku dziennika. Realizować to będziemy za pomocą polecenia logger. Na przykład:

[user@server1 ~]$ logger "Hello, World! It's $LOGNAME"
[user@server1 ~]$ sudo tail -1 /var/log/messages
[sudo] password for user: 
Jul 17 13:03:42 server1 user[2706]: Hello, World! It's user

Te komunikaty są rozpoznawane przez rsyslog jako user.notice, jednak za pomocą opcji -p możemy podać swój selektor. Polecenie logger możemy być przydatne do testowania zmian w konfiguracji rsyslog.

12.3. Dzienniki systemd

Systemd poza funkcjami omówionymi na początku tego rozdziału dostarcza wielu narzędzi, które czasami bywają irytujące, ale nie które z nich jak np. demon systemd-journald są całkiem przydatne, ponieważ to za jego zasługą dowiadujemy się dlaczego nasza usługa nie chce się uruchomić. Ten demon przechwytuje komunikaty z bardzo wielu źródeł od jądra przez ramdysk do różnorakich usług. Swoje komunikaty przechowuje w pliku /run/log/journal, zatem w pliku, który nie jest trwały i zostanie zniszczony w momencie zamknięcia systemu. Konfiguracja tego demona przechowywana jest w pliku /etc/systemd/journald.conf, w którym w razie potrzeby możemy zmienić.

12.3.1. Wyświetlanie dziennika systemd

Do wyświetlenia informacji zebranych przez demona systemd-journald mamy do dyspozycji takie polecenie jak journalctl. Jesli wydamy to polecenie bez żadnych opcji to polecenie zwróci ono treść podobną do zawartości /var/log/messages.

[user@server1 ~]$ sudo journalctl 
[sudo] password for user: 
Jul 16 12:48:20 server1.example.com kernel: Linux version 5.14.0-70.13.1.el9_0.x86_64 (mockbuild@x86-vm-08.build.eng.bos.redhat.com) (gcc (GCC) 11.2.>
Jul 16 12:48:20 server1.example.com kernel: Linux version 5.14.0-70.13.1.el9_0.x86_64 (mockbuild@x86-vm-08.build.eng.bos.redhat.com) (gcc (GCC) 11.2.>
Jul 16 12:48:20 server1.example.com kernel: The list of certified hardware and cloud instances for Red Hat Enterprise Linux 9 can be viewed at the Re>
Jul 16 12:48:20 server1.example.com kernel: Linux version 5.14.0-70.13.1.el9_0.x86_64 (mockbuild@x86-vm-08.build.eng.bos.redhat.com) (gcc (GCC) 11.2.>
Jul 16 12:48:20 server1.example.com kernel: Linux version 5.14.0-70.13.1.el9_0.x86_64 (mockbuild@x86-vm-08.build.eng.bos.redhat.com) (gcc (GCC) 11.2.>
Jul 16 12:48:20 server1.example.com kernel: The list of certified hardware and cloud instances for Red Hat Enterprise Linux 9 can be viewed at the Re>
Jul 16 12:48:20 server1.example.com kernel: Command line: BOOT_IMAGE=(hd0,msdos1)/vmlinuz-5.14.0-70.13.1.el9_0.x86_64 root=/dev/mapper/rhel-root ro c>
Jul 16 12:48:20 server1.example.com kernel: x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
Jul 16 12:48:20 server1.example.com kernel: x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
Jul 16 12:48:20 server1.example.com kernel: x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
...

Przy użyciu opcji -o wraz z wartością verbose, narzędzie zwróci bardzo szczegółowy opis każdego z komunikatów.

Sat 2022-07-16 12:48:20.235840 CEST [s=5135ad3430d34e718c820bc5b157f883;i=1;b=9ca0ad75315f4891b4807b449a2ce046;m=233ae8;t=5e3e9df815e40;x=7786ef23901>
    _SOURCE_MONOTONIC_TIMESTAMP=0
    _TRANSPORT=kernel
    PRIORITY=5
    SYSLOG_FACILITY=0
    SYSLOG_IDENTIFIER=kernel
    MESSAGE=Linux version 5.14.0-70.13.1.el9_0.x86_64 (mockbuild@x86-vm-08.build.eng.bos.redhat.com) (gcc (GCC) 11.2.1 20220127 (Red Hat 11.2.1-9), G>
    _BOOT_ID=9ca0ad75315f4891b4807b449a2ce046
    _MACHINE_ID=4015aef220c344e1875a3fbbd1453b45
    _HOSTNAME=server1.example.com

Polecenie ma masę opcji nie ma sensu ich tutaj wszystkich przytaczać. Znajdziemy je na stronie podręcznika man journalctl.

Komunikaty dla poszczególnych usług znajdziemy na końcu informacji zwracanych przez systemctl status. Dlatego ważne jest, aby używać tego polecenia z uprawnieniami administratora.

12.3.2. Konfiguracja miejsca do przechwywania dzienników systemd

Jak wiemy dziennik systemd nie jest przechowywany na stałe w systemie. To możemy zmienić. W bardzo prosty sposób. Otóz, dyrektywa Storage w konfiguracji demona może mieć cztery wartości:

Domyślną jej wartością jest auto, więc aby włączyć przechowywanie dzienników na stałe, wystarczy utworzyć katalog journal w katalogu /var/log. Tutaj pragnę zaznaczyć, że w RHEL 8 wystarczyło zrestartować usługę, ale w przypadku RHEL 9 należy zrestartować cały system. Kiedy system będzie gotowy wewnątrz katalogu zostanie utworzony podkatalogu o nazwany wartością /etc/machine-id.

[user@server1 ~]$ ls -l /var/log/journal/
total 4
drwxr-sr-x+ 2 root systemd-journal 4096 Jul 16 20:17 4015aef220c344e1875a3fbbd1453b45
[user@server1 ~]$ cat /etc/machine-id 
4015aef220c344e1875a3fbbd1453b45

Teraz komunikaty dziennika systemd bedą przechowywane w tym katalogu.

12.4. Dostosowywanie systemu

W RHEL jak i inych systemach klasy enterprise możemy spotkać się z narzędziem, które na podstawie monitorowania zasobów komputera może zmienić ich ustawienia aby podnieść ich wydajność lub gdy przestanie być to konieczne ją obniżyć aby zaoszczędzić wykorzystywaną energię elektryczną. Tym zajmuje się demon tuned. Nie występuję on domyślnie w systemie i trzeba go zainstalować.

Tuned może dynamicznie na podstawie danych uzyskanych z różnych monitorów zasobów dostosowywać wydajność sprzętu do potrzeb. Może on również zostać przez nas statycznie skonfigurowany za pomocą predefiniowanych profili. Wydajność systemu wówczas nie zmieni się dopóki my nie załadujemy innego profilu. Konfiguracja statyczna jest jego domyślną konfiguracją.

12.4.1. Polecenie tuned-adm

Demon tuned dostarczany jest w postaci pakietów i znajduje się na płycie instalacyjnej z RHEL. Instalacji dokonuje za pomocą poniższego polecenia:

[user@server1 ~]$ sudo dnf install tuned -y
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Last metadata expiration check: 2:38:24 ago on Sun 17 Jul 2022 11:44:30 AM CEST.
Package tuned-2.18.0-1.el9.noarch is already installed.
Dependencies resolved.
Nothing to do.
Complete!

Uwaga, w RHEL 9 demon może wymagać ręcznej aktywacji jednostki za pomocą polecenia systemctl. Wraz z demonem zostanie nam dostarczone polecenie tuned-adm za pomocą, którego będziemy mogli administrować wydajnością systemu dostosowywaną przez te profile. Za pomocą podpolecenia list możemy wyświetlić listę dostępnych profili.

[user@server1 ~]$ sudo tuned-adm list
Available profiles:
- accelerator-performance     - Throughput performance based tuning with disabled higher latency STOP states
- balanced                    - General non-specialized tuned profile
- desktop                     - Optimize for the desktop use-case
- hpc-compute                 - Optimize for HPC compute workloads
- intel-sst                   - Configure for Intel Speed Select Base Frequency
- latency-performance         - Optimize for deterministic performance at the cost of increased power consumption
- network-latency             - Optimize for deterministic performance at the cost of increased power consumption, focused on low latency network performance
- network-throughput          - Optimize for streaming network throughput, generally only necessary on older CPUs or 40G+ networks
- optimize-serial-console     - Optimize for serial console use.
- powersave                   - Optimize for low power consumption
- throughput-performance      - Broadly applicable tuning that provides excellent performance across a variety of common server workloads
- virtual-guest               - Optimize for running inside a virtual guest
- virtual-host                - Optimize for running KVM guests
Current active profile: virtual-guest

Poza listą podpolecenie zwraca również aktywny profil. Identyczne działanie możemy otrzymać poprzez wydanie polecenia tuned-adm wraz z podpoleceniem active.

[user@server1 ~]$ sudo tuned-adm active
[sudo] password for user: 
Current active profile: virtual-guest

Zmiany profilu dokonujemy za pomocą podpolecenia profile a następnie podajemy nazwę profilu.

[user@server1 ~]$ sudo tuned-adm profile balanced
[user@server1 ~]$ sudo tuned-adm active
Current active profile: balanced

Demon sam jest wstanie na podstawie naszej konfiguracji sprzętowej dobrać odpowiedni profil. Aby uzyskać od niego taką informację wydajemy polecenie z podpoleceniem recommend.

[user@server1 ~]$ sudo tuned-adm recommend
virtual-guest

Profil rekomendowany możemy ustawić przez podanie jego nazwy jako argumentu podpolecenia profile lub tak jak przedstawiłem to na poniższym przykładzie.

[user@server1 ~]$ sudo tuned-adm profile $(sudo tuned-adm recommend)

Jeśli dostosowanie wydajności nie jest już nam potrzebne możemy je wyłączyć za pomocą podpolecenia off.

[user@server1 ~]$ sudo tuned-adm off
[user@server1 ~]$ sudo tuned-adm active
No current active profile.

Ćwiczenie 1: Zmiana domyślnego celu ładowania systemu

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1 zmień domyślny cel ładowania systemu na multi-user.target. Uruchom ponownie maszynę i za pomocą polecenia systemctl oraz who sprawdź zmiany. Następnie wróć do standardowego celu i ponownie zweryfikuj zmiany.

Ćwiczenie 2: Zapis własnego komunikatu diagnostycznego

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1 zapis swój komunikat diagnostyczny. Następnie wyświetl go w terminalu lub konsoli.

Ćwiczenie 3: Zastosowanie profili wydajności

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1 za pomocą polecenia tuned-adm Wyświetl listę profil, następnie ustaw profil balanced i potwierdź to za pomocą odpowiednie podpolecenia. Ustaw profil rekomendowany, następnie wyłacz profil. Pamiętaj! Może być konieczna ręczna aktywacja jednostki tuned.service.

Podsumowanie

W tym rodziale dowiedzieliśmy się w jaki sposób realizowany jest ostatni etap rozruchu systemu. Poznaliśmy odpowiedzialne za nie narzędzie oraz część jego możliwości. Dowiedzieliśmy się za co odpowiada rsyslogd oraz gdzie możemy szukać zapisanych w systemie komunikatów diagnostycznych. Na koniec poznaliśmy narzędzie które możemy dostoswać wydajność naszego komputera do określonych potrzeb. W następnym rodziale zajmiemy się podstawami obsługi dysków.

13. Podstawy zarządzania pamięcią masową

Dane na których operują użytkownicy, wyniki ich pracy czy programy przez nich używane muszą być gdzieś składowane, aby co ponowne uruchomienie komputera nie trzeba było konfigurować naszego środowiska na nowo. Odziwo istnieją użytkownicy, który korzystają z dystrybucji Linuksa w trybie LiveCD (systemu załadowanego z płyty do pamięci komputera). Aby móc wykorzystać komputer do składowania danych potrzebujemy specjalnych urządzeń - dysków, i to zarządzaniem dyskami zajmiemy się w tym rozdziale.

W tym rozdziale powinna znaleźć się także obsługą dysków za pomocą technologii VDO, jednak z RHEL 9 została ona wciągnieta do LVM, które będą omawaiane w następnym rozdziale. Także omówimy sobie VDO zaraz po nauczeniu się podstaw LVM.

Do wykonania ćwiczeń oraz przykładów będzie nam potrzebna maszyna oznaczona jak server2 (patrz: rodział 1).

13.1. Podstawowe pojęcia związane z zarządzaniem dyskami

Dyski w systemach uniksowych są urządzeniami blokowymi służącymi do przechowywania danych. Dyski mogą być podzielone na logiczne części nazwane partycjami. Partycje w ujęciu systemów operacyjnych są podstawową jednoską związaną z zarządzaniem dyskami, więc aby uzyskać dostęp do przestrzenii znajdującej się na dysku, musimy utworzyć na nim partycję. Na tym polega zarządzanie dyskami, na tworzeniu, zmienianiu i usuwaniu partycji. Informacja o partycjach zwana tablicą partycji znajdujących się na dysku znajduje się w pierwszym jego sektorze obok programu rozruchowego. Partycjami możemy zarządzać za pomocą dwóch schematów MBR oraz GPT.

13.1.1 Schemat partycjonowania MBR (Master Boot Record)

MBR jest jednym z pierwszych schematów partycjonowania, został on ustanowiony dawno temu, więc posiada pewne ograniczenia względem dysków stosowanych w obecnych komputerach. Schemat ten jest domyślnym schematem wykorzystywanym przez komputery obsługiwane z pomocą BIOS-u. Pozwala on podzielenie dysku na maksymalnie 4 partycje podstawowe (służące do przechowywania danych). Jeśli wymagana jest większa ilość, to wówczas należy poświęcić jedną z partycji podstawowych na partycję rozszerzoną (kontener na partycje logiczne), a następnie realizować dalsze partycjonowanie za pomocą partycji logicznych (te partycje również służą do przechowywania danych jednak różnią się one nieco do partycji podstawowych). Partycji logicznych na dysku może być maksymalnie 11, co daje nam maksymalną ilość partycji na dysku wynoszącą 14. W praktycje może oznaczać, że to więcej niż kto kolwiek, kiedy kolwiek potrzebował. Schemat ten ma jeszcze jedno ograniczenie otóż, nie może zaadresować przestrzenii większej niż 2TB ze względu na to iż jest to 32-bitowy schemat partycjonowania, jak wiemy obecne dostępne dyski dysponują przestrzenią powyżej 10TB.

13.1.2. Schemat patycjonowania GPT (GUID Partition Table)

Wraz rozwojem komputerów, wprowadzeniem nowego oprogramowania układowego jakim jest UEFI, zaczął być wdrażany kompatybilny z nim schemat GPT. Schemat ten nie posiada podziałów na partycje podstawowe, rozszerzone, logiczne itd. Wszystkie są podstawowe i może być ich aż do 128. Maksymalna pojemność dysków, które GPT może zaadresować jest nadal daleko po za zasięgiem ludzkości. Umożliwia on także nadawanie etykiet partycjom co nie było możliwe w przypadku MBR. Poza tym GPT, tworzy kopie obszaru na którym zajduje się informacja o partycjach zaraz przed końcem dysku, co również nie było realizowane w schemacie MBR.

13.1.3. Partycje dyskowe w systemie

Podczas partycjowania należy uważać aby nie nadpisać obecnie istniejących partycji, albo nie zostawiać między nimi przerw marnując tym samym przestrzeń dysku. Partycje w systemie są uprządkowane za pomocą numeracji. W przypadku partycjonowania ze schematem MBR, partycje podstawowe otrzymują numery od 1 do 4, natomiast numeracja dysków logicznych rozpocznyna sie od 5. W przypadku schematu GPT numeracja rozpoczynia się od 1. Za pomocą polecenia lsblk możemy wświetlić dostępne w systemie urządzenia blokowe:

[user@server2 ~]$ lsblk
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sr0            11:0    1    8G  0 rom  /mnt
vda           252:0    0   10G  0 disk 
├─vda1        252:1    0    1G  0 part /boot
└─vda2        252:2    0    9G  0 part 
  ├─rhel-root 253:0    0    8G  0 lvm  /
  └─rhel-swap 253:1    0    1G  0 lvm  [SWAP]
vdb           252:16   0  256M  0 disk 
vdc           252:32   0  256M  0 disk 
vdd           252:48   0  256M  0 disk 
vde           252:64   0  256M  0 disk 
vdf           252:80   0    4G  0 disk 
vdg           252:96   0    1G  0 disk 
vdh           252:112  0    1G  0 disk 

W moim przypadku dyski noszą nazwy vdX, gdzie X to litera porządkowa dysku zatem pierwszy dysk to vda, drugi vdb i tak dalej. Przy niektóych nazwach widnieją cyfry, te nazwy to nazwy partycji. W Twoim przypadku oznaczenia dysku mogą być nieco inne, ponieważ ja zamiast VirtualBox używam wirtualizacji KVM wraz ze innym sterownikiem odpowiedzialnym za obsługę pamięci masowych. Nie miej jednak polecenie z przykładu zwraca nazwę urządzenia, przedstawiając dysk w postaci drzewa, zwraca rozmiar zarówno dysków jak i partycji, typ urządzenia oraz punkt montowania partycji w systemie. Typ lvm oznacza logiczne woluminy LVM (będzie o tym w następny rozdziale).

13.1.4. Narzędzia do zarządzania pamięcią masową

W RHEL dostępnych jest wiele narzędzi odpowiedzialnych za zarządzanie dyskami. Każda technologia wdrożona do systemu posiada swoje oprogramowanie. W tym rozdziale skupimy się wyłączenia na dwóch z nich. Na programie parted pozwalającym na obsługę schematu MBR oraz gdisk obsługującym schemat GPT. Ze względu na to iż będzie pracować z urządzeniami komputera, do wykonywania czynności przy użyciu tych narzędzi będą potrzebne uprawnienia administratora.

13.1.5. Technologia thin provisioning

Omawiając podstawy zarządzania dyskami należałoby wspomnieć o technologii thin provisioning jest technika polegająca na przedstawianiu logiczynych zasobów dyskowy znacznie większych niż są one w rzeczywistości. Przez utworzone w ten sposób woluminy posiadają one znaczenie wiekszą pojemność niż utrzymujące je fizyczne urządzenia, kiedy dochodzi się do poziomu wyczerpywania fizycznego miejsca, rozszerza się wolumin o kolejne fizyczne dyski. Dzięki tej technologii można logicznie założyć duży zasób dyskowy do wykorzystania i jeśli rzeczywiście będzie potrzeba użycia takie ilości przestrzeni będzie się dodwać stopniowo kolejne dyski nie wydając pieniędzy na przestrzeń, której nigdy nie wykorzystamy.

13.2. Zarządzenie partycjami ze schematem MBR

W tym podrozdziale nauczymy się zarządzać partycjami ze schematem MBR przy użyciu narzędzia parted. Tego polecenia możemy używać albo w trybie interaktywnym albo bezpośrednio z wiersza polecenia. Poniższe czynności będą opierać się wyłącznie na obsłudze z poziomu wiersza polecenia. Na początek wyświetlimy sobie zawartość dysku.

[user@server2 ~]$ sudo parted /dev/vdb print
Error: /dev/vdb: unrecognised disk label
Model: Virtio Block Device (virtblk)                                      
Disk /dev/vdb: 268MB
Sector size (logical/physical): 512B/512B
Partition Table: unknown
Disk Flags: 

Za pomocą podpolecenia print możemy wyświetlić informacje na temat dysku oraz znajdujące się na nim partycje. Zwróćmy uwagę na pierwszą linię, mówi ona o tym, że nie znaleziony etykiety dysku, w tym przypadku chodzi o schemat partycjonowania (tablicę partycji). Zapamiętajmy też składnie tego polecenia, gdyż pierwszym argumentem zawsze jest nazwa urządzenia następnie podpolecenie wykonujące określoną czynność. Poniższe polecenie nada etykietę msdos ustawiając tym samym schemat na MBR.

[user@server2 ~]$ sudo parted /dev/vdb mklabel msdos
[sudo] password for user: 
Information: You may need to update /etc/fstab.

[user@server2 ~]$ sudo parted /dev/vdb print                      
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 268MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start  End  Size  Type  File system  Flags

Teraz kiedy zdecydowaliśmy o schemacie partycjonowania, możemy utworzyć partycję. Przy tworzeniu partycji jest pewien haczyk. Otóż podczas tworzenia partycji w parted należy podać początek oraz koniec. Zatem chcąc stworzyć partycję o wielkości 100 MB podajemy początek 1 (co jest równe 1M, jest również pierwsza partycja więc rozpoczyna się od pierwszego megabajta dysku) 101M stosując również skróty jednostek odnoszące się wyłacznie do wielkorotności jak M - megabajt, G - gigabajt itd. Następną partycję moglibyśmy zacząć od 101M i ustawiając jej koniec, na koniec pozostałej wolnej części dysku za pomocą takiego wyrażenia jak: 100%Free.

[user@server2 ~]$ sudo parted /dev/vdb mkpart primary 1 101M
[sudo] password for user: 
Information: You may need to update /etc/fstab.

[user@server2 ~]$ sudo parted /dev/vdb print
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 268MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start   End    Size    Type     File system  Flags
 1      1049kB  101MB  99.6MB  primary

Po podpoleceniu podajemy rodzaj partycji w tym przypadku jest to primary, następnie podajemy jej początek 1 oraz koniec 101M. Tak utworzą partycję możemy teraz usunąć za pomocą podpolecenia rm, które przyjmuje numer jako argument.

[user@server2 ~]$ sudo parted /dev/vdb rm 1
[sudo] password for user: 
Information: You may need to update /etc/fstab.

[user@server2 ~]$ sudo parted /dev/vdb print                              
Model: Virtio Block Device (virtblk)
Disk /dev/vdb: 268MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start  End  Size  Type  File system  Flags

13.3. Zarządzanie partycjami ze schematem GPT

Zarządzanie partycjami ze schematem GPT różni się od tego ze schematem MBR. Nie tylko tym, że wykorzystuje się do tego inne narzędzia. Te narzędzia mają również inną zasadę działania. Do zarządzania partycjami GPT wykorzystuje się narzędzie gdisk, które jest narzędziem interaktywnym, jego różnica polega na tym, że nie zapisze ono zmian na dysku do póki nie użyjemy odpowiednie polecenia. Polecenia w tym narzędziu przyjmują postać pojedynczych liter, a ich listę możemy uzyskać w momencie wydania polecenia znaku zapytania (?). Polecenie uruchamia się podając nazwę urządzenia jako argument.

[user@server2 ~]$ sudo gdisk /dev/vdd
GPT fdisk (gdisk) version 1.0.7

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries in memory.

Command (? for help): ?
b	back up GPT data to a file
c	change a partition's name
d	delete a partition
i	show detailed information on a partition
l	list known partition types
n	add a new partition
o	create a new empty GUID partition table (GPT)
p	print the partition table
q	quit without saving changes
r	recovery and transformation options (experts only)
s	sort partitions
t	change a partition's type code
v	verify disk
w	write table to disk and exit
x	extra functionality (experts only)
?	print this menu

Command (? for help): 

Polecenie na początku zwróci listę używanych na dysku schematów tablicy partycji. Następnie wydałem odpowiednie polecenie aby wyświetić listę dostępnych polecenień. Jak widać na dysku nie ma żadnej tablicy za pomocą polecenia o zostanie stworzona nowa tablica partycji (miejsce z informacjami o partycjach dysku) w schemacie GPT.

Command (? for help): o       
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): Y
Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/vdd.
The operation has completed successfully.

Poza utworzeniem nowej tablicy partycji ze schematem GPT, program zabezpieczy MBR dla wstecznej zgodności z komputerami obsługiwanymi przez BIOS. Na koniec wyszedłem, aby zapisać zmiany i uruchomiłem program ponownie.

[user@server2 ~]$ sudo gdisk /dev/vdd
GPT fdisk (gdisk) version 1.0.7

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): 

Teraz program przedstawia, że odnalazł prawidłową tablicę ze schematem GPT oraz zabezpieczony MBR. Teraz aby utworzyć partycję użyje opcji n następnie pozostawie część domyślnych informacji. Podam tylko jej wielkość w linii oznaczonej jako ostatnii sektor. Wiecej szczegółów znajduje się na przykładzie poniżej:

Command (? for help): n
Partition number (1-128, default 1): 
First sector (34-524254, default = 2048) or {+-}size{KMGTP}: 
Last sector (2048-524254, default = 524254) or {+-}size{KMGTP}: +100M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 
Changed type of partition to 'Linux filesystem'

Command (? for help): p   
Disk /dev/vdd: 524288 sectors, 256.0 MiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): F5BE61A0-220E-409E-AB2F-18AFDD56D410
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 524254
Partitions will be aligned on 2048-sector boundaries
Total free space is 319421 sectors (156.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          206847   100.0 MiB   8300  Linux filesystem

Warto zwrócić uwagę na to w jaki sposób została podana wielkość partycji (+100M). Za pomocą opcji p możemy wyświetlić zmiany jakie zaszły póki co w pamięci. Więc żeby zmiany, które zostały utworzone zapisać podajemy polecnie w, nastepnie potwierdzamy zapisanie zmian.

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/vdd.
The operation has completed successfully.

Aby usunąc partycję podajemy opcję d, która następnie poprosi o podanie numery partycji o ile jest więcej niż jedna. Jeśli jest tylko jedna automatycznie ją usunie.

[user@server2 ~]$ sudo gdisk /dev/vdd
[sudo] password for user: 
GPT fdisk (gdisk) version 1.0.7

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): d
Using 1

Command (? for help): p
Disk /dev/vdd: 524288 sectors, 256.0 MiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): F5BE61A0-220E-409E-AB2F-18AFDD56D410
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 524254
Partitions will be aligned on 2048-sector boundaries
Total free space is 524221 sectors (256.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name

Oczywiście jeśli przypadkowo coś usuneliśmy lub zmieniliśmy możemy użyć opcji q, aby zakończyć działanie programu bez zapisywania zmian na dysku.

Odnośnie egzaminu:
Znajomość zarówno parted oraz gdisk jest wystarczająca, aby zrealizować proste zadania związane z zarządzaniem dyskami na egzaminie.

Ćwiczenie 1: Zarządzanie partycjami za pomocą parted

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server2 utwórz jedną 100 MB partycję na jednym z dysków o pojemności 250 MB. Użyj do tego tablicy o schemacie MBR. Do utworzenia partycji użyj narzędzia parted z poziomu wiersza polecenia. Nastepnie utworz taką samą partycję ale w trybie interaktywnym tego polecenia. Wyświetl informacje na temat dysku. Na koniec usuń obie partycje używając wiersza polecenia.

Ćwiczenie 2: Zarządzanie partycjami za pomocą gdisk

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server2 utwórz przy tablicy w schemacie GPT dwie partycje po 80MB każda. Wyświetl informacje na temat dysku. Jeśli chcesz usuń te partycje.

Podsumowanie

W tym rozdziale poznaliśmy podstawy teoretyczne oraz praktyczne zarządzania dyskami. Nauczyliśmy się tworzenia oraz usuwania partycji przy różnych schematach tablic. W następnym rozdziale przejdziemy do bardziej zaawansowanych czynności związanych z zarządzaniem pamięcią masową w RHEL.

14. Zaawansowane zarządzanie pamięcią masową

W tym rodziale zajmiemy się bardziej zawansowanymi zagadnieniami związanymi zarządzaniem pamiecią masową. W tym rozdziale poruszymy również pominięty z względu na zmiany w RHEL podrozdział związany z bardziej efektywnym wykorzystaniem przestrzeni na dysku dzięki VDO. Kolejnym zagadnienieniem będzie LVM, który pozwala spojrzeć nieco inaczej na urządzenia służące do przechowywania danych. Ostatnim tematem będzie Stratis, który łączy ze sobą kilka komponentów systemu pozwalających na stworzenie stablinego i wydajnego systemu plików (o systemach plików będzie w następnym rozdziale).

14.1. Zarządzanie pamięcią masową przy użyciu LVM

LVM, czyli Logical Volume Manager pozwala inaczej spojrzeć na dostępne w naszych komputerach dyski. Za pomocą tego narzędzia możemy zebrać wszystkie dostępne w systemie dyski zainicjować je, aby były gotowe do pracy z LVM, wówczas dyski stają się woluminami fizycznymi. Woluminy fizyczne łączy się w grupę woluminów, która staje się pulą to tworzenie nowych urządzeń dyskowych bazujących na miejscu dostępnym w grupie. Takie urządzenia noszą nazwę woluminów logicznych. W ujęciu LVM najmiejszą logiczną jednostką alokacji przestrzeni dyskowej są ekstenty. Ekstenty dzielą się na logiczne będące cząstkami woluminów logicznych oraz na fizyczne będące elementami woluminów fizycznych. Ekstenty podczas tworzenia grupy woluminów mają domyślną wielkość wynoszącą około 4 MB. Oczywiście można to zmienić. A wielkości ekstentów (logicznych i fizycznych) nie muszą być sobie równe. Pojęcie ekstenów będzie potrzebne nam przy VDO.

Elementy logiczne LVM są bardzo eleastyczne, może je dodawać usuwać, rozszerzać, zmniejszać i zmieniać wcześniej nadane nazwy. Tym się właśnie zajmiemy w tym rodziale. Oczywiście będziemy pracować z urządzeniami więc niezbędne będą uprawnienia administratora.

14.1.1. Tworzenie woluminów fizycznych

Aby rozpocząć pracę z LVM musimy przekształcić dysk lub partycję aby nadawała się do pracy z tym rozwiązaniem. Na tych urządzeniach blokowych zostaną utworzone specjalne struktury, dzięki czemu będzie można później włączyć je do grupy.

Chcąc użyć partycji jako woluminu fizycznego tworzmy partycje, bez znaczenia czy jest schemat tablicy partycji GPT czy MBR, następnie partycji ustawiamy flagę lvm. Tak przygotowana partycja jest gotowa by użyć jej jako woluminu fizycznego. Poniżej znajduje się przykład przedstawiający jak to zrealizować.

[user@rhel90 ~]$ sudo gdisk /dev/vdb
GPT fdisk (gdisk) version 1.0.7

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries in memory.

Command (? for help): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): Y

Command (? for help): p
Disk /dev/vdb: 524288 sectors, 256.0 MiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 66CCB514-9659-461D-834B-1661E9D112F4
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 524254
Partitions will be aligned on 2048-sector boundaries
Total free space is 524221 sectors (256.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name

Command (? for help): n
Partition number (1-128, default 1): 
First sector (34-524254, default = 2048) or {+-}size{KMGTP}: 
Last sector (2048-524254, default = 524254) or {+-}size{KMGTP}: +128M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8e00
Changed type of partition to 'Linux LVM'

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/vdb.
The operation has completed successfully.

Tak utworzoną partycje możemy zainicjować jako wolumin fizyczny za pomocą polecenia pvcreate.

[user@rhel90 ~]$ sudo pvcreate /dev/vdb1
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Physical volume "/dev/vdb1" successfully created.

Teraz wolumin nadaje się do podłączenia do grupy.

Inicjowanie całych dysków jest o wiele prostsze. Wystarczy użyć polecenie pvcreate i podać nazwę urządzenia jako argument.

[user@rhel90 ~]$ sudo pvcreate /dev/vdc
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Physical volume "/dev/vdc" successfully created.

Listę dostępnych w systemie woluminów fizycznych możemy wyświetlić za pomocą polecenia pvs.

[user@rhel90 ~]$ sudo pvs
[sudo] hasło użytkownika user: 
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  PV         VG   Fmt  Attr PSize   PFree  
  /dev/vda2  rhel lvm2 a--  <11,00g      0 
  /dev/vdb1       lvm2 ---  128,00m 128,00m
  /dev/vdc        lvm2 ---  256,00m 256,00m

Kolumny oznaczają kolejno: fizyczny wolumin (PV), grupę do której należy fizyczny wolumin (VG), format (Fmt), atrybuty (Attr, wartości poszczególnych bitów są rozpisane w na stronie podręcznika), rozmiar fizyczny (PSize) oraz fizyczną ilość wolnego miejsca (PFree).

14.1.2. Tworzenie grup woluminów

Posiadając już odpowiednie zasoby w postaci woluminów fizycznych, możemy utworzyć grupę woluminów, aby móc zarządzać zaawansowanymi właściwościami LVM. Grupy tworzone są za pomocą polecenia vgcreate. Utworzę teraz grupę z wcześniej stworzonych woluminów.

[user@rhel90 ~]$ sudo vgcreate vg_course /dev/vdb1 /dev/vdc
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Volume group "vg_course" successfully created

Polecenie przyjmuje jako pierwszy argument nazwę grupy a następnie wchodzące w jej skład fizyczne woluminy. Listę utworzonych w systemie grup możemy wyświetlić za pomocą polecenia vgs. Natomiast bardziej szczegółowe informacje na temat grupy możemy uzyskać więcej informacji za pomocą polecenia vgdisplay.

[user@rhel90 ~]$ sudo vgs
[sudo] hasło użytkownika user: 
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  VG        #PV #LV #SN Attr   VSize   VFree  
  rhel        1   2   0 wz--n- <11,00g      0 
  vg_course   2   0   0 wz--n- 376,00m 376,00m

Polecenie to zwraca informacje podobne do poprzedniego polecenia pvs. Dodatkowe kolumny zwracają informacje na temat ilość woluminów fizycznych w grupie (#PV), ilość woluminów logicznych (#LV) oraz ilość snapshotów w grupie woluminów. Reszta kolumn jest podobna do tych informacji zwracanych przez pvs. Innym narzędziem zwracającym więcej informacji na temat grupy woluminów jest vgdisplay, jako argument przyjmuje ona nazwę grupy, jeśli zostanie pominięta polecenie zwróci informacje na temat wszystkich grup w systemie.

[user@rhel90 ~]$ sudo vgdisplay vg_course 
[sudo] hasło użytkownika user: 
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  --- Volume group ---
  VG Name               vg_course
  System ID             
  Format                lvm2
  Metadata Areas        2
  Metadata Sequence No  1
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                2
  Act PV                2
  VG Size               376,00 MiB
  PE Size               4,00 MiB
  Total PE              94
  Alloc PE / Size       0 / 0   
  Free  PE / Size       94 / 376,00 MiB
  VG UUID               g4X6FW-P1fu-jS0t-UaGg-8mIK-Kv0J-2bLWSD

Polecenie to zwraca rozmiar fizycznych ekstentów, który w tym przypadku posiada wartość domyślną. Rozmiar ten możemy zmienić podając opcje -s oraz wielkość fizycznych ekstentów jako argument opcji podczas tworzenia grupy. Chcąc wydobyć konkretne informacje z tego polecenia możemy posłużyć się poleceniem grep.

[user@rhel90 ~]$ sudo vgdisplay vg_course | grep 'PE Size'
[sudo] hasło użytkownika user: 
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  PE Size               4,00 MiB

14.1.3. Tworzenie woluminów logicznych LVM

Do tworzenia woluminów logicznych służy polecenie lvcreate. Wydając to polecenie wraz z opcją --help, zostaną nam zwrócone wszystkie możliwe rodzaje segmentów woluminów logicznych. W tym materiale będą nas interesować tylko dwa linear (liniowy, zwykły) oraz VDO. O VDO będzie w następnym podrozdziale. Zatem do stworzenia liniowego woluminu logicznego potrzebujemy wielkości (opcja -L) oraz nazwy grupy w której ma zostać stworzony. Inną dość przydatną opcją, niezależną od rodzaju woluminu jest opcja -n, która pozwala na nadanie nazwy woluminowi. Teraz utworzę wolumin logiczny w grupie, którą wcześniej utworzyłem.

[user@rhel90 ~]$ sudo lvcreate -L 100MB -n lvol0 vg_course
  Logical volume "lvol0" created.

Czasami podana wielkość woluminu może zostać zaokrąglona, ze względu na potrzebę użycia okrągłej liczby ekstentów. W przypadku tego woluminu zostały użyte 25 ekstenty (25 x 4MB = 100MB), co możemy podejrzeć wydając polecenie vgdisplay z opcją -v Przez co zwróci ona bardziej szczegółowe informacje na temat grupy, w tym informacje na temat woluminów logicznych w niej zawartych.

[user@rhel90 ~]$ sudo vgdisplay -v vg_course
...
  --- Logical volume ---
  LV Path                /dev/vg_course/lvol0
  LV Name                lvol0
  VG Name                vg_course
  LV UUID                Z2cH37-sWGu-NEIR-JoKN-AT5M-o5EI-d8QmKU
  LV Write Access        read/write
  LV Creation host, time rhel90, 2022-07-22 11:35:47 +0200
  LV Status              available
  # open                 0
  LV Size                100,00 MiB
  Current LE             25
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:2
...

Innm narzędziem, które zwraca informacje na temat woluminów logicznych jest lvs. Zwraca ono podobne informacje do vgs na przykład:

[user@rhel90 ~]$ sudo lvs
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  LV   VG   Attr       LSize Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root rhel -wi-ao---- 9,79g                                                    
  swap rhel -wi-ao---- 1,20g       

Informacje zawierają nazwę woluminu (LV), grupę woluminów (VG), atrybuty (Attr) oraz wielkości (LSize).

Poza klasyczną wielkością podawną w wielkrotnościach bajtów, wielkość woluminu możemy podawać ilości ekstentów, służy do tego opcją -l.

Tak utworzone woluminy są pełno prawnymi urządzeniami blokowymi przypominającymi partycję. Możemy zainstalować na nich odpowiedni system plików (będzie o tym w następnym rozdziale), zamontować je w systemie i wykorzystać do przechowywania danych.

14.1.4. Zmiana nazwy elementów LVM

Nazwę możemy zmienić takim elementom jak wolumin logiczny - polecenie lvrename oraz grupa woluminów - polecenie vgrename. Polecenie lvrename wymaga podania scieżki do woluminu (znajdziemy go za pomocą polecenia vgdisplay -v) oraz nowej nazwy. Możemy ominąć potrzebę podawania ścieżki do woluminu, poprzedzając jego nazwę, nazwą grupy. Ze zmianą nazwy grupy jest o wiele prościej wystarczy podać poleceniu vgrename starą nazwę grupy oraz nową.

Zmiana nazwy woluminu za pomoca ścieżki woluminu:

[user@rhel90 ~]$ sudo lvrename /dev/vg_course/lvol0 rhelvol0
  Renamed "lvol0" to "rhelvol0" in volume group "vg_course"

Zmiana nazwy woluminu za pomocą nazwy grupy:

[user@rhel90 ~]$ sudo lvrename vg_course rhelvol0 rhel9vol0
  Renamed "rhelvol0" to "rhel9vol0" in volume group "vg_course"

Zmiana nazwy grupy:

[user@rhel90 ~]$ sudo vgrename vg_course rhcsa9
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Volume group "vg_course" successfully renamed to "rhcsa9"

14.1.5. Rozszerzanie elementów LVM

Za pomocą poleceń vgextend możemy rozszerzyć grupę woluminów o dodatkowe woluminy fizyczne. Natomiast za pomocą polecenia lvextend możemy rozszerzyć wolumin logiczny o dodatkowe miejsce, przy czym wolumin należy podać w postaci ścieżki.

Rozszerzenie grupy woluminów o kolejny wolumin fizyczny:

[user@rhel90 ~]$ sudo vgextend rhcsa9 /dev/vdd
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Volume group "rhcsa9" successfully extended

Rozszerzenie woluminów logicznych o dodatkową przestrzeń:

[user@rhel90 ~]$ sudo lvextend -L +100M /dev/rhcsa9/rhel9vol0
  Size of logical volume rhcsa9/rhel9vol0 changed from 100,00 MiB (25 extents) to 200,00 MiB (50 extents).
  Logical volume rhcsa9/rhel9vol0 successfully resized.

Zwróćmy uwagę w jaki sposób podajemy wielkość o którą chcemy rozszerzyć podany wolumin.

14.1.6. Zmniejszanie elementów LVM

Za pomocą kolejnej pary poleceń lvreduce, oraz vgreduce możemy zmniejszyć ilość miejsca w przypadku woluminów logicznych oraz usunąć z grupy okreslone woluminy fizyczne. Przyczym warto dodać, o ile rozszerzenie miejsca nie było działaniem destrukcyjnym (gdy np. na woluminach logicznych znajdują sie systemy plików oraz dane) to zmieniejszanie z całą pewnością jest takim działaniem.

Zmniejszenie woluminu logicznego o 50 MB, przy czym wielkość możemy podać odejmując podaną wartość lub podając nową wielkość dla woluminu. Obie formy są poprawne.

[user@rhel90 ~]$ sudo lvreduce -L -50M /dev/rhcsa9/rhel9vol0
[sudo] hasło użytkownika user: 
  Rounding size to boundary between physical extents: 48,00 MiB.
  WARNING: Reducing active logical volume to 152,00 MiB.
  THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you really want to reduce rhcsa9/rhel9vol0? [y/n]: y
  Size of logical volume rhcsa9/rhel9vol0 changed from 200,00 MiB (50 extents) to 152,00 MiB (38 extents).
  Logical volume rhcsa9/rhel9vol0 successfully resized.

[user@rhel90 ~]$ sudo lvreduce -L 100M /dev/rhcsa9/rhel9vol0
  WARNING: Reducing active logical volume to 100,00 MiB.
  THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you really want to reduce rhcsa9/rhel9vol0? [y/n]: y
  Size of logical volume rhcsa9/rhel9vol0 changed from 152,00 MiB (38 extents) to 100,00 MiB (25 extents).
  Logical volume rhcsa9/rhel9vol0 successfully resized.

Usunięcie z grupy jednego z woluminów fizycznych.

[user@rhel90 ~]$ sudo vgreduce rhcsa9 /dev/vdd
[sudo] hasło użytkownika user: 
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Removed "/dev/vdd" from volume group "rhcsa9"

14.1.7. Wycofywanie zmian LVM na dyskach

Wycofywanie zmian rozpoczniemy od usunięcia woluminów logicznych a służy temu polecenie lvremove, aby usunąć wolumin podaje się nazwę grupy oraz nazwę woluminu:

[user@rhel90 ~]$ sudo lvremove rhcsa9 rhel9vol0
  Devices file /dev/vdb is excluded by filter: device is partitioned.
Do you really want to remove active logical volume rhcsa9/rhel9vol0? [y/n]: y
  Logical volume "rhel9vol0" successfully removed.
  Volume group "rhel9vol0" not found
  Cannot process volume group rhel9vol0

Kombinację tych dwóch wartości możemy zastąpić ścieżką do woluminu, a potwierdzenie możemy wymusić za pomocą opcji -f.

Po usunięciu woluminów przyszedł czas na usunięcie grupy woluminów, do tego należy wykorzystać polecenie vgremove. Polecenie to przyjmuje nazwę grupy jako argument.

[user@rhel90 ~]$ sudo vgremove rhcsa9
[sudo] hasło użytkownika user: 
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Volume group "rhcsa9" successfully removed

Po usunięciu grupy pozostaje tylko wyczyszcznie dysków ze wszelkich struktur stworzonych na potrzeby LVM. Polecenie, które wykona tę czynność to pvremove.

[user@rhel90 ~]$ sudo pvremove /dev/vdb1 /dev/vdc
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Labels on physical volume "/dev/vdb1" successfully wiped.
  Labels on physical volume "/dev/vdc" successfully wiped.

14.2. Wykorzystanie VDO do zarządzania pamięciami masowymi

VDO, czyli Virtual Disk Optimizer jest technologia zarządzania pamięciami masowymi pozwalająca na zaoszczędzenie wykorzystywanej przestrzeni, zwiększenie wydajności przesyłanych danych oraz zaoszczędzenie pieniędzy przeznaczonych na zakup dysków. VDO wykorzystuje takie techniki jak kompresja, technologia thin provisioning oraz unikanie tworzenia duplikatów plików. Kiedyś VDO było odrębną techniką tak jak LVM czy Stratis. Obecnie od RHEL 8.5 zostało zunifikowane wraz z LVM jako jeden z rodzajów logicznych woluminów. Tym zajmiemy się tym podrodziale otóż utworzymy logiczny volumin VDO.

Aby użyć wolumin typu VDO musimy dysponować co najmniej 5GB przestrzenii w grupie woluminów. Ja poniżej stworzyłem grupę składającą się z jednego 10GB fizycznego voluminu.

[user@rhel90 ~]$ sudo pvcreate /dev/vdf
[sudo] hasło użytkownika user: 
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Physical volume "/dev/vdf" successfully created.
[user@rhel90 ~]$ sudo vgcreate vg-vdo /dev/vdf
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Volume group "vg-vdo" successfully created
[user@rhel90 ~]$ sudo vgs
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  VG     #PV #LV #SN Attr   VSize   VFree  
  rhel     1   2   0 wz--n- <11,00g      0 
  vg-vdo   1   0   0 wz--n- <10,00g <10,00g

Przed utworzeniem woluminu, musimy poznać ilość ekstentów dostępnych w grupie. Za pomocą poniższego polecenia możemy ustalić te liczbę. Jest ona istotna, ponieważ jeśli jej nie podamy nie będziemy mogi skorzystać z thin provisioning.

[user@rhel90 ~]$ sudo vgdisplay -v vg-vdo | grep 'Free PE'
[sudo] hasło użytkownika user: 
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  Total PE / Free PE    2559 / 2559

Teraz możemy za pomocą polecenia lvcreate utworzyć wolumin logiczny w stylu VDO:

[user@rhel90 ~]$ sudo lvcreate --type vdo -n vdo-vol0 -l 2559 -V 1TB vg-vdo
    The VDO volume can address 6 GB in 3 data slabs, each 2 GB.
    It can grow to address at most 16 TB of physical storage in 8192 slabs.
    If a larger maximum size might be needed, use bigger slabs.
  Logical volume "vdo-vol0" created.

W ten sposób stworzyłem wolumin w stylu VDO, za pomocą opcji -V podaje wirtualną wielkość tego urządzenia. Polecenie poza utworzeniem podanego woluminu utworzy dodatkowy wolumin puli. Tak utworzony wolumin VDO może nam posłużyć do przechowywania danych na serwerze. Poniżej znajduje się wynik działania poleceń lvs oraz vgdisplay.

[user@rhel90 ~]$ sudo lvs -a vg-vdo
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  LV             VG     Attr       LSize   Pool   Origin Data%  Meta%  Move Log Cpy%Sync Convert
  vdo-vol0       vg-vdo vwi-a-v---   1,00t vpool0        0,00                                   
  vpool0         vg-vdo dwi------- <10,00g            40,02                                  
  [vpool0_vdata] vg-vdo Dwi-ao---- <10,00g                                                      

Zwróćmy uwagę, że samo utrzymanie takiego woluminu, kosztuje nas 4GB miejsca na dysku i nie jest to zależne od jego rozmiaru. Poniżej znajdują się informacje uzyskane z polecenia vgdisplay:

[user@rhel90 ~]$ sudo vgdisplay -v vg-vdo
  Devices file PVID none last seen on /dev/vdd1 not found.
  Devices file PVID none last seen on /dev/vdd2 not found.
  Devices file PVID none last seen on /dev/vdc1 not found.
  --- Volume group ---
  VG Name               vg-vdo
  System ID             
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  16
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                2
  Open LV               0
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               <10,00 GiB
  PE Size               4,00 MiB
  Total PE              2559
  Alloc PE / Size       2559 / <10,00 GiB
  Free  PE / Size       0 / 0   
  VG UUID               qPdY4n-YQUG-x5tu-szTe-B3bT-idC8-vseZXH
   
  --- Logical volume ---
  LV Path                /dev/vg-vdo/vpool0
  LV Name                vpool0
  VG Name                vg-vdo
  LV UUID                H9mloX-8x8N-4OHz-HHvz-qWhZ-dnJE-Sq1sCX
  LV Write Access        read/write
  LV Creation host, time rhel90, 2022-07-22 15:17:50 +0200
  LV VDO Pool data       vpool0_vdata
  LV VDO Pool usage      40,02%
  LV VDO Pool saving     100,00%
  LV VDO Operating mode  normal
  LV VDO Index state     online
  LV VDO Compression st  online
  LV VDO Used size       4,00 GiB
  LV Status              NOT available
  LV Size                <10,00 GiB
  Current LE             2559
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
   
  --- Logical volume ---
  LV Path                /dev/vg-vdo/vdo-vol0
  LV Name                vdo-vol0
  VG Name                vg-vdo
  LV UUID                sc5RL6-LfF6-jzCT-6TNK-c4Q3-J2sf-m1ZufS
  LV Write Access        read/write
  LV Creation host, time rhel90, 2022-07-22 15:17:53 +0200
  LV VDO Pool name       vpool0
  LV Status              available
  # open                 0
  LV Size                1,00 TiB
  Current LE             262144
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:4
   
  --- Physical volumes ---
  PV Name               /dev/vdf     
  PV UUID               kAxqd2-v4Xo-nmB8-311H-nM2d-DtHn-WutyCf
  PV Status             allocatable
  Total PE / Free PE    2559 / 0

W RHEL pozostało jeszcze jedno polecenie, które pozwala zwrócić statystykę na temat obecnego w systemie woluminu puli VDO, takim poleceniem jest vdostats. Poniżej znajduje się przykład jego użycia.

[user@rhel90 ~]$ sudo vdostats --si
Device                    Size      Used Available Use% Space saving%
vg--vdo-vpool0-vpool     10.7G      4.3G      6.4G  40%            0%

Na powyższym przykładzie użyłem opcji --si, aby przeskalować jednostki do poziomu czytelnego dla człowieka.

Usuwanie takiego woluminu wygląda jak usuwanie każdego innego woluminu, jednak w tym przypadku program zapyta czy usunąć także zależny wolumin puli.

14.4. Zarządzanie pamięcią masową za pomocą Stratis

Stratis jest to technologia łącząca ze sobą trzy kompnenty systemowe, pozwalając na łączenie dysków w pule oraz tworzenie już gotowych do montowania systemów plików typu XFS. Stratis jest praktycznie rzecz biorąc bardzo podobny do LVM jednak nie tworzymy logicznych woluminów, a gotowe systemy plików, grupa do której należą dyski nazwane w tym przypadku blockdev nazywana jest pulą.

Stratis nie jest natywnie dostępny w systemie i trzeba go zainstalować. Pakiety znajdują się w repozytorium na płycie instalacyjnej z RHEL 9. Aby zainstalować w Stratis za systemie wydajemy poniższe polecenie:

[user@rhel90 ~]$ sudo dnf install stratisd stratis-cli -y
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 0:36:10 temu w dniu pią, 22 lip 2022, 15:23:29.
Pakiet stratisd-2.4.2-3.el9.x86_64 jest już zainstalowany.
Pakiet stratis-cli-2.4.3-2.el9.noarch jest już zainstalowany.
Rozwiązano zależności.
Nie ma nic do zrobienia.
Ukończono.

Stratis występuje w systemie w postaci demona, dlatego też należy włączyć i uruchomić jego jednostkę.

[user@rhel90 ~]$ sudo systemctl enable stratisd.service
[user@rhel90 ~]$ sudo systemctl start stratisd.serivce
[user@rhel90 ~]$ sudo systemctl status stratisd
● stratisd.service - Stratis daemon
     Loaded: loaded (/usr/lib/systemd/system/stratisd.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2022-07-22 08:16:57 CEST; 7h ago
       Docs: man:stratisd(8)
   Main PID: 758 (stratisd)
      Tasks: 6 (limit: 7746)
     Memory: 4.1M
        CPU: 8.731s
     CGroup: /system.slice/stratisd.service
             └─758 /usr/libexec/stratisd --log-level debug

lip 22 08:16:56 rhel90 stratisd[758]: [2022-07-22T06:16:56Z INFO  libstratis::stratis::run] stratis daemon version 2.4.2 started
lip 22 08:16:56 rhel90 stratisd[758]: [2022-07-22T06:16:56Z INFO  libstratis::stratis::run] Using StratEngine
lip 22 08:16:56 rhel90 stratisd[758]: [2022-07-22T06:16:56Z INFO  libstratis::engine::strat_engine::liminal::identify] Beginning initial ...
lip 22 08:16:56 rhel90 stratisd[758]: [2022-07-22T06:16:56Z DEBUG libstratis::stratis::run] 2: thread started
lip 22 08:16:56 rhel90 stratisd[758]: [2022-07-22T06:16:56Z DEBUG libstratis::stratis::run] 3: thread started
lip 22 08:16:57 rhel90 stratisd[758]: [2022-07-22T06:16:57Z INFO  libstratis::stratis::ipc_support::dbus_support] D-Bus API is available
...

14.3.1. Tworzenie puli Stratis

Teraz kiedy Stratis jest uruchomiony. Możemy przejść do utworzenia puli. Do utworzenia puli użyjemy podpolecenia pool polecenia stratis oraz podpolecenia create podpolecenia pool. Jako argumenty podajemy nazwę puli oraz listę dysków.

[user@rhel90 ~]$ sudo stratis pool create rhcsa /dev/vdg
[sudo] hasło użytkownika user: 

Informacje na temat puli możemy uzyskać za pomocą podpolecenia list podpolecenia pool.

[user@rhel90 ~]$ sudo stratis pool list
Name                    Total Physical   Properties                                   UUID
rhcsa   1 GiB / 37.63 MiB / 986.37 MiB      ~Ca,~Cr   5091bc53-7693-4a92-a434-fe8e682d0f58

Z informacji zwracanych z tego polecenie nas na tym etapie powinno interesować kolumna zawierająca informacje o wielkości puli. Teraz możemy utworzyć system plików.

14.3.2. Tworzenie systemu plików Stratis

Tworzenie systemu plików odbywa się za pomocą podpolecenia create, podpolecenia filesystem. To podpolecenie wymaga jako argumenty nazwę puli oraz nazwę systemu plików.

[user@rhel90 ~]$ sudo stratis filesystem create rhcsa rhcsafs0
[user@rhel90 ~]$ sudo stratis filesystem
Pool Name   Name       Used      Created             Device                        UUID                                
rhcsa       rhcsafs0   546 MiB   Jul 22 2022 16:34   /dev/stratis/rhcsa/rhcsafs0   5ca8e5b1-f32a-42e0-ac90-29777b3fc8ed

Zwróćmy uwagę na to, iż Stratis sam dobiera na podstawie wielkości puli wielkość systemu plików. Tak utworzony system jest dostępny pod ścieżką w kolumnie Device i gotowy do montowania.

14.3.3. Rozszerzenie puli oraz zmiana nazwy puli i systemu plików Stratis

Do rozszerzenia puli Stratis służy podpolecenie add-data podpolecenia pool. To podpolecenie oczekuje podania nazwy urządzenia dyskowego.

[user@rhel90 ~]$ sudo stratis pool add-data rhcsa /dev/vdh
[user@rhel90 ~]$ sudo stratis blockdev
Pool Name   Device Node   Physical Size   Tier
rhcsa       /dev/vdg              1 GiB   Data
rhcsa       /dev/vdh              1 GiB   Data

Za pomoca podpolecenia blockdev, mamy możliwość podejrzenia przypisanych do puli dysków. Jak widzimy teraz w puli znajdują się dwa dyski, zatem będziemy mogli utworzyć kolejny system plików, ponieważ samych systemów plików nie możemy zmienić. Teraz zmienimy nazwę puli oraz nazwę systemu plików.

Do zmiany nazw służy podpolecenie rename kolejno podpolecenia pool oraz filesystem. Na poniższym przykładzie znajduje się zastosowanie tego podpolecenia:

[user@rhel90 ~]$ sudo stratis pool rename rhcsa rhcsa9
[user@rhel90 ~]$ sudo stratis filesystem rename rhcsa9 rhcsafs0 rhcsa9fs0

14.3.4. Usuwanie elementów Stratis

Za pomocą podpolecenia destroy możemy usunąć zarówno system plików jak i pulę Stratis. Poniżej znajduje się przykład użycia, tego podpolecenia w innych ujęciach.

Usunięcie systemu plików:

[user@rhel90 ~]$ sudo stratis filesystem destroy rhcsa9 rhcsa9fs0

Usunięcie puli Stratis:

[user@rhel90 ~]$ sudo stratis pool destroy rhcsa9

Żadna pula już w systemie nie istnieje.

[user@rhel90 ~]$ sudo stratis pool
Name   Total Physical   Properties   UUID

Odnośnie egzaminu:
Nie musimy uczyć się na pamięć podpoleceń czy składni. Wiekszość informacji możemy uzyskać wydając polecenie z opcją --help lub wydać samo podpolecenie bez argumentów. Jedyne o czym należy pamiętać to które polecenie lub podpolecenie jest od czego.

Ćwiczenie 1: Tworzenie grup woluminów oraz logicznych woluminów

Jako użytkownik z możliwością podniesienia uprawnień na serwerze oznaczonym jako server2. Stwórz z jedego z 250MB dysków wolumin fizyczny następnie utwórz grupę vg100 ustawiając wielkość fizycznych ekstentów na 16MB, wykorzystując utworzony fizyczny wolumin. Utwórz dwa woluminy logiczne o nazwach kolejno lvol0 oraz swapvol o wielkości 100 oraz 120MB. Zweryfikuj wykonane wcześniej czynności za pomocą poleceń: pvs, lvs, vgs oraz vgdisplay.

Ćwiczenie 2: Rozszerzenie grupy woluminów oraz logicznego woluminu

Jako użytkownik z możliwością podniesienia uprawnień na serwerze oznaczonym jako server2. Użyj kolejnego dysku o wielkości 250MB jako woluminu fizycznego LVM. Dodaj nowy wolumin do grupy z poprzedniego ćwiczenia. Rozszerz pojemność woluminu logicznego lvol0 do 300MB. Sprawdź wykonane czynności za pomocą poleceń: pvs, lvs, vgs oraz vgdisplay.

Ćwiczenie 3: Redukcja oraz usunięcie woluminów logicznych

Jako użytkownik z możliwością podniesienia uprawnień na serwerze oznaczonym jako server2. Zmniejsz rozmiar woluminu logicznego lvol0 do 80MB. Usuń oba woluminy logiczne. Sprawdź wykonane czynności za pomocą poleceń: pvs, lvs, vgs oraz vgdisplay.

Ćwiczenie 4: Wycofywanie zmian LVM na dyskach

Jako użytkownik z możliwością podniesienia uprawnień na serwerze oznaczonym jako server2. Usuń grupę woluminów logicznych vg100. Wyczyść konfiguracje LVM z dysków. Sprawdź wykonane czynności za pomocą polecenia lsblk.

Ćwiczenie 5: Logiczne woluminy VDO

Jako użytkownik z możliwością podniesienia uprawnień na serwerze oznaczonym jako server2. Utwórz konfigurację LVM na podstawie jedno z 10GB dysków (jeśli nie masz takiego dysku, dodaj go). W utworzonej grupie utwórz wolumin VDO o wielkości wirtualnej 50GB. Zweryfikuj wykonanie czynności za pomocą poleceń vgdisplay oraz vdostats. Wycofaj wszystkie dokonane zmiany.

Ćwiczenie 6: Tworzenie puli Stratis

Jako użytkownik z możliwością podniesienia uprawnień na serwerze oznaczonym jako server2. Sprawdź za pomocą lsblk dostępność jednego dysków o pojemności 1GB. Nastepnie utwórz z jego użyciem nową pulę Stratis o nazwie strpool. Potwierdź wykonanie czynności wyświetlając informacje o puli oraz o urządzeniach blokowych za pomocą podpoleceń polecenia Stratis oraz polecenia lsblk.

Ćwiczenie 7: Rozszerzenie i usunięcie puli Stratis

Jako użytkownik z możliwością podniesienia uprawnień na serwerze oznaczonym jako server2. Za pomocą drugiego dysku o pojemności 1GB rozszerz rozmiar puli Stratis strpool. Zweryfikuj wykonaną czynność. Na koniec usuń pulę Stratis, zweryfikuj to za pomocą polecenia lsblk.

Podsumowanie

W tym rozdziale poznaliśmy zaawansowane sposoby na zarządzanie dyskami naszych komputerów. Poznaliśmy wiele poleceń, których składni nie trzeb się uczyć na pamięć. Trzeba się jednak orientować jakie polecenie jest do czego. Poznaliśmy zaległą technologię VDO oraz trochę uprzedzającą fakty zebrane w następnym rodziale technologię Stratis. W następnym rozdziale poznamy systemy plików oraz sposoby na formatowanie tworzonych w tym oraz w poprzednim rozdziale urządzeń blokowych, aby w końcu można było ich użyć do przechowywania danych.

15. Systemy plików oraz przestrzeń wymiany

W ostatnich dwóch rozdziałach poruszaliśmy temat zarządzania przestrzenią dyskową. Oczywiście opisywane tam metody znajdą również zastosowanie w przypadku innych pamięci masowych niż dyski. Czynności wykonywane tam kończyły się wraz z utworzeniem docelowego urządzenia przeznaczonego do przechowywania danych. To jednak nie koniec, aby takie urządzenie mogło przechowywać dane muszą zostać na nim zainstalowane specjalne struktury, które będą zajmować się organizacją oraz sposobem dostepu do danych zebranych na urządzeniu. Tym właśnie jest system plików.

Komputery, ktorych używamy do pracy zawsze mają określną liczbę zasobów. Jednym z nich jest pamięć operacyjna, która przechowuję zarówno kod uruchomionych programów jak i dane, na których one pracują. W zależności od konfiguracji sprzętowej może się ona szybko wyczerpać, więc wymyślono coś takiego jak przestrzeń wymiany. Jest specjalny rodzaj alokacji przestrzeni na dysku, przechowywujący w niej nieużywane w danym momencie obszary pamięci operacyjnej. Jeśli te dane będą potrzebne znów wrócą one do pamięci, a inne zostaną przeniesione właśnie do przestrzeni wymiany. Przestrzeń wymiany inaczej nosi nazwę swapu (czyt. słapu).

15.1 Systemy plików na RHEL 9

Red Hat Enterprise Linux w wersji 9 natywnie obsługuje cztery systemy plików są nim między innymi:

Zarządzanie systemami plików obejmuje ich tworzenie, rozszerzanie, zmniejszanie, montowanie oraz odmontowywanie. Tworzenie systemu plików nazwywane jest jego instalacją lub formatowaniem. Obsługa danego systemu plików dostarcza kilku narzędzi, które będziemy poznawać w trakcie wykonywania przykładów, poza nimi system posiada jeszcze kilka poleceń dotyczących ogólnie systemów plików.

15.2. Montowanie oraz odłączanie systemów plików

Chcąc skorzystać z przestrzeni dyskowej zawartej na partycji, musi ona zostać sformatowana pod określny system plików. Załóżmy, że zostało już to zrobione. Musi ona również zostać podłączona do katalogu głównego, tym właśnie zajmuje się montowanie. Do montowania służy polecenie mount. Wydanie go bez podania żadnego argumentu spowoduje wyświetlenie wszystkich zamontowanych w systemie systemów plików.

/dev/mapper/rhel-root on / type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
selinuxfs on /sys/fs/selinux type selinuxfs (rw,nosuid,noexec,relatime)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=31,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=12986)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime,seclabel)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,seclabel,pagesize=2M)
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime,seclabel)
tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime,seclabel)
fusectl on /sys/fs/fuse/connections type fusectl (rw,nosuid,nodev,noexec,relatime)
configfs on /sys/kernel/config type configfs (rw,nosuid,nodev,noexec,relatime)
/dev/vda1 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/sr0 on /mnt type iso9660 (ro,relatime,nojoliet,check=s,map=n,blocksize=2048)

System do działania potrzebuje wielu systemów plików i to nie tylko tych dyskowych ale także tych wirtualnych dla tego wynik działania tego polecenia nie jest zbyt czytelny. Możemy go jednak bardzej uściślić podając w za pomocą opcji -t interesującyu nas system plików.

[user@rhel90 ~]$ mount -t xfs
/dev/mapper/rhel-root on / type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)

Aby zamontować system plików należy podać po poleceniu mount, nazwę urządzenia, punkt montowania oraz ewentualnie opcje. Punkten montowania może być dowolny katalog znajdujący się w systemie. Najlepiej jakby był pusty, ponieważ pliki z zamontowanego katalogu przesłonią jego rzeczywistą zawartość.

Innym argumentem są opcje montowania. Polecenie posiada kilka opcji ogólnych, natomiast montowanie określnego systemu plików pozwala także na użycie specyficznych dla niego opcji. Poniżej znajduje się kilka ogólnych opcji, opcje specyficzne znajdują się na stronie podręcznika poświęconej systemowi plików.

Poleceniem służącym do odłączania systemu plików jest umount. Najczęściej podaje się mu punkt montowania. Po odmontowaniu system plików staje się niedostępny dla użytkowników oraz programów.

15.3. Pozyskiwanie identyfikatora UUID systemu plików

Podczas montowania jednym z podawanych argumentów jest nazwa urządzenia. Sprawdza się to podczas szybkiego montowania w trakcie działania systemu. Nazwa urządzenia nie jest jedyną wartością jaką możemy podać poleceniu mount podczas montowania jako wartość wskazującą na urządzenie. Do montowania możemy użyć etykiety, którą ustawiamy podczas formatowania partycji lub specjalnego identyfikatora UUID nadawanego po zainstalowaniu systemu plików na partycji.

Identyfikator w przypadku systemów plików takich jak EXT oraz XFS ma długość 128 bitów natomiast w przypadku VFAT są to tylko 32 bity, 8 heksadecymalnych znaków. UUID możemy uzyskać za przy użyciu specyficznego narzędzia dostarczanego wraz system plików lub dwóch programów ogólnych blkid lub znanego już wcześniej lsblk wraz z opcją -f.

[user@rhel90 ~]$ blkid
/dev/mapper/rhel-root: UUID="c09cb7eb-2f8e-459e-bb64-21dde92cd7c0" BLOCK_SIZE="512" TYPE="xfs"
/dev/vda2: UUID="AHWsb0-0mbi-MxDK-0bpS-Q6ib-cz4l-rHQWAr" TYPE="LVM2_member" PARTUUID="45658df6-02"

[user@rhel90 ~]$ lsblk -f /dev/vda
NAME          FSTYPE    FSVER    LABEL  UUID                                   FSAVAIL FSUSE% MOUNTPOINTS
vda                                                                                           
├─vda1        xfs                bootfs fb4c6cf9-8041-42ce-9e75-82026a4a0164    753,1M    26% /boot
└─vda2        LVM2_memb LVM2 001        AHWsb0-0mbi-MxDK-0bpS-Q6ib-cz4l-rHQWAr                
  ├─rhel-root xfs                       c09cb7eb-2f8e-459e-bb64-21dde92cd7c0      5,9G    39% /
  └─rhel-swap swap      1               be58cfe2-c3a6-4731-9b0b-f58604074bd8                 [SWAP]

Odnośnie egzaminu:
Alternatywne nazwenictwo jest istotne gdy chcemy utworzyć wpisy automatycznego montowania podczas uruchamiania systemu. Klasyczne nazwy, którymi posługujemy się w systemie przy następnym uruchomieniu mogą już być zupełnie inne dla tych samych urządzeń, a UUID czy etykieta jest przypisana na stałe do systemu plików, zostanie zmieniona dopiero przez ponowne sformatowanie partycji.

15.4. Nadawanie etykiet systemom plików

Etykiety są jedną z metod alternatywnego nazewnictwa urządzeń przechowywujących systemy plików. Możemy je nadwać za pomocą narzędzi dostarczanych wraz z systemem plików. Dla systemów EXT jest narzędzie e2label, natomiast dla XFS jest polecenie xfs_admin wraz z opcją -L. Przy czym etykiety mają swoje ograniczenia w ilość znaków. Dla XFS ten limit wynosi 12 znaków, a w przypadku EXT4 jest to 16 znaków. Teraz odmontuje system plików zamontowany w /boot a następnie nadam mu etykietę i zamontuje ponownie. Na początku jednak sprawdzam nazwę urzędzenia partycji oraz zainstalowany na niej system plików.

[user@rhel90 ~]$ lsblk -f
NAME FSTYPE FSVER LABEL                 UUID                                   FSAVAIL FSUSE% MOUNTPOINTS
sr0  iso966 Jolie RHEL-9-0-0-BaseOS-x86_64
                                        2022-04-19-20-42-48-00                       0   100% /mnt
vda                                                                                           
├─vda1
│    xfs                                fb4c6cf9-8041-42ce-9e75-82026a4a0164    753,1M    26% /boot
└─vda2
     LVM2_m LVM2                        AHWsb0-0mbi-MxDK-0bpS-Q6ib-cz4l-rHQWAr                
  ├─rhel-root
  │  xfs                                c09cb7eb-2f8e-459e-bb64-21dde92cd7c0      5,9G    39% /
  └─rhel-swap
     swap   1                           be58cfe2-c3a6-4731-9b0b-f58604074bd8                 [SWAP]
[user@rhel90 ~]$ sudo umount /boot
[sudo] hasło użytkownika user: 
[user@rhel90 ~]$ sudo xfs_admin -L "bootfs" /dev/vda1
zapisywanie wszystkich superbloków
nowa etykieta = "bootfs"
[user@rhel90 ~]$ sudo mount /boot
[user@rhel90 ~]$ lsblk -f
NAME FSTYPE FSVER LABEL                 UUID                                   FSAVAIL FSUSE% MOUNTPOINTS
sr0  iso966 Jolie RHEL-9-0-0-BaseOS-x86_64
                                        2022-04-19-20-42-48-00                       0   100% /mnt
vda                                                                                           
├─vda1
│    xfs          bootfs                fb4c6cf9-8041-42ce-9e75-82026a4a0164    753,1M    26% /boot
└─vda2
     LVM2_m LVM2                        AHWsb0-0mbi-MxDK-0bpS-Q6ib-cz4l-rHQWAr                
  ├─rhel-root
  │  xfs                                c09cb7eb-2f8e-459e-bb64-21dde92cd7c0      5,9G    39% /
  └─rhel-swap
     swap   1                           be58cfe2-c3a6-4731-9b0b-f58604074bd8                 [SWAP]

Jak możemy zauważyć różnice pomiędzy pierwszym wywołaniem lsblk a drugim. Przy drugim wywowałaniu przy partycji zamontowanej w katalogu /boot w kolumnie LABEL widnieje teraz napis bootfs. Tak utworzoną etykietę możemy wykorzystać podczas ustawiania automatycznego montowania.

15.5. Automatyczne monotowanie systemów plików - /etc/fstab

Katalog główny możemy być rozbity na kilka różnych systemów plików. Plik zawarte w poszczególnych katalogach są potrzebne do rozruchu systemu, dlatego też w katalogu /etc znajduje się plik tekstowy fstab, w którym to przechowywane są wpisy systemów plików montowanych podczas uruchamiania systemu. Wpisy poza automatycznym montowaniem, skracają też potrzebę podawania wszystkich argumentów poleceniu mount. Jeśli taki system plików nie jest montowany automatycznie to, żeby go zamontować wystarczy podać albo punkt montowania albo nazwę urządzenia. Raczej będzie to punkt montowania. Poniżej znajduję się kilka wpisów, które posłużą nam do analizy.

[user@rhel90 ~]$ sudo grep -v -e '^#' -e '^$' /etc/fstab 
/dev/mapper/rhel-root   /                       xfs     defaults        0 0
UUID=fb4c6cf9-8041-42ce-9e75-82026a4a0164 /boot                   xfs     defaults        0 0
/dev/mapper/rhel-swap   none                    swap    defaults        0 0
/dev/sr0	/mnt	iso9660	ro	0	0

Aby nieco ułatwić czytelność pozbyłem się komentarzy oraz pustych wierszy. Każdy z wpisów składa się z 6 kolumn, kolumny te kolejno przedstawiają:

Kolumny 5 oraz 6 nie mają zastosowania dla innych systemów plików niż EXT, dlatego też przy wpisach innych systemów plików wpisuje się 0 (zero).

Za pomocą polecenia mount wraz z opcją -a możemy zamontować wszystkie systemy plików, których wpisy znajdują się w tym pliku. Często będziemy korzystać z tego polecenia zatwierdzając zamiany w tym pliku.

Odnośnie egzaminu:
Zgubiony lub niepoprawny wpis w pliku /etc/fstab może unieruchomić system. Aby to naprawić należy poprawić wpisy w trybie ratunkowy. Ze względu na to należy zwrócić szczególną uwagę na poprawność umieszczanych w tym pliku wpisów.

15.6. Monitorowanie zużycia miejsca w systemie plików

Jednym z zadań administracyjnych na serwerach jest monitorowanie zużycia miejsca w systemie plików. Jednym z dostępnych narzędzi jest polecenie df, które domyślnie wyświetla urządzenie jego rozmiar, ilość zużytego miejsca, ilość dostępnego miejsca, procentowe zużycie miejsca na dysku oraz punkt montowania. Poniżej znajduje się przykład.

[user@rhel90 ~]$ df -h
System plików         rozm. użyte dost. %uż. zamont. na
devtmpfs               606M     0  606M   0% /dev
tmpfs                  636M     0  636M   0% /dev/shm
tmpfs                  255M  7,4M  247M   3% /run
/dev/mapper/rhel-root  9,8G  3,9G  6,0G  40% /
/dev/sr0               8,0G  8,0G     0 100% /mnt
tmpfs                  1,0M     0  1,0M   0% /run/stratisd/keyfiles
tmpfs                  128M   52K  128M   1% /run/user/42
tmpfs                  128M   36K  128M   1% /run/user/1000
/dev/vda1             1014M  261M  754M  26% /boot

Użyłem w przykładzie polecenia wraz z opcją -h, która powoduje skalowanie jednostek. Domyślnie polecenia przedstawia wartości w postaci KB (kilobajtów). Wraz z tą opcją używa się jeszcze kilku innych takich jak:

15.7. Określenie użycia systemu plików

Za pomocą polecenia df mogliśmy ustalić ogólne wartości na temat zużycia systemu plików. Za pomocą polecenia du możemy ustalić i ile miejsca zajmują określone pliki oraz foldery. To polecenie również domyślnie zwraca informacje w KB. Najczęściej wykorzystywanymi opcjami są -s wyświetające podsumowanie oraz -h skalujące jednostki to poziomu czytelnego przez człowieka.

[user@rhel90 ~]$ sudo du -sh /usr
[sudo] hasło użytkownika user: 
3,6G	/usr

15.8. Zarządzanie systemami plików

Zarządzanie systemami plików wygląda podobnie w wszystkich poznanych tutaj przypadkach (metodach zarządzania pamięcią masową). W tym podrozdziale skupimy się na najbardziej zaawansowanych przypadkach.

15.8.1. Tworzenie systemów plików

Tworzenie systemów plików, czy też formatowanie partycji wygląda identycznie czy jest to wolumin logiczny czy partycja na dysku. Na poniższym przykładzie zaprezentowałem tworzenie partycji na trzech partycjach podstawowych na każdej z nich zainstalowałem inny system plików:

[user@rhel90 ~]$ sudo mkfs -t vfat /dev/vdb1
mkfs.fat 4.2 (2021-01-31)
[user@rhel90 ~]$ sudo mkfs -t ext4 /dev/vdb2
mke2fs 1.46.5 (30-Dec-2021)
Tworzenie systemu plików o 78848 blokach 1k oraz 19680 i-węzłach
UUID systemu plików: d97751fb-dce0-4406-b03b-925dfa64a183
Kopie zapasowe superbloku zapisane w blokach: 
	8193, 24577, 40961, 57345, 73729

Przydzielanie tablicy grup: zakończono                      
Zapis tablicy i-węzłów: zakończono                      
Tworzenie kroniki (4096 bloków): wykonano
Zapis superbloków i podsumowania systemu plików: wykonano

[user@rhel90 ~]$ sudo mkfs -t xfs /dev/vdb3
meta-data=/dev/vdb3              isize=512    agcount=4, agsize=4883 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1    bigtime=1 inobtcount=1
data     =                       bsize=4096   blocks=19531, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =log wewnętrzny        bsize=4096   blocks=1368, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =brak                   extsz=4096   blocks=0, rtextents=0

W przykładzie użyłem polecenia mkfs i za pomocą opcji -t podałem typ żądanego systemu plików. Równie dobrze możemy podać nazwę polecenia a następnie po kropce typ systemu plików (np. mkfs.ext4). Następnie podaje się urządzenie blokowe, które ma zostać sformatowane.

Tworzenie systemu plików dla woluminu VDO najlepiej jest przeprowadzić z użyciem opcji -E nodiscard dla EXT4 lub opcją -K dla XFS obie wykonują tę samą czynność i pozwalają zainstalować je szybciej na tego typu pamięciach masowych.

15.8.2. Montowanie oraz odłączanie systemu plików

Za pomocą polecenia mount możemy podłączyć systemy plików z poprzedniego podrozdziału do naszego systemu. Polecenie mount nie wymaga podania rodzaju systemu plików, potrafi ono samo to określić.

[user@rhel90 ~]$ sudo mount /dev/vdb3 /media/
[sudo] hasło użytkownika user: 
[user@rhel90 ~]$ sudo mount -t xfs
/dev/mapper/rhel-root on / type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vdb3 on /media type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)

Ostatni wiersz dotyczy montowania ze wcześniejszego polecenia.

Aby odmontować ten system plików należy wydać poniższe polecenie:

[user@rhel90 ~]$ sudo umount /media
[user@rhel90 ~]$ sudo mount -t xfs
/dev/mapper/rhel-root on / type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)

Wiersz dotyczący katalogu /media nie wystąpił po odmontowaniu, tak wiec system plików został odłączony od systemu.

15.8.3. Montowanie automatyczne z użyciem pliku /etc/fstab

Tak jak już wcześniej wspomiałem, za pomocą pliku /etc/fstab możemy zdefiniować wpisy z systemami plików, które będą automatycznie montowane podczas uruchamiania systemu lub będzie możliwość ich szybszego montowania, za pomocą polecenia mount. Definiując nowy wpis musimy wypełnić, każdą z kolumn. Należy również dodać, że tworząc wpisy /etc/fstab, należy pisać bardzo uważnie, ponieważ jeden błąd może unieruchomić cały system.

Dodanie wpisów do /etc/fstab można dokonać na wiele sposobów. Ja na przykład przełączyłem się na superużytkownika i za pomocą echo dodawałem kolejne wpisy, co zobrazowałem na poniższym przykładzie.

[user@rhel90 ~]$ mkdir /{vfat,ext4,xfs}fs1

[user@rhel90 ~]$ lsblk -f /dev/vdb
NAME   FSTYPE FSVER LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
vdb                                                                           
├─vdb1 vfat   FAT16       BA6B-6E4E                                           
├─vdb2 ext4   1.0         d97751fb-dce0-4406-b03b-925dfa64a183                
└─vdb3 xfs                37f34709-79da-4adb-aff4-8de1cfafe9c6                

[root@rhel90 user]# echo "UUID=BA6B-6E4E /vfatfs1 vfat defaults 0 0" >> /etc/fstab
[root@rhel90 user]# echo "UUID=d97751fb-dce0-4406-b03b-925dfa64a183 /ext4fs1 ext4 defaults 0 0" >> /etc/fstab
[root@rhel90 user]# echo "UUID=37f34709-79da-4adb-aff4-8de1cfafe9c6 /xfsfs1 xfs defaults 0 0" >> /etc/fstab 

[user@rhel90 ~]$ grep -v -e '^#' -e '^$' /etc/fstab
/dev/mapper/rhel-root   /                       xfs     defaults        0 0
UUID=fb4c6cf9-8041-42ce-9e75-82026a4a0164 /boot                   xfs     defaults        0 0
/dev/mapper/rhel-swap   none                    swap    defaults        0 0
/dev/sr0	/mnt	iso9660	ro	0	0
UUID=BA6B-6E4E /vfatfs1 vfat defaults 0 0
UUID=d97751fb-dce0-4406-b03b-925dfa64a183 /ext4fs1 ext4 defaults 0 0
UUID=37f34709-79da-4adb-aff4-8de1cfafe9c6 /xfsfs1 xfs defaults 0 0

[user@rhel90 ~]$ sudo mount -a

[user@rhel90 ~]$ df -h
System plików         rozm. użyte dost. %uż. zamont. na
...
/dev/vdb1               75M     0   75M   0% /vfatfs1
/dev/vdb2               67M   14K   62M   1% /ext4fs1
/dev/vdb3               71M  4,6M   67M   7% /xfsfs1

Na początku stworzyłem punkty montowania, trzy podkatalogi w katalogu głównym. Następnie odszukałem w systemie identyfikatory UUID, ponieważ są to zwykłe partycje musiałem ich użyć. Za pomocą przekierowania wyjścia polecenia echo zapisałem wpisy odpowiednie dla tych partycji, aby to potwierdzić użyłem polecenia mount wraz z opcją -a, żeby zamontować wszystkie wpisy w pliku. Polecenie oczywiście te zamontowane pomija, więc spokojnie każdą zmianę w tym pliku możemy potwierdzać za pomocą tego polecenia. Na koniec wyświetliłem sprawdzenie stanu zużycia systemów plików, aby lepiej zaprezentować fakt, iż systemy wcześniej zapisane do pliku /etc/fstab zostały poprawnie podłączone.

Wpis do pliku dotyczący LVM, różniłby się tylko tym, że zamiast UUID mogłaby być ścieżka do woluminu logicznego LVM. Jeśli chodzi o systemy plików tworzone przez Stratis można użyć UUID lub ścieżki urządzenia wyświetlanej na liście systemów plików, jednak Stratis wymaga uruchomienia usługi aby systemy plików były w ogóle widoczne, zatem przed opcją defaults należało by użyć opcji x-systemd.requires=stratisd.service.

15.8.4. Rozszerzanie systemu plików na woluminie logicznym LVM

W systemie posiadam już zamontowane dwa woluminy logiczne z LVM. Chcę rozszerzyć ich wielkość o 100MB. Wszystkie czynności takiej jak związane z LVM zostały już wykonane poza samym rozszerzeniem woluminów. Polecenie lvextend jak i lvresize posiadają opcję -r, która pozwala na uruchomienie programu odpowiedzialnego za zmianę rozmiaru systemu plików. W przypadku EXT4 uruchamiany jest program resize2fs a w przypadku XFS xfs_growfs. Opcję -r możemy pominąć lub o niej zapomnieć, wówczas należy uruchomić te programy z poziomu wiersza polecenia. Poniżej znajdują się przykłady jak można rozszerzyć zamontowane już woluminy logiczne.

[user@rhel90 ~]$ sudo lvextend -L +100M -r /dev/vg-lvm/ext4vol
  Devices file /dev/vdb is excluded by filter: device is partitioned.
  Size of logical volume vg-lvm/ext4vol changed from 252,00 MiB (63 extents) to 352,00 MiB (88 extents).
  Logical volume vg-lvm/ext4vol successfully resized.
resize2fs 1.46.5 (30-Dec-2021)
System plików /dev/mapper/vg--lvm-ext4vol jest zamontowany pod /ext4fs2; wymagana zmiana rozmiaru w locie
old_desc_blocks = 2, new_desc_blocks = 3
System plików na /dev/mapper/vg--lvm-ext4vol ma teraz 360448 (1k) bloków.

[user@rhel90 ~]$ sudo lvextend -L +100M /dev/vg-lvm/xfsvol
  Size of logical volume vg-lvm/xfsvol changed from 252,00 MiB (63 extents) to 352,00 MiB (88 extents).
  Logical volume vg-lvm/xfsvol successfully resized.
[user@rhel90 ~]$ sudo xfs_growfs /dev/vg-lvm/xfsvol
meta-data=/dev/mapper/vg--lvm-xfsvol isize=512    agcount=4, agsize=16128 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=1    bigtime=1 inobtcount=1
data     =                       bsize=4096   blocks=64512, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =log wewnętrzny        bsize=4096   blocks=1368, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =brak                   extsz=4096   blocks=0, rtextents=0
bloki danych zmienione z 64512 na 90112

15.9. Zarządzanie przestrzenią wymiany

Na wstępie tego rozdziału omówiliśmy sobie czym jest przestrzeń wymiany. Teraz przejdziemy do jej zarządzania. Poza użyciem specyficznego programu do formatowania partycji jako przestrzeni wymiany ten podrozdział nie będzie różnić się niczym od pozostałych. Nie ma różnicy czy stworzymy naszą przestrzeń wymiany na partycji czy woluminie logicznym. W systemie może być również wiecej niż jedna przestrzeń wymiany, są one wówczas priorytetyzowane.

15.9.1. Tworzenie przestrzeni wymiany

Do stworzenia przestrzeni wymiany wykorzystamy polecenie mkswap. Polecenie to może nadać etykietę partycji, poprzez użycie opcji -L. Do użycia mamy maksymalnie 16 znaków. Poniżej znajduje się przykład, w którym utworzyłem przestrzeń wymiany, zdefiniowałem także dla niej etykietę.

[user@rhel90 ~]$ sudo mkswap -L swapvol /dev/vg-lvm/swapvol
Tworzenie obszaru wymiany w wersji 1, rozmiar = 52 MiB (bajtów: 54521856)
LABEL=swapvol, UUID=3c99f75a-4559-4f6b-970b-f78cc4333e9b

15.9.2. Włączanie i wyłączanie swapu

Utworzony wcześniej swap jeszcze nie działa. Wymagane jest jego włączenie za pomocą polecenia swapon. To polecenie potrafi również wyświetlić wszystkie dostępne w systemie przestrzeni wymiany. Ręczne włączenie wymaga podania nazwy urządzenia. Poniżej znajduje się przykład:

[user@rhel90 ~]$ sudo swapon /dev/vg-lvm/swapvol
[sudo] hasło użytkownika user: 
[user@rhel90 ~]$ sudo swapon
NAME      TYPE      SIZE USED PRIO
/dev/dm-1 partition 1,2G   7M   -2
/dev/dm-7 partition  52M   0B   -3

Po włączeniu przestrzeni wymiany uruchomiłem jeszcze raz polecenie swapon bez żadnych argumentów aby wyświetlić wszystkie swapy dostępne w systemie. W dystrybucjach Linuksa może być ich 32. Dezaktywacji swapu dokonujemy za pomocą polecenia swapoff podając jako argument nazwę urządzenia.

[user@rhel90 ~]$ sudo swapoff /dev/vg-lvm/swapvol
[sudo] hasło użytkownika user: 
[user@rhel90 ~]$ sudo swapon
NAME      TYPE      SIZE USED PRIO
/dev/dm-1 partition 1,2G 6,8M   -2

Po wyłączeniu swapu widzimy, że nie jest on już dostępny w systemie.

15.9.3. Automatyczne włączanie przetrzeni wymiany - /etc/fstab

Przestrzeń wymiany tak jak inne systemy plików może być włączana automatycznie podczas uruchamiania systemu operacyjnego. Poniżej znajduje się przykład, który obrazuje dodawanie przestrzeni wymiany do pliku /etc/fstab oraz jej automatyczne włączanie nie tylko poprzez ponowne uruchomienie systemu.

[root@rhel90 user]# echo "LABEL=swapvol none swap defaults 0 0" >> /etc/fstab

[user@rhel90 ~]$ sudo swapon -a
[user@rhel90 ~]$ swapon
NAME      TYPE      SIZE USED PRIO
/dev/dm-1 partition 1,2G 6,8M   -2
/dev/dm-7 partition  52M   0B   -3

Tym razem zamiast nazwy urządzenia użyłem etykiety. Polecenie swapon wraz z opcją -a, ma takie samo działanie dla przestrzeni wymiany jak mount -a dla zwykłych systemów plików.

15.9.4. Monitorowanie zużycia przestrzeni wymiany

Do kontrolowania z użycia przestrzeni wymiany możemy wykorzystać proste polecenie, które zwraca nam informacje o ilości wykorzystywanej w systemie pamięci operacyjnej. Do tego służy polecenie free. Pod czas korzystania z tego narzędzia najlepiej od razu przeskalować jednostki za pomocą polecenia -h.

[user@rhel90 ~]$ free -h
               total        used        free      shared  buff/cache   available
Mem:           1,2Gi       930Mi       104Mi       8,0Mi       236Mi       179Mi
Swap:          1,3Gi       6,0Mi       1,2Gi

Druga linia zawiera informacje na temat przestrzeni wymiany. Prócz tego polecenia możemy wykorzystać dowolne polecenie monitorujące zasoby komputera, np. top.

Ćwiczenie 1: Tworzenie systemów plików oraz dodawanie wpisów do /etc/fstab

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server2. Na jednym z 250MB dysków utwórz tablicę partycji w schemacie MBR. Utwórz 3 partycje po 70MB na każdej z nich utwórz inny system plików. Utworz adekwatne dla nich punkty montowania. Zamontuj je ręcznie. Określ ich UUID a następnie dodaj wpisy montowania do pliku /etc/fstab. Ręcznie odłącz zamontowane wcześniej partycje, następnie zamontuj je automatycznie i potwierdź to za pomocą polecenia df -h.

Ćwiczenie 2: Wolumin VDO z XFS

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server2. Utwórz wolumin logiczny VDO o rozmiarze wirtualnym 50GB, sformatuj go pod XFS, dodaj do /etc/fstab, następnie zamontuj automatycznie potwierdź to przy użyciu polecenia df -h.

Ćwiczenie 3: Zarządzanie systemami plików z LVM

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server2. Utwórz dwa woluminy logiczne jeden o wielkości 100MB a drugi 120MB, utworz na nich systemy plików EXT4 na pierwszym, a drugim XFS. Zamontuj je w systemie. Dodaj drugi dysk do grupy, następnie rozszerz woluminy logiczne o 100MB przyczym użyj opcji -r podczas rozszerzenia woluminiu z XFS, a przy rozszerzaniu woluminu z EXT4 pomiń tę opcję i ręczenie rozszerz system plików. Zweryfikuj wykonane czynności.

Ćwiczenie 4: Montowanie automatyczne woluminu Stratis

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server2. Użyj jednego z 1GB dysków do stworzenia systemu plików Stratis. Dodaj wpis z systemem do pliku /etc/fstab. Uruchom system ponownie. Sprawdź czy system plików został zamontowany za pomocą polecenia df -h. Co mozemy zaobserować z wyników działania tego polecenia? Jaką pojemność ma zamontowany system plików Stratis?

Ćwiczenie 5: Przestrzeń wymiany

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server2. Na pozostałych z trzeciego ćwiczenia ekstentach, utwórz kolejny wolumin o nazwie swap. Stwórz z niego przestrzeń wymiany. Dodaj wpis tej przestrzeni do pliku /etc/fstab. Aktywują ją automatycznie, następnie wyświetl wszystkie dostępne przestrzenie wymiany w systemie oraz wyświetl łaczną pojemność swapu w systemie.

Podsumowanie

Tym rozdziałem zakończyliśmy naukę zarządzania pamięciami masowymi w RHEL. Poznaliśmy systemy plików oraz mechanizmy z nimi związane. Nauczyliśmy się instalować systemy plików na partycjach oraz woluminach logicznych. Dowiedzieliśmy się w jaki sposób montowane są automatycznie dostępne w systemie operacyjnym systemy plików oraz w jaki sposób poprawnie utworzyć wpis do pliku /etc/fstab. Na koniec poznaliśmy w jaki sposób zarządzać przestrzenią wymiany w RHEL. W następnym rozdziale zajmiemy się już podstawowymi pojęciami związanymi z siecią.

16. Podstawy sieci w RHEL 9

Potrzeba współdzielenia danych oraz zasobów między komputerami spowodowała, że zaczęto je łączyć w sieci. Na sieć komputerową składają się minimum dwa komputery, połączone ze sobą albo bezpośrednio albo za pomocą urządzenie pośredniczącego np. przełącznika. Aby komunikacja między nimi mogła być możliwa wbudowane w nie urządzenia sieciowe (karty/adaptery sieciowe) muszą zostać odpowiednio skonfigurowane z użyciem podstawowej wiedzy na temat sieci IP. Poznaną tutaj wiedzę na temat sieci IP, wykorzystamy do tworzenia oraz konfigurowania nowych połączeń sieciowych w systemach klasy enterprise.

16.1. Podstawowe pojęcia o sieciach komputerowych

Aby poprawnie skonfigurować interfejs sieciowy komputera musimy poznać podstawowe pojęcia związane z sieciami.

16.1.1. Adres IPv4

Adres protokołu internetowego w wersji 4, bo tak możemy rozszyfować nazwę tego podrozdziału jest podstawowym identyfikatorem służący to komunikacji w sieci. Każdy z komputerów podłączonych do sieci posiada ten adres przydzielony automatycznie z serwera DHCP lub przypisany ręcznie. Adres IPv4 występuje w postaci 4 oddzielonych kropką oktetów, każdy z oktetów może przyjąć wartości od 0 do 255. Adres IPv4 możemy sprawdzić za pomocą polecenia ip z podpoleceniem addr

[user@server10 ~]$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:55:b7:60 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.6/24 brd 192.168.122.255 scope global noprefixroute enp1s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe55:b760/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

Polecenie to wyświetla informacje na temat dostępnych w systemie połączeń sieciowych (interfejsów). Jednym z nich jest połączenie o nazwie enp1s0 posiadające adres 192.168.122.6. Drugie połączenie sieciowe, to specjalny interfejs systemowy tzw. pętla zwrotna, pozwala ona na komunikację z usługami sieciowym w tym systemie. Adresem pętli zwrotnej jest zawsze 127.0.0.1.

Komputery podłączone do tej samej sieci posiadają identyczne, niektóre z częsci adresu IPv4. Część wspólna nazywana jest częścią sieciową, pozostała część (część hosta) jest ustatalna indywidualnie dla każdego hosta i tylko jemu przypisywana. Które części adresu są częścią hosta, a które są częscią sieci jest ustatalne w trakcie jej konfiguracji na podstawie klas adresowych. Klasy adresowe możemy podzielić na trzy klasy:

Poza tymi klasami w specyfikacji wyróżnia się takie klasy jak D oraz E, jednak mają one specjalne przeznaczenie.

Tak duże klasy adresów są dzielone na mniejsze części, aby łatwiej zarządzać takimi sieciami, zwiększyć ich wydajność oraz zapewnić oporność na na awarie czy odsepraować je od siebie. Po podziale każda z takich sieci posiada logicznie wytyczone granice. Te granice są wskazywane przez maskę podsieci, której zadaniem jest zaznaczenie części sieciowej adresu IPv4 oraz części przeznaczonej dla komputerów w sieci. Oktety maski podsieci mogą posiadać wartości takie jak 255, 0 oraz inne przy czym wartości większe od 0 nie mogą znajdować się przed oktetami zawierającymi 255 lub wartości inne niż 0. Z czego to wynika? Otóż z podstaw adresacji IPv4. Każdy adres składa się z 4 oktetów. Co to tak naprawdę oznacza? Każdy adres IPv4 możemy rozpisać za pomocą systemu binarnego (0 i 1), oktet o poprostu 8 bitów (bit - najmniejsza ilość informacji, przestawia pojedyńczy stan 0 lub 1), 8 zer lub jedynek. Na masce podsieci jedynki określają bity, które są częścią sieciową adresów, a zera tą część która pozostaje do adresowania hostów, dlatego też zera nie mogą występować między jedynkami. Poniżej znajduje się przykład. Mój serwer ma adres 192.168.122.7 oraz maskę podsieci: 255.255.255.0. Poniżej znajduje się zapis tych parametrów w postaci binarnej:

11000000.10101000.01111010.00000111 = 192.168.122.7
11111111.11111111.11111111.00000000 = 255.255.255.0
====================================================
11000000.10101000.01111010. | 00000111 
        192.168.122.              7
    Część sieciowa adresu     Część hosta 

Maskę możemy zapisać w innej postaci niż dziesiętna (255.255.255.0). Jest to zapis w którym do określenia maski używa się ilości jedynek. Zatem maska z poprzedniego przykładu 255.255.255.0 wynosiła by 24. Kombinację adresu IPv4 hosta wraz z maską w takim zapisie oddzieloną ukośnikiem (/) nazywa się notacją CIDR i jest to obecnie najpopularnieszy sposób nadawania adresów IPv4 interfejsom sieciowym.

Maska podsieci może mieć maksymalną wartość 32 bitów, pozwalając na istnienie jednego adresu w sieci (najczęciej jest wskazanie na konkretny adres) lub minimalną wartość jaką jest 0, alokując tym samym wszystkie możliwe adresy, ok. 4,3 miliarda.

16.1.2. Adresy prywatne

Upowszechnienie się łącz szerokopasmowych dało możliwość budowania w sieci lokalnych, w których systemy są konfigurowane przez centralny serwer (router lub bramę). Tego typu sieci posługują się wenętrzną adresacją zawaną także prywatnymi klasami adresów. Tak jak w klasach publicznych, ogólno dostępne są trzy klasy:

Te klasy są ustalane w standardzie dotyczącym klas prywatnych, jednak administratorzy wolą używać mniejszych klas. Dlatego też klasa A pozostaje bez zmian, ale w klasie B używa się maski 16 bitowej, a w klasie C maski 24 bitowej.

16.1.3. Protokoły

Protokoły definiują standardy komunikacyjne, które umożliwiają wymianę informacji przez sieć. Protokoły działają na niższych warstwach niż usługi. Do protokołów możemy zaliczyć np. protokół IP, którego adresacji poświęciliśmy cały poprzedni podrozdział. W RHEL oraz w innych dystrybucjach w katalogu /etc w pliku protocols znajduje się zestawienie protokółów. Poniżej znajduje się fragment tego pliku.

[user@server10 ~]$ cat /etc/protocols 
...
ip	0	IP		# internet protocol, pseudo protocol number
hopopt	0	HOPOPT		# hop-by-hop options for ipv6
icmp	1	ICMP		# internet control message protocol
igmp	2	IGMP		# internet group management protocol
ggp	3	GGP		# gateway-gateway protocol
ipv4	4	IPv4		# IPv4 encapsulation
st	5	ST		# ST datagram mode
tcp	6	TCP		# transmission control protocol
...

16.1.4. Protokoły TCP i UDP

Z pośród wcześniejszego zestawienia protokołów, za transmisję danych w sieciach opartych na protokole IP odpowiedzialne są dwa protokoły TCP oraz UDP.

Protokół TCP stosowany w przypadku usług, w których liczy się jakość danych. Ten protokół zestawia połączenie w swojej wartstwie między klient oraz serwerem. Wówczas następuje sekwencjonowana transmisja danych, strony potwierdzają otrzymanie pakietu danych przez to w przypadku zaginięcia pakietu jest on przesyłany ponownie. Większość usług w internecie opiera się o ten protokół, gdyż pozwala on na rzetelną wymianę informacji.

Innym również często wykorzysywanym protokołem jest UDP. Protokół ten nie zestawia połączenia i poprostu pusza swoje pakiety prawdopodbnie licząc na niezawodność protokołów warstwy niższej jak IP. Nie dokonują on retransmisji gdy pakiety zostaną zgubione. Jednak jest on znacznie szybszy niż TCP dlatego też jest on wykorzystywany do przesyłania danych w czasie rzeczywistym.

Protokoły TCP oraz UDP w wprowadzają pojecie portu czyli numeru połączenia, wykorzystywanego do komunikacji. Dane są przesyłane z portu prywatnego (portu klienta, port wysoki > 10000) do portu publicznego usługi. Do portów publicznych możemy zaliczyć: HTTP - 80/TCP, HTTPS - 443/TCP czy SSH - 22/TCP. Rodzaj transmisji został podany po ukośniku. Więcej portów publicznych znajduje się w pliku /etc/services, fragment zawartości tego pliku znajduje się poniżej:

[user@server10 ~]$ head -100 /etc/services
...
ftp             21/tcp
ftp             21/udp          fsp fspd
ssh             22/tcp                          # The Secure Shell (SSH) Protocol
ssh             22/udp                          # The Secure Shell (SSH) Protocol
telnet          23/tcp
telnet          23/udp
# 24 - private mail system
lmtp            24/tcp                          # LMTP Mail Delivery
lmtp            24/udp                          # LMTP Mail Delivery
smtp            25/tcp          mail
smtp            25/udp          mail
time            37/tcp          timserver
...

16.1.5. Protokół ICMP

Protokół ICMP jest protokołem diagnostycznym, a jego zadaniem jest sprawdzenie możliwości połaczenia z podanym komputerem. Narzędziem korzystającym z tego protokołu jest ping. Zadaniem tego narzędzia jest wysłanie do komputera pakietu i otrzymanie odpowiedzi. Na podstawie otrzymanych (lub nie) danych polecenie rejestruje każdą odpowiedź lub jej brak oraz pod koniec działania wyświetla statystkę.

[user@server10 ~]$ ping -c4 192.168.122.7
PING 192.168.122.7 (192.168.122.7) 56(84) bytes of data.
64 bytes from 192.168.122.7: icmp_seq=1 ttl=64 time=0.354 ms
64 bytes from 192.168.122.7: icmp_seq=2 ttl=64 time=0.344 ms
64 bytes from 192.168.122.7: icmp_seq=3 ttl=64 time=0.517 ms
64 bytes from 192.168.122.7: icmp_seq=4 ttl=64 time=0.573 ms

--- 192.168.122.7 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3086ms
rtt min/avg/max/mdev = 0.344/0.447/0.573/0.100 ms

Przy dużej i w pełni działającej sieci odpowiedzi powinny nie przekraczać 2, 3 ms (milisekund). Czasy tutaj są poniżej 1 ms, ponieważ to sieć wirtualna w wewnątrz tego samego komputera.

16.1.6. Adres fizyczny

Każdy z interfejsów posiada swój adres fizyczny. Jest on wykorzystywany do transmisji danych w najniżej warstwie logicznej. Każdy z pakietów, które opuściły komputer źródłówy są opatrzone właśnie tym adresami. Aby poznać adres naszej karty sieciowej należy wydać poniższe polecenie:

[user@server10 ~]$ ip addr | grep 'ether'
    link/ether 52:54:00:55:b7:60 brd ff:ff:ff:ff:ff:ff

Adres mojej karty to: 52:54:00:55:b7:60. Protokół IP ściśle współpracuje z adresem fizycznym. Istnieje nawet protokół ARP, który umożliwia zamianę adresów IP na adresy fizyczne.

16.1.7. Adres IPv6

Ze względu na dość szybką ekspansję internetu, okazało sie że pula adresów IPv4 jest na wyczerpaniu. Czego się dziwić, jeśli mamy 8 miliardów ludzi na świecie, a tylko 4,3 miliarda adresów. Nie pomogła również adresacja prywatna. Zatem była potrzeba nowego rozwiązania. I w ten sposób narodził się Protokół internetowy w wersji 6. Jest on dostępny w RHEL od wersji 8. Zamiast 32-bitowego adresu wprowadza on 128-bitowy adres, który pozwoli zaadresować praktycznie nieosiągalną liczbę hostów (bo aż 340 i 36 zer). Adres ten składa się z 8 oddzielonych dwu kropkiem grupy, w której znajdują po cztery liczby heksadecymalne (0 - F).

Protokół IPv6, korzysta z protokołu NDP (ang. Neighbor Discover Protocol), do przeszukiwania sieci i odnajwydwania innych urządzeń sieciowych skonfigurowanych za pomocą tego protokołu. Rozszerza on również funkcjonalność IPv4 o diagnostykę problemów związanych z połączniem, powtarzalność adresów oraz routing. Za pomocą poniższego polecenia wyświetliłem adres IPv6 interfejsu na swoim komputerze.

[user@server10 ~]$ ip addr | grep 'inet6'
    inet6 fe80::5054:ff:fe55:b760/64 scope link noprefixroute 

16.1.8. Nazwa hosta

Każdy system operacyjny ma swoją nazwę, aby ułatwić jego identyfikację w sieci. Nazwa hosta występuje w przypadku dystrybucji Linuksa w symbolu zachęty chociaż możemy ją poznać na kilka róznych sposób. Na przykład:

[user@server10 ~]$ hostname
server10.example.com
[user@server10 ~]$ hostnamectl --static
server10.example.com
[user@server10 ~]$ uname -n
server10.example.com
[user@server10 ~]$ nmcli general hostname 
server10.example.com
[user@server10 ~]$ cat /etc/hostname
server10.example.com

Natomiast najlepszym i najszybszym sposóbem aby zmienić taką nazwę jest zmiana jej w pliku użytym w ostatnim poleceniu przykładu: /etc/hostname. Po zmianie pliku musimy jeszcze zrestartować usługę systemd-hostnamed.service.

16.2. Urządzenia sieciowe i połączenia

Teraz kiedy mamy solidną porcę wiedzy teoretycznej możemy przejść do konfiguracji połączeń sieciowych w RHEL 9.

16.2.1. Nazewnictwo urządzeń sieciowych w systemie

Do pewnego czasu nazwy urządzeń sieciowych, tj. interfejsów sieciowych były łatwe do zapamiętania, przez co upraszczały zadania administracyjne z nimi związane. Wywodziły się one od takich słów jak Ethernet czy wireless lan i były numerowane od 0 kolejno w takiej samej kolejności w jakiej zostały odnalezione w momencie uruchomienia systemu. Jednak jak to bywa z takimi rozwiązaniami, w przypadku dużej ilość interfejsów po ponownym uruchomieniu systemu ich nazwy mogły być już zupełnie inne. Co prowadziło do problemów konfiguracji. Podobnie jest z dyskami.

Obecnie nazwy interfejsów są ustalane za pomocą zasad demona udevd, przez co wprowadza się do systemu spójne oraz przewidywalne nazwenictwo bez wcześniej wspomnianego problemu.

Teraz zamiast eth0 interfejs może widnieć po nazwą np. enp7s0 lub ens160.

16.2.2. Profile połączeń sieciowych

Do RHEL 9 każe połączenie sieciowe miało swój profil (plik konfiguracyjny) w katalogu /etc/sysconfig/network-scripts Obecnie zrezygnowano z tej metody na rzecz tworzenia plików key-file dla podsystemu sieciowego NetworkManager. Sens profili został w nowych plikach zachowany ponieważ koniec, końców chodzi o konfigurację sieciową interfejsu. Jako jedno z zalet plików key-file jest to iż są prostsze i łatwiejsze do utworzenia. Poniżej znajduje się pliki key-file głównego połączenia sieciowego na mojej maszynie.

[connection]
id=enp1s0
uuid=eb849d6a-d642-32e9-9219-9496570c1d45
type=ethernet
autoconnect-priority=-999
interface-name=enp1s0
timestamp=1658937868

[ethernet]

[ipv4]
address1=192.168.122.6/24,192.168.122.1
dns=192.168.122.1;
method=manual

[ipv6]
addr-gen-mode=eui64
method=auto

[proxy]

Posługiwanie się plikami key-file jest najlepszym rozwiązaniem jeśli definiujemy dodatkowy interfejs w systemie. Wówczas do dyspozycji mamy już gotowy plik i na jego podstawie możemy zbudować nasz plik. Poniżej zamieściłem plik, który utworzyłem na potrzeby drugiego połączenia na mojej maszynie. Ten plik zawiera minimalną ilość informacji potrzebną do prawidłowego połączenia sieciowego. To połączenie nie posiada definicji adresu bramy domyślniej (tam kierowane są pakiety jeśli ich adres, jest poza naszą siecią lokalną - adres routera lub bramy) oraz adresów serwerów DNS (serwery DNS zmieniają nazwy domenowe na adresy IP).

[connection]
id=enp7s0
uuid=13e3cf57-9a36-4cb3-a4e0-c1b2411bf984
type=ethernet
interface-name=enp7s0

[ethernet]

[ipv4]
address1=192.168.100.6/24
method=manual

[ipv6]
addr-gen-mode=eui64
method=auto

[proxy]

Usunąłem z sekcji [connection] takie dyrektywy jak autoconnect-priority=-999, oraz timestamp. Z tego względu iż moje połączenie jest w sieci wewnętrznej, usunąłem drugi adres po przecinku w dyrektywie address1 oraz dyrektywę dns, z sekcji [ipv4]. Ten plik od pliku bazowego różni się również wartością w dyrektywie uuid, wartość UUID możemy uzyskać za pomocą polecenia uuidgen, podając jako argument nazwę interfejsu. Tak stworzony plik należy przenieść do katalogu /etc/NetworkManager/system-connections a następnie załadować za pomocą polecenia nmcli connection load podając ścieżkę do pliku jako argument. Po załadowaniu go, możemy aktywować połączenie za pomocą polecenia nmcli connection up filename i w tym poleceniu również należy podać ścieżkę pliku profilu.

Po aktywowaniu tego pliku w systemie pojawi się nowe połączenie:

[user@server10 ~]$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:55:b7:60 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.6/24 brd 192.168.122.255 scope global noprefixroute enp1s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe55:b760/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: enp7s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:ef:9f:e0 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.6/24 brd 192.168.100.255 scope global noprefixroute enp7s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:feef:9fe0/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

Odnośnie egzaminu:
Jak będziemy mogli zauważyć większość konfiguracji sieciowej obecnie w RHEL kręci się wokół podsystemu NetworkManager. Wystarczy, że będziemy znać jeden ze sposób na konfigurację połączenia sieciowego w systemie.

16.2.3. Zarządzanie połączeniami sieciowym za pomocą podsystemu NetworkManager

Podsystem NetworkManager, to potężne narzędzie, które potrafi ułatwić zarządzanie połączeniami sieciowymi. Za pomocą polecenia nmtui, może zarządzać połączeniami za pomocą prostego interfejsu okien w trybie tekstowym (pakiety dialog, whiptail). Innym narzędziem jest polecenie nmcli, którego działanie opiera się na kategoriach objektów. W tym materiale skupimy się wyłącznie na połączeniach - connection oraz urządzeniach - device. Dostęp do czynności możliwych do wykonania otrzymamy gdy podamy po poleceniu nazwę kategorii obiektu i naciśniemy klawisz Tab. Na przykład:

[user@server10 ~]$ nmcli connection 
add      delete   edit     help     load     monitor  show     
clone    down     export   import   modify   reload   up       

Jeśli wydamy polecenie nmcli connection bez żadnej akcji wówczas polecenie zwróci nam listę dostępnych w systemie połączeń.

[user@server10 ~]$ nmcli connection
NAME    UUID                                  TYPE      DEVICE 
enp1s0  eb849d6a-d642-32e9-9219-9496570c1d45  ethernet  enp1s0 
enp7s0  13e3cf57-9a36-4cb3-a4e0-c1b2411bf984  ethernet  enp7s0 

Poniżej znajduje się lista czynności związana z połączeniami:

Lista czynności związana z urządzeniami.

Nazwy kategorii obiektów oraz niektórych czynności możemy zapisać za pomocą jedno literowych skrótów. Na przykład chcąc wyświetlić dostępne w systemie połączenia możemy wydać następujące polecenie:

[user@server10 ~]$ sudo nmcli c s
[sudo] hasło użytkownika user: 
NAME    UUID                                  TYPE      DEVICE 
enp1s0  eb849d6a-d642-32e9-9219-9496570c1d45  ethernet  enp1s0 
enp7s0  13e3cf57-9a36-4cb3-a4e0-c1b2411bf984  ethernet  enp7s0 

W powyższym przykładzie użyłem skrótu c od kategorii connection oraz skrótu s od czynności show. Więcej skrótów znajdziemy na stronie podręcznika.

16.2.4. Tworzenie nowych połączeń sieciowych za pomocą nmcli

Jak moglismy wynioskować na podstawie czynność z poprzedniego podrozdziału, możemy wykorzystać polecenie nmcli do tworzenia nowych połączeń. Dodawanie połączenia rozpoczniemy od sprawdzenia dostępnych w systemie interfejsów sieciowych.

[user@server10 ~]$ sudo nmcli d s
[sudo] hasło użytkownika user: 
DEVICE  TYPE      STATE          CONNECTION 
enp1s0  ethernet  połączono      enp1s0     
enp7s0  ethernet  połączono      enp7s0     
enp8s0  ethernet  rozłączono     --         
lo      loopback  niezarządzane  --         

Jeden z interfejsów znajduje się w stanie rozłączono. Ten interfejs posłuży nam do stworzenia nowego połączenia. Za pomocą czynności add tworzmy nowe połączenie sieciowe.

[user@server10 ~]$ sudo nmcli c add type Ethernet ifname enp8s0 con-name enp8s0 ip4 192.168.100.8/24 gw4 192.168.100.1
[sudo] hasło użytkownika user: 
Pomyślnie dodano połączenie „enp8s0” (ff0d4164-b179-4d03-804d-ff08f8f4da31).

Na powyższym przykładzie dodałem nowe połączenie (c add) typu Ethernet (type Ethernet), użyłem do tego interfejsu enp8s0 (ifname enp8s0). Nadałem nazwę połączeniu od nazwy interfejsu - enp8s0 (con-name enp8s0), na końcu ustawiłem adres IPv4 z 24 bitową maską (ip4 192.168.100.8/24) oraz adres bramy domyślnej (gw4 192.168.100.1).

Tak utworzone połączenie nie wymaga już żadnej czynności, od razu jest ono gotowe do pracy, o czym może świadczyć lista połączeń na poniższym przykładzie.

Połączenia zarządzane za pomocą NetworkManager możemy wyłączać oraz włączać.

[user@server10 ~]$ sudo nmcli c s
[sudo] hasło użytkownika user: 
NAME    UUID                                  TYPE      DEVICE 
enp1s0  eb849d6a-d642-32e9-9219-9496570c1d45  ethernet  enp1s0 
enp7s0  13e3cf57-9a36-4cb3-a4e0-c1b2411bf984  ethernet  enp7s0 
enp8s0  ff0d4164-b179-4d03-804d-ff08f8f4da31  ethernet  enp8s0 

Połączenia zarządzane za pomocą NetworkManager możemy wyłączać oraz włączać.

[user@server10 ~]$ sudo nmcli c down enp8s0
Pomyślnie dezaktywowano połączenie „enp8s0” (ścieżka aktywacji D-Bus: /org/freedesktop/NetworkManager/ActiveConnection/3)
[user@server10 ~]$ sudo nmcli c s
NAME    UUID                                  TYPE      DEVICE 
enp1s0  eb849d6a-d642-32e9-9219-9496570c1d45  ethernet  enp1s0 
enp7s0  13e3cf57-9a36-4cb3-a4e0-c1b2411bf984  ethernet  enp7s0 
enp8s0  ff0d4164-b179-4d03-804d-ff08f8f4da31  ethernet  --     

[user@server10 ~]$ sudo nmcli c up enp8s0
Pomyślnie aktywowano połączenie (ścieżka aktywacji D-Bus: /org/freedesktop/NetworkManager/ActiveConnection/4)
[user@server10 ~]$ sudo nmcli c s
NAME    UUID                                  TYPE      DEVICE 
enp1s0  eb849d6a-d642-32e9-9219-9496570c1d45  ethernet  enp1s0 
enp7s0  13e3cf57-9a36-4cb3-a4e0-c1b2411bf984  ethernet  enp7s0 
enp8s0  ff0d4164-b179-4d03-804d-ff08f8f4da31  ethernet  enp8s0 

16.2.5. Plik /etc/hosts

Za pomocą pliku /etc/hosts możemy zbudować wewnątrz systemu mapowanie nazw domenowych na adresy IP, dzięki czemu będziemy mogli posługiwać się prostymi nazwami zamiast adresami IP. Każdy z wpisów w pliku składa się z trzech kolumn: adresu IP, nazwy domenowej oraz aliasu (krótkiej dodatkowej nazwy, której możemy używać zamiast nazwy domenowej, jeśli ta okaże się dość długa). Najczęściej w aliasie zapisywana jest nazwa hosta bez sufiksu domenowego. Na poniższym przykładzie znajduje się zawartość pliku /etc/hosts z mojego serwera.

[user@server10 ~]$ cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.122.6 server10.example.com server10
192.168.122.7 server20.example.com server20
192.168.100.6 server10s7.example.com server10s7
192.168.100.7 server20s7.example.com server20s7

Pierwsze dwa wpisy dotyczą tego aby odwołanie się do localhost zwracało adres pętli zwrotnej. Wpisy poniżej to mapowania adresów na nazwy maszyn wykorzystywanych do ćwiczeń w tym materiale. Zwróćmy uwagę na to, iż w pierwszych wierszach zdefiniowano więcej niż jeden alias.

16.2.6. Testowanie łączności

A przetestować możliwość połączenia między klietem a serwera lub między serwerami możemy skorzystać z jednego z narzędzi potokołu ICMP, a mianowicie narzędzia ping.

[user@server10 ~]$ ping -c4 server20s7.example.com 
PING server20s7.example.com (192.168.100.7) 56(84) bytes of data.
64 bytes from server20s7.example.com (192.168.100.7): icmp_seq=1 ttl=64 time=0.497 ms
64 bytes from server20s7.example.com (192.168.100.7): icmp_seq=2 ttl=64 time=0.335 ms
64 bytes from server20s7.example.com (192.168.100.7): icmp_seq=3 ttl=64 time=0.483 ms
64 bytes from server20s7.example.com (192.168.100.7): icmp_seq=4 ttl=64 time=0.302 ms

--- server20s7.example.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3096ms
rtt min/avg/max/mdev = 0.302/0.404/0.497/0.086 ms

Za pomocą poniższego polecenia wysłałem cztery pakiety diagnostyczne (-c4) przy użyciu sieci wewnętrznej do drugiej maszyny w laboratorium.

Ćwiczenie 1: Zmiana nazwy hosta

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej server1 oraz server2 zmień nazwę hosta z server1 na server10 oraz z server2 na Przy użyciu wykorzystywanego przez ciebie sposóbu wirtualizacji, stwórz sieć wewnętrzną. Następnie dodaj do każdej z maszyn dodatkowy interfejs sieciowy. Jak użytkownik z możliwością podniesienia uprawnień za pomocą dowolnej metody dodaj i skonfiguruj połączenia sieciowe na obu maszynach. Następnie dodaj wpisy wskazujące na maszyny do pliku /etc/hosts na każdej znich. Wykonanie tych czynności potwierdź sprawdzeniem połączenia.

Podsumowanie

W tym rozdziale zapoznaliśmy się z fundamentalnymi pojęciami związanymi z siecią. Poznalimśmy adresy IPv4, klasy adresów, adresy IPv6 oraz kilka protokołów. Dowiedzieliśmy się jak zarządzać połączeniami sieciowym w RHEL 9. Poznaliśmy prosty system, który umożliwi nam pisanie nazw hostów zamiast adresów IP oraz sposób na testowanie łączności. W następnym rozdziale zajmiemy się sieciowym systemem plików.

17. Network File System

Niektóre z mechanizmów współdzielenia dostępnych na Red Hat Enterprise Linux pozwalają na korzystanie z udostępnionych plików, tak jakby były by one lokalnie dostępne. Jednym z takich mechanizmów jest NFS, jego obsługa na docelowym systemie sprowadza się do wydania pojedynczego polecenia mount, a z dodatkowym demonem AutoFS tak na prawdę wystarczy próba dostępu do katalogu w którym powinny znajdować się dane aby były one dostępne na naszym komputerze.

NFS czyli (ang. Network File System) sieciowy system plików jest usługą sieciową pozwalającą na współdzielenie zasobów plikowych. NFS działa w architekturze klient-serwer. Co oznacza, że jeden z komputerów udostępnia pliki, na których drugi komputer po podłączeniu jest w stanie pracować. NFS udostępnia foldery, a każdy taki folder nazwany jest udziałem. Natomiast sam proces udostępniania folderu (udziału) nazywany jest eksportem. Przyczym eksport może być dokonany w kierunku okreslonego/określonych klienta/klientów. Klient chcąc skorzystać z udziału NFS dokonuje jego montowania tak jakby podłączał dodatkowy system plików do swojego systemu, podając oczywiście odpowiednie parametry.

Za stosowaniem NFS płynie wiele korzyści o takich jak wieloplatformowość, wieloużytkowość, redukcja kosztów administracyjnych oraz związanych z zakupem kolejnych dysków. Po zastosowaniu mechanizmów AutoFS będziemy w stanie zapewnić użytkownikom zdalne katalogi domowe.

Dostępnych jest wiele wersji NFS. W RHEL od wersji 8 domyślną wersją NFS jest wersja 4.2, która daje możliwość wykorzystania wyłącznie protokołu TCP zapisu asynchronicznego oraz obsługuje pliki powyżej 2GB. Po za tym pozwala ona na połączenia przez firewall oraz internet. Wersja ta usprawnia kilka zabezpieczeń, wspiera szyfrowanie transmisji oraz możliwość użycia ACL.

17.1. Konfiguracja serwera oraz klienta NFS

Na początku zadań praktycznych skonfigurujemy sobie prosty udział, aby mniej więcej wiedzieć jak skonfigurować serwer NFS.

17.1.1. Konfiguracja serwera NFS

Konfigurację zaczynamy od serwera i tam na początku instalujemy niezbędne oprogramowanie.

[user@server20 ~]$ sudo dnf install nfs-utils
[sudo] hasło użytkownika user: 
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 1:05:53 temu w dniu pią, 29 lip 2022, 15:59:59.
Pakiet nfs-utils-1:2.5.4-10.el9.x86_64 jest już zainstalowany.
Rozwiązano zależności.
Nie ma nic do zrobienia.
Ukończono.

Następnie tworzymy katalog który będzie udziałem. Nadajemy mu odpowiednie uprawnienia, aby każdy klient mógł zapisywać w nim swoje pliki.

[user@server20 ~]$ sudo mkdir /nfstest
[sudo] hasło użytkownika user: 
[user@server20 ~]$ sudo chmod 777 /nfstest

Następnie w pliku /etc/exports, definiuje eksport udziału do klienta. Eksport pozwala również na zdefiniowanie dodatkowych opcji dotyczących udziału, w naszym przypadku będzie przypisanie klientowi możliwości zapisu oraz odczytu.

/nfstest server10(rw)

Po zdefiniowaniu eksportu możemy przejść do uruchomienia demona NFS. Następnie otworzymy port na zaporze sieciowej pozwalający na swobodną komunikację klienta z serwerem.

[user@server20 ~]$ sudo systemctl enable --now nfs-server.service
Created symlink /etc/systemd/system/multi-user.target.wants/nfs-server.service → /usr/lib/systemd/system/nfs-server.service.
[user@server20 ~]$ sudo firewall-cmd --permanent --add-service nfs
success
[user@server20 ~]$ sudo firewall-cmd --reload 
success

Teraz wystarczy tylko zmusić demona NFS, aby dokonał eksportu. A możemy to zrobić za pomocą poniższego polecenia.

[user@server20 ~]$ sudo exportfs -av
exporting server10.example.com:/nfstest

Opcja -a nakazuje eksport wszystkich udziałów zapisanych w pliku /etc/exports, natomiast opcją -v włącza bardziej szczegółowe wyświetlanie komunikatów diagnostycznych. Bez tej opcji polecenie nie zwróci nic. Konfigurację serwera możemy uznać za zakończoną. Teraz przejdziemy do konfiguracji klienta.

17.1.2. Konfiguracja klienta NFS

Konfigurację klienta tak jak serwera musimy rozpocząć od zainstalowania niezbędnego oprogramowania, ponieważ obsługa NFS nie jest natywnie wbudowana.

[user@server10 ~]$ sudo dnf install nfs-utils
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 0:24:05 temu w dniu pią, 29 lip 2022, 17:16:50.
Pakiet nfs-utils-1:2.5.4-10.el9.x86_64 jest już zainstalowany.
Rozwiązano zależności.
Nie ma nic do zrobienia.
Ukończono.

Podłączanie się do serwera NFS wygląda jak zwykłe montowanie systemu plików w systemie.

[user@server10 ~]$ sudo mount server20:/nfstest /media

Nie musimy podawać nawet typu systemu plików. Aby nie musieć podłączać się za każdym razem możemy dopisać poniższy wiersz do pliku /etc/fstab.

server20:/nfstest /local nfs _netdev 0 0

Na powyższym przykładzie użyłem opcji _netdev, ponieważ do realizacji tego montowania potrzebna jest działająca na systemie sieć.

17.2. Konfiguracja udziału NFS za pomocą AutoFS

Dla konfiguracji klienta jest o wiele lepsze rozwiązanie niż ręczne montowanie NFS lub wpis w /etc/fstab. W RHEL jest dostępny demon, który pozwala na zamontowanie udziału NFS w momecie gdy jest on potrzebny. Na przykład gdy chcemy wyświetlić zawartość katalogu, w którym powinny znajdować się udostępnione przez niego pliki. Tym zajmuje się właśnie AutoFS. Ten demon, nie tylko montuje na żądanie ale także odłącza go gdy nie ma prób uzyskania dostępu do niego przez okres 5 minut. Główny plik konfiguracyjny znajduje się w katalogu /etc pod nazwą autofs.conf. Poniżej znajduje się jego zawartość:

[ autofs ]
timeout = 300
browse_mode = no
mount_nfs_default_protocol = 4
[ amd ]
dismount_interval = 300

W tym pliku są ustawiane globalne opcje takie jak czas oczekiwania na udział (timeout=300) czy czas po którym udział zostanie odłączony (dismount_interval=300). Jednak ten plik nie służy do definicji automatycznego montowania.

Wszelkiego rozdzaju montowanie bo nie tylko udziałów definiowane jest w pliku /etc/auto.master. Poniżej znajduje się jego zawartość bez komentarzy oraz pustych linii.

[user@server10 ~]$ grep -v -e '^#' -e '^$' /etc/auto.master
/misc	/etc/auto.misc
/net	-hosts
+dir:/etc/auto.master.d
+auto.master

Montowania za pomocą AutoFS nazwane jest mapowaniem. Mapowania możemy dokonać na trzy sposoby: główny, bezpośredni oraz pośredni. Przyczym główny sposób mapowania służy jedynie do określenia czy mapowanie będzie bezpośrednie lub pośrednie. Samych mapowań dokonuje się w plikach użytkowników składowanych w katalogu /etc/auto.master.d. W jednym z takich plików może znajdować się wiele punktów montowania. W pliku /etc/auto.master najczęściej definiuje się zbiorczy punkt montowania oraz rodzaj mapowania. Natomiast konkretne udziały z wyszczególnionymi punktami montowania zapisuje się właśnie w plikach użytkowników. Jedno z mapowań jest już zapisane w pierwszej linii powyższego przykładu.

17.2.1. Mapowanie bezpośrednie

W przypadku mapowania bezpośredniego montowana jest dowolna ilość niepowiązanych ze sobą punktów montowania. Przyczym o to kilka punktów o których trzeba wiedzieć używając tego rodzaju mapowania.

Każdy udział montowany bezpośrenio będzie mieć osobny wpis w wyniku działania polecenia mount wydanego bez argumentów. Na poniższych przykładach pokazałem jak możemym wykorzystać AutoFS do bezpośredniego montowania udziału NFS z poprzedniego podrozdziału.

W pliku /etc/auto.master zapisuje poniższą linię:

/-  /etc/auto.master.d/auto.direct

Oznacza ona mapowanie bezpośrednie. /- onznacza, że punkty montowania w pliku mapowania użytkownika /etc/auto.master.d/auto.direct będą przestawiowe w postaci ścieżek bezwzględnych. W pliku mapowania zapisujemy poniższą linię.

/nfstest  server20:/nfstest

Po włączeniu usługi za pomocą polecenia sudo systemctl enable --now autofs.service wystarczy, że wyświetlimy zawartość głównego katalogu i zauważym nowy folder /nfstest z zamontowanym udziałem.

[user@server10 ~]$ ls -l /
razem 26
dr-xr-xr-x.   2 root root    6 2021-08-10  afs
drwxr-xr-x.   2 root root    6 07-29 13:11 autodir
lrwxrwxrwx.   1 root root    7 2021-08-10  bin -> usr/bin
dr-xr-xr-x.   5 root root 4096 07-16 21:23 boot
drwxr-xr-x.  21 root root 3280 07-29 12:36 dev
drwxr-xr-x. 108 root root 8192 07-29 21:46 etc
drwxr-xr-x.   3 root root   18 07-16 21:25 home
lrwxrwxrwx.   1 root root    7 2021-08-10  lib -> usr/lib
lrwxrwxrwx.   1 root root    9 2021-08-10  lib64 -> usr/lib64
drwxr-xr-x.   2 root root    6 07-29 12:39 local
drwxr-xr-x.   2 root root    6 2021-08-10  media
drwxr-xr-x.   2 root root    0 07-29 21:46 misc
dr-xr-xr-x.   1 root root 2048 04-19 22:42 mnt
drwxr-xr-x.   2 root root    0 07-29 21:46 net
drwxr-xr-x.   2 root root    6 07-29 15:14 nfshome
drwxrwxrwx.   2 root root    6 07-29 17:17 nfstest
drwxr-xr-x.   2 root root    6 2021-08-10  opt
dr-xr-xr-x. 199 root root    0 07-29 12:36 proc
dr-xr-x---.   2 root root  167 07-29 21:46 root
drwxr-xr-x.  37 root root 1220 07-29 21:46 run
lrwxrwxrwx.   1 root root    8 2021-08-10  sbin -> usr/sbin
drwxrwxrwx.   2 root root    6 07-29 15:34 sharenfs
drwxr-xr-x.   2 root root    6 2021-08-10  srv
dr-xr-xr-x.  13 root root    0 07-29 12:36 sys
drwxrwxrwt.   9 root root 4096 07-29 21:46 tmp
drwxr-xr-x.  12 root root  144 07-16 21:11 usr
drwxr-xr-x.  20 root root 4096 07-16 21:29 var

Poniżej zamieszczam wynik działania polecenia mount bez argumentów:

[user@server10 ~]$ mount
...
/etc/auto.master.d/auto.direct on /nfstest type autofs (rw,relatime,fd=17,pgrp=1313,timeout=300,
minproto=5,maxproto=5,direct,pipe_ino=26905)
server20:/nfstest on /nfstest type nfs4 (rw,relatime,vers=4.2,rsize=262144,wsize=262144,
namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.122.6,local_lock=none,
addr=192.168.122.7)

17.2.2. Mapowanie pośrednie

Mapowanie pośrednie jest wykorzystywane gdy chcemy zamontować wszystkie udziały w jednym wspólnym katalogu nadrzędnym. Oto kilka punktów jakie trzeba znać podczas mapowania pośredniego.

Poniżej przedstawiam w jaki sposób można zamontować udział NFS z poprzedniego podrozdziału za pomocą mapowania pośredniego AutoFS.

Na początęk wyświetlimy zawartość głównego pliku mapowania czyli /etc/auto.master

[user@server10 ~]$ grep -v -e '^#' -e '^$' /etc/auto.master
/misc	/etc/auto.misc
/net	-hosts
+dir:/etc/auto.master.d
+auto.master

W pierszej linii znajduje się definicja katalogu nadrzędnego przygotowana przez AutoFS. W tej linii znajduje się także ścieżka do pliku, w którym należy zapisać definicje udziałów do mapowania pośredniego. Zapisujemy więc poniższą linię do pliku /etc/auto.misc.

nfstest server20:/nfstest

Po zapisaniu zmian w pliku restartujemy usługę. Teraz możemy spróbować uzyskać dostęp do udziału. Udział będzie dostępny jak podkatalog katalogu /misc

[user@server10 ~]$ sudo systemctl restart autofs.service 
[user@server10 ~]$ ls -l /misc/nfstest
razem 0

Poniżej znajdują się wpisy z systemowego rejestru montowania:

/etc/auto.misc on /misc type autofs (rw,relatime,fd=5,pgrp=1042,timeout=300,minproto=5,maxproto=5,
indirect,pipe_ino=24514)
server20:/nfstest on /misc/nfstest type nfs4 (rw,relatime,vers=4.2,rsize=262144,wsize=262144,
namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.122.6,local_lock=none,
addr=192.168.122.7)

Zwróćmy uwagę na to, iż występuje tylko jeden wpisy dotyczący katalogu /misc typu autofs. Gdzie w przypadku montowania bezpośredniego, każdy punkt montowania posiadał własny wpis typu autofs.

Jeśli należałoby wybrać między mapowaniem bezpośrednim oraz pośrednim. To tak naprawdę, nie ma lepszych czy gorszych. Każde z rodzajów mapowań ma swoje cechy kluczowe, które sobie omówiliśmy w listach przed każdym z przykładów. Na podstawie tych cech oraz potrzeb jakie ma spełniać udział należy wybrać odpowiednią dla siebie metodę.

17.2.3. Automatyczne montowanie zdalnych katalogów domowych

Przy użyciu AutoFS oraz montowania pośredniego możemy zamontować katalog domowy użytkownika z innego serwera o ile są one udostępnione za pomocą NFS. Aby to zrealizować potrzebne są dwa czynniki:

  1. Katalog /home na serwerze powinien zostać wyeksportowany.
  2. Udwzorowanie użytkowników na obu systemach powinno być takie same, tj. najlepiej aby użytkownicy mieli te same UID-y aby uniknąć problemów z prawem własności plików złożonych katalogch domowych.

Aby przetestować AutoFS w praktyce wykonamy kilka czynności, aby odzworowany użytkownik z jednego serwera na drugim serwerze miał dostęp do swoich danych.

Na początku na serwerze, który udostępnia katalogi domowe tworzymy użytkownika. Ja utworzyłem użytkownika user40 z UID wynoszącym 4040 oraz nadałem mu hasło.

[user@server20 ~]$ sudo useradd -u 4040 user40
[sudo] hasło użytkownika user: 
[user@server20 ~]$ sudo passwd user40
Zmienianie hasła użytkownika user40.
Nowe hasło: 
BŁĘDNE HASŁO: Hasło jest krótsze niż 8 znaków
Proszę ponownie wpisać nowe hasło: 

Następnie dokonujemy eksportu katalogu /home oraz wymuszamy zmianę udziałów NFS.

[user@server20 ~]$ cat /etc/exports
/home	server10(rw)
[user@server20 ~]$ sudo exportfs -avr
exporting server10.example.com:/home

Tak naprawdę na tym konfiguracja serwera kończy się. Przejdziemy teraz do konfiguracji klienta. W pliku /etc/auto.master utworzymy nową deklaracje katalogu nadrzędnego dla punktów montowania pośredniego.

[user@server10 ~]$ grep -v -e '^#' -e '^$' /etc/auto.master
/misc	/etc/auto.misc
/nfshome	/etc/auto.master.d/auto.home
/net	-hosts
+dir:/etc/auto.master.d
+auto.master

W drugiej linii znajduje się katalog nadrzędny, w którym będą przechowywane katalogi domowe. Musimy go utworzyć.

[user@server10 ~]$ sudo mkdir /nfshome

Następnie w pliku w którym powinny znajować się definicję punktów montowania zapisujemy poniższą linię:

* -rw server20:/home/&

W tym zapisie gwiazdka odnosi się dowolnej nazwy punktu montowania, ponieważ użytkowników może być bardzo dużo i nie sensu ich tu wszystkich wypisywać. Ampersand (&) może być odwołaniem do wielu serwerów lub wielu udziałów NFS. W tym przypadku został użyty do wskazania wielu katalogów w udziale. Po zapisaniu zmian w pliku należy zrestartować usługę.

[user@server10 ~]$ sudo systemctl restart autofs

Teraz wystarczy odwzrować użytkownika i się na niego przełączyć.

[user@server10 ~]$ sudo useradd -u 4040 -b /nfshome -M user40
[user@server10 ~]$ su - user40
Hasło: 
[user40@server10 ~]$

Podczas odwzorowywania użytkownika nadałem mu UID 4040, taki sam jak na serwerze (-u 4040), wskazałem mu inny niż /home katalog bazowy dla katalogów domowych (-b /nfshome) w tym przypadku jest katalog nadrzędny dla punktów montowania pośredniego AutoFS. Użyłem również opcji -M, aby podczas tworzenia nowego użytkownika nie został utworzony katalog domowy. Po utworzeniu użytkownika przełączyłem się na niego. Poniżej znajdują się wpisy z systemowego rejestru montowania, świadczące o tym iż katalog domowy użytkownika user40 jest katalogiem zdalnym podłączonym z pomocą NFS.

[user40@server10 ~]$ pwd
/nfshome/user40
[user40@server10 ~]$ mount
...
/etc/auto.master.d/auto.home on /nfshome type autofs (rw,relatime,fd=11,pgrp=1756,timeout=300,
minproto=5,maxproto=5,indirect,pipe_ino=28415)
server20:/home/user40 on /nfshome/user40 type nfs4 (rw,relatime,vers=4.2,rsize=262144,wsize=262144
,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.122.6,local_lock=none,
addr=192.168.122.7)

W ten sposób mogę utrzymać jeden katalog domowy na zdalnym serwerze i korzystać z niego na wielu komputerach.

Odnośnie egzaminu:
Jednym z zdań egzaminacyjnych może być automatyczne montowanie zdalnych katalogów domowych z wykorzystaniem NFS oraz AutoFS.

Ćwiczenie 1: Tworzenie udziału NFS oraz mapowanie bezpośrednie

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server10 utwórz udział NFS z katalogu /sharenfs i udostępnij go. Na maszynie server20 zainstaluj usługę AutoFS, następnie skonfiguruj automatyczne montowanie wcześniej utworzonego udziału w sposób bezpośredni. Spróbuj uzyskać dostęp do udziału, aby go zamontować. Potwierdź to za pomocą polecenia df -h.

Ćwiczenie 2: Pośrednie mapowanie udziału NFS

Wykorzystując udział z poprzedniego ćwiczenia zmień konfigurację AutoFS aby udział montowany był w sposób pośredni w katalogu nadrzędnym /autoindir. Spróbuj uzyskać dostęp do udostępnionego w ten sposób udziału. Potwierdź to za pomocą polecenia df -h

Podsumowanie

W tym rodziale poznaliśmy pierwszą usługę sieciową, która służy do udostępniania plików a jest nią NFS. Nauczyliśmy się konfigurować udziały na serwerze oraz montować je na systemach klienckich. Poznaliśmy usługę AutoFS, która pozwala na za montowanie udziału NFS, kiedy będzie on potrzebny. Poznaliśmy też jej konfiguracje w kilku ciekawych przypadkach. Następny rozdział będzie poświęcony synchronizacji czasu systemowego oraz rozwiązywaniu nazw domenowych.

18. Synchronizacja czasu oraz rozwiązywanie nazw w RHEL 9

Wiele komponentów systemowych opiera swoje czynności o poprawność daty i czasu. Jednym z nich, który każdemu przychodzi do głowy jest cron - systemowy harmonogram zadań. Bez ustawień czasu harmonogram był by najzwyczajniej w świecie bezużyteczny. Nad poprawnością czasu w systemach Uniksowych czuwa protokół NTP wraz z jego najnowszą implementacją w postaci programu chrony.

Aby osoba korzystająca z komputera nie musiała zapamiętać liczb adresu IP chcąc zapmiętać adres strony, wprowadzono prostsze do zapamiętania nazwy domenowe. Mimo tego adres IP dalej jest potrzebny do realizacji połączenia, zatem wraz z nazwami domenowymi pojawiła się usługa DNS, której zadaniem jest zamiana nazw domenowych na adresy IP i vice versa o ile jest to możliwe.

18.1. Network Time Protocol - synchronizacja czasu

Synchronizacja czasu za pomocą NTP polega na jak najdokładnieszym ustawieniu czasu rzeczywistego na zegarze systemowym za pomocą sieci. Czas na serwerach NTP nie bierze się z nikąd najpopularniejszym sposób na uzyskanie dokładnego czasu są zegary atomowe rozlokowane w wielu instytucjach naukowych na całym świecie. Zegary te są podłączone do serwerów, których adresy są dostępne na oficjalnej stronie protokołu: ntp.org, gdzie znajdziemy zbiory serwerów nazywane pulami. Pule mogą mieć wiele różnych podziałów, na przykład na poszczególne kraje, a nawet konkretne dystrybucje Linuksa. W przypadku RHEL adres puli NTP to: rhel.pool.ntp.org. W tych pulach, może znajdować się wiele serwerów, które mają określone role. Wśród nich możemy wyróżnić takie jak:

Sieć NTP ma hierachiczną budowę i opiera się ona o poziomy nazywane Stratum. Poziomy zaczynają się od poziomu 0 na tym poziomie znajdują się głownie zegary atomowe lub inne wzorce czasu. Klient NTP nie może uzyskać informacji z tego poziomu. Nie jest on podłączony do sieci. Drugim poziom - poziom 1 - jest miejscem w hierarchii, który znajdują się głównie serwery podstawowe oraz peer-y. Na kolejnych poziomach znajdują się serwery podrzędne oraz peer-y tych poziomów. Czas uzyskany z najwyższych poziomów można uznać za najbardziej dokładny i rzetelny. Wszystkich poziomów w hierarchi jest 15.

Klient wybiera serwer na podstawie czasu oczekiwania na odpowiedź, im krótszym tym czas będzie bardziej dokładny.

18.1.1. Konfiguracja demona Chrony

Chrony jest najnowszą implementacją demona synchronizacji czasu. Działa on na porcie 123 protokołu UDP. Jego plik konfiguracjny znajduje się bezpośrednio w katalogu /etc pod nazwą chrony.conf. Poniżej znajduje się jego zawartość.

pool 2.rhel.pool.ntp.org iburst
sourcedir /run/chrony-dhcp
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
keyfile /etc/chrony.keys
ntsdumpdir /var/lib/chrony
leapsectz right/UTC
logdir /var/log/chrony

Z tych przedstawionych na przykładzie na bardziej szczegółowe omówienie zasługuje dyrektywa pool wskazująca na adres puli, dodatkowo atrybut iburst powoduje przyspieszenie uzyskania czasu z serwera NTP. Ma to swój cel głównie podczas uruchamiania systemu. W pliku wskazywanym przez dyrektywę driftfile przechowywany jest wskaźnik rozbierzności czasu zegara systemowego, informacje przechowywane w tym pliku pomagają w utrzymaniu precyzji zegara. Jeśli mielibyśmy wskazać konkretny serwer czasu, to wówczas należało by użyć dyrektywy server. Dyrektywa logdir przechowuje plik dziennika /var/log/chrony.

Demon chrony dostarczany jest wraz z poleceniem chronyc za pomocą którego możemy sprawdzić wydajność demona oraz kontrolować jego działanie. Informacje o NTP z demona możemy uzyskać za pomocą podpoleceń takich jak sources oraz tracking.

Aby skonfigurować chrony nic nie trzeba robić. Wystarczy instalacja oraz włączenie jednostki. Domyślna konfiguracja świetnie sprawdza się w podstawowych zastosowaniach. Konfigurację zaczniemy zatem od sprawdzenia czy demon jest zainstalowany czy nie.

[user@server10 ~]$ sudo dnf install chrony
Updating Subscription Management repositories.
Unable to read consumer identity

This system is not registered with an entitlement server. You can use subscription-manager to register.

Ostatnio sprawdzono ważność metadanych: 0:31:37 temu w dniu pon, 1 sie 2022, 14:03:11.
Pakiet chrony-4.1-3.el9.x86_64 jest już zainstalowany.
Rozwiązano zależności.
Nie ma nic do zrobienia.
Ukończono.

W przypadku Red Hat Enterprise Linux 9 jest on domyślnie zainstalowany. To pozostaje tylko go włączyć.

[user@server10 ~]$ sudo systemctl enable --now chronyd.service 
Created symlink /etc/systemd/system/multi-user.target.wants/chronyd.service → /usr/lib/systemd/system/chronyd.service.
[user@server10 ~]$ sudo systemctl status chronyd.service --no-pager
● chronyd.service - NTP client/server
     Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2022-08-01 14:37:11 CEST; 23s ago
       Docs: man:chronyd(8)
             man:chrony.conf(5)
    Process: 1968 ExecStart=/usr/sbin/chronyd $OPTIONS (code=exited, status=0/SUCCESS)
   Main PID: 1970 (chronyd)
      Tasks: 1 (limit: 5891)
     Memory: 824.0K
        CPU: 18ms
     CGroup: /system.slice/chronyd.service
             └─1970 /usr/sbin/chronyd -F 2

sie 01 14:37:11 server10.example.com systemd[1]: Starting NTP client/server...
sie 01 14:37:11 server10.example.com chronyd[1970]: chronyd version 4.1 starting (+CMDMON +NTP +RE…EBUG)
sie 01 14:37:11 server10.example.com chronyd[1970]: Frequency -6.706 +/- 0.264 ppm read from /var/…drift
sie 01 14:37:11 server10.example.com chronyd[1970]: Using right/UTC timezone to obtain leap second data
sie 01 14:37:11 server10.example.com chronyd[1970]: Loaded seccomp filter (level 2)
sie 01 14:37:11 server10.example.com systemd[1]: Started NTP client/server.
sie 01 14:37:16 server10.example.com chronyd[1970]: Selected source 178.215.228.24 (2.rhel.pool.ntp.org)
sie 01 14:37:16 server10.example.com chronyd[1970]: System clock TAI offset set to 37 seconds
Hint: Some lines were ellipsized, use -l to show in full.

Za pomocą polecenia chronyc wraz z podpoleceniem sources możemy podejrzeć z jakim serwerem czasu połączył się nasz klient oraz na jakim poziomie hierarchi się one znajdują.

[user@server10 ~]$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^+ pingless.com                  2   6   377     2  -3517us[-2773us] +/-   27ms
^+ s.complex.net.pl              2   6   377     1  -2877us[-2877us] +/-   44ms
^* ntp.ifj.edu.pl                2   6   377     1    +10ms[  +11ms] +/-   33ms
^+ any.time.nl                   2   6   377    66  -2061us[-1153us] +/-   62ms

Gwiazdka w pierwszej kolumnie wiersza oznacza, że to właśnie z tego serwera został pobrany czas. Możemy się upewnić za pomocą drugiego podpolecenia tracking. Aby poznać więcej informacji na temat kolumn wyszukajmy sekcji sources na stronie podręcznika polecenia chronyc. Wynik działania podpolecenia tracking znajduje się poniżej:

[user@server10 ~]$ chronyc tracking
Reference ID    : C0560E43 (ntp.ifj.edu.pl)
Stratum         : 3
Ref time (UTC)  : Mon Aug 01 12:48:05 2022
System time     : 0.001653226 seconds slow of NTP time
Last offset     : -0.001851230 seconds
RMS offset      : 0.001920320 seconds
Frequency       : 7.629 ppm slow
Residual freq   : -0.463 ppm
Skew            : 4.887 ppm
Root delay      : 0.042983003 seconds
Root dispersion : 0.008079479 seconds
Update interval : 64.8 seconds
Leap status     : Normal

Zwróćmy uwagę na rozbierzność na temat poziomów serwera. W przypadku mojego serwera czas systemowy spóźnia się o wartość zapisaną w wierszu System time.

Odnośnie egzaminu:
Podczas egzaminu maszyny egzaminacyjne nie będą mieć dostępu do internetu, więc podczas konfiguracji klienta NTP będzie trzeba wskazać na serwer w sieci egzaminacyjnej. Ujmujemy w komentarz dyrektywę pool oraz server jeśli występuje, następnie dopisujemy kolejną dyrektywę server z adresem serwera NTP w sieci egzaminacyjnej.

18.1.2. Ustawienia daty i czasu

Podczas omawiania synchronizacji czasu nie może zabraknąć ręcznych ustawień. W przypadku RHEL można to zrealizować na dwa sposóby. Pierwszym z nich jest jest polecenie timedatectl oraz date. Tak prezentują się informacje zwracane przez oba narzędzia uruchomione bez żadnych argumentów:

[user@server10 ~]$ timedatectl
               Local time: pon 2022-08-01 15:34:51 CEST
           Universal time: pon 2022-08-01 13:34:51 UTC
                 RTC time: pon 2022-08-01 13:34:51
                Time zone: Europe/Warsaw (CEST, +0200)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

[user@server10 ~]$ date
pon, 1 sie 2022, 15:34:55 CEST

Aby móc ustawić ręcznie datę oraz czas trzeba wyłączyć możliwość ustawienia daty oraz czasu z protokołu NTP. Możemy to zrobić za pomocą poniższego polecenia:

[user@server10 ~]$ sudo timedatectl set-ntp false
[user@server10 ~]$ timedatectl
               Local time: pon 2022-08-01 15:35:58 CEST
           Universal time: pon 2022-08-01 13:35:58 UTC
                 RTC time: pon 2022-08-01 13:35:58
                Time zone: Europe/Warsaw (CEST, +0200)
System clock synchronized: yes
              NTP service: inactive
          RTC in local TZ: no

Ustawienie daty oraz czasu następuje w przypadku timedatectl, dokonujemy za pomocą podpolecenia set-time następnie podajemy datę oraz godzinę.

[user@server10 ~]$ sudo timedatectl set-time "1970-01-01 12:01"
[user@server10 ~]$ timedatectl 
               Local time: czw 1970-01-01 12:01:12 CET
           Universal time: czw 1970-01-01 11:01:12 UTC
                 RTC time: czw 1970-01-01 11:01:13
                Time zone: Europe/Warsaw (CET, +0100)
System clock synchronized: no
              NTP service: inactive
          RTC in local TZ: no

Natomiast w przypadku polecenia date używa się opcji --set.

[user@server10 ~]$ sudo date --set "1970-01-05 23:59"
pon, 5 sty 1970, 23:59:00 CET
[user@server10 ~]$ date
pon, 5 sty 1970, 23:59:02 CET

Aby wszystko wróciło do normy, wystarczy że włączymy obsługę NTP.

[user@server10 ~]$ sudo timedatectl set-ntp true
[sudo] hasło użytkownika user: 
[user@server10 ~]$ chronyc tracking
Reference ID    : D4539E53 (dedibox.demongeot.biz)
Stratum         : 3
Ref time (UTC)  : Mon Aug 01 13:56:32 2022
System time     : 0.000497948 seconds slow of NTP time
Last offset     : -0.000775014 seconds
RMS offset      : 0.000775014 seconds
Frequency       : 6.770 ppm slow
Residual freq   : -57.944 ppm
Skew            : 0.724 ppm
Root delay      : 0.065063119 seconds
Root dispersion : 0.003521844 seconds
Update interval : 1.5 seconds
Leap status     : Normal
[user@server10 ~]$ timedatectl 
               Local time: pon 2022-08-01 15:57:19 CEST
           Universal time: pon 2022-08-01 13:57:19 UTC
                 RTC time: pon 2022-08-01 13:57:19
                Time zone: Europe/Warsaw (CEST, +0200)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

18.2. Rozwiązywanie nazw domenowych

Za rozwiązywanie nazw domenowych odpowiedzialna jest usługa DNS. W tym podrozdziale przyjrzymy się tylko konfiguracji klienta oraz poznamy narzędzia dzięki którym będziemy mogli odpytać serwer w poszukiwaniu adresu IP lub nazwy hosta. Na początek jednak trochę teorii.

Przestrzeń nazw domenowych posiada hierahiczną strukturę odwróconego drzewa. Podobnie do katalogu głównego. Podbnie tutaj występuję wezeł główny oznaczonony kropką (.). Kropka funkcjonuje również jako separator w nazwach. Po głównym weźle, ktory jest domyślny i nie trzeba do podawać występuje TLD czyli domena najwyższego rzędu. Domenami najwyszego rzędu są między innymi .com, .net, .org itd. Domeny geograficzne na przykład .pl również są domenami najwyższego rzędu. Po nich wystepują poddomeny niższego rzędu. Mogą być to domeny takie jak .com itd. o ile w hierarchi poprzedza je na przykład domena geograficzna. W poddomenach niższego rzędu występują już nazwy własne, które się wydzierżawia od dostawcy usług domenowych. Taką nazwą własną mozę być redhat lub github. Po nazwie własnej, występuje nazwa hosta. Ma to miejsce w 80% przypadków, reszta to sieci korporacyjne gdzie między nazwą hosta, a nazwą własną domeny możemy występować jeszcze duża ilość poddomen. Weźmy dla przykładu nazwe domenową tej strony. Jeśli zapiszemy ją zgodnie z hierarchią to będzie wyglądać jak na przykładzie ponizej.

.-↓
  io.↓
     github.↓
            morketsmerke
.io.github.morketsmerke

W taki sposób są rozwiązywane nazwy domenowe na adresy IP przyczym najczęsiej w początkowej fazie, kroka oznaczająca główny węzeł zostaje pominięta, następnie szuka się serwera nazwy obsługującego github.io. Następnie otrzymuje się adres, który z hostów w sieci github to morketsmerke (oczywiście w dużym skrócie). Ludzie za to wpisują adresy do aplikacji w sposób odwrotny do hierarchi: morketsmerke.github.io. I w momencie gdy takie zapytanie trafi do serwera system DNS zacznie wysłać zapytania do innych serwerów odpowiedzialnych za utrzymanie poszczególnych części nazwy.

Serwery DNS zajmują się utrzymaniem bazy mapowań adresów domenowych na adresy IP, odpowiadaniem na zapytania klientów oraz synchronizacją tych baz z innymi serwerami. Wpis bazie danych DNS nazywany jest rekordem. Serwery DNS dzieli się na serwery główne oraz podrzędne. Serwery podrzędne wykorzystuje się głównie do równoważenia obciążenia oraz nadmiarowości, ponieważ bazy są przesyłane na serwery podrzędne za każdą aktulizacją wprowadzą w konfiguracji domeny.

18.2.1. Plik /etc/resolv.conf

Za pomocą pliku /etc/resolv.conf, narzędzia odpowiedzialne za rozwiązywanie nazw wiedzą w jaki sposób mają tworzyć zapytania oraz gdzie przesyłać je dalej (na przykład, gdy korzystamy z aliasu zapisanego w pliku /etc/hosts). Plik można edytować jak każdy inny plik tekstowy. Zazwyczaj zawiera on do trzech dyrektyw. Zawartość pliku z jednej z moich maszyny prezentuje się na poniższym przykładzie.

[user@server10 ~]$ cat /etc/resolv.conf 
# Generated by NetworkManager
search example.com
nameserver 192.168.122.1

Dyrektywa search zawiera sufiks z nazwą domeny, jest on dodawany do każdej zapisanej nazwy hosta pod czas tworzenia zapytania DNS. W tej dyrektywie może być ich maksymalnie 6, przyczym warto pamiętać o tym aby na początku zawsze znajdowała się domena lokalna. Kolejną dyrektywą jest nameserver zawierająca adresy IP odpytywanych po kolei serwerów DNS. W tej dyrektywie mogą znaleźć się maksymalnie 3 adresy, będą one odpytywane w takiej samej kolejności jak zostały zapisane. Ostatnia dyrektywą jest dyrektywa domain, jednak nie jest ona stosowana jeśli w pliku jest zapisana dyrektywa search.

18.2.2. Plik /etc/nsswitch.conf

Inny plikiem wpływającym na działanie rozwiązywania nazw systemach Uniksowych jest /etc/nsswitch.conf. Jego zadaniem jest wybranie odpowiedniego źródła dla żądanych informacji i w tym przypadku nie chodzi tylko o nazwy domenowe. Każdy wpis składa się ze wskazania bazy danych. W przypadku nazw domenowych jest hosts. Następnie wskazywane są źródła. Poniżej znajduje się przykładowy wpis:

hosts:      files dns myhostname

Gdzie files jest w tym przypadku plikiem /etc/hosts; dns jest odpytaniem serwerów zapisanych w /etc/resolv.conf.

Jeśli program podczas rozwiązywania nazwy nie znajdzie jej w pliku, przechodzi do kolejego źródła. Plik /etc/nsswitch.conf za pomocą słów kluczowych pozwala na określenie akcji jeśli uzyskano dane lub nie z zapisanych źródeł. Poniżej znajduje się lista słów kluczowych.

Dzięki tym słowom kluczowym, jeśli np. nazwa hosta nie zostanie odnaleziona w pliku /etc/resolv.conf można przerwać dalsze poszukiwania.

hosts:      files [notfound=return] dns myhostname

18.2.3. Narzędzia do rozwiązywania nazw domenowych

Czasem może zdarzyć się potrzeba przetestowania rozwiązywania nazw w systemie i do tego mogą posłużyć nam dostępne w systemie narzędzia. Jest ich kilka.

Pierwszym z nich jest dig. Bardzo elastyczne i użyteczne narzędzie. Możemy wykorzystać je do wielu czynności. Plusem tego narzędzia jest domyślna ilość zwracanych informacji. Poniżej znajduje się rozwiązanie nazwy redhat.com za pomocą tego narzędzia.

[user@server10 ~]$ dig redhat.com

; <<>> DiG 9.16.23-RH <<>> redhat.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20452
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;redhat.com.			IN	A

;; ANSWER SECTION:
redhat.com.		312	IN	A	52.200.142.250
redhat.com.		312	IN	A	34.235.198.240

;; Query time: 56 msec
;; SERVER: 192.168.122.1#53(192.168.122.1)
;; WHEN: Mon Aug 01 17:50:49 CEST 2022
;; MSG SIZE  rcvd: 71

Na samym dole mamy podsumowanie, w którym znajdują się informacje o tym jak długo trwało zapytanie, czy adres odpytywanego serwera. Polecenie to również możemy odpytać podając adres IP.

[user@server10 ~]$ dig -x 52.200.142.250

; <<>> DiG 9.16.23-RH <<>> -x 52.200.142.250
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14382
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;250.142.200.52.in-addr.arpa.	IN	PTR

;; ANSWER SECTION:
250.142.200.52.in-addr.arpa. 300 IN	PTR	ec2-52-200-142-250.compute-1.amazonaws.com.

;; Query time: 88 msec
;; SERVER: 192.168.122.1#53(192.168.122.1)
;; WHEN: Mon Aug 01 17:55:58 CEST 2022
;; MSG SIZE  rcvd: 112

Użycie opcji -x pozwala na podanie adresu IP.

Kolejny narzędziem jest polecenie host. Jak argument przyjmuje nazwę domenową i zwraca adres IP serwera oraz pozostałe wpisy w bazie DNS powiązane z tą nazwą. Poniżej znajduje się przykład.

[user@server10 ~]$ host redhat.com
redhat.com has address 34.235.198.240
redhat.com has address 52.200.142.250
redhat.com mail is handled by 10 us-smtp-inbound-2.mimecast.com.
redhat.com mail is handled by 10 us-smtp-inbound-1.mimecast.com.

Za pomocą polecenia host możemy uzyskać podobne wyjście jak w przypadku polecenia dig. Służy do tego opcja -v.

[user@server10 ~]$ host -v redhat.com
Trying "redhat.com"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5646
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;redhat.com.			IN	A

;; ANSWER SECTION:
redhat.com.		247	IN	A	52.200.142.250
redhat.com.		247	IN	A	34.235.198.240

Received 60 bytes from 192.168.122.1#53 in 4 ms
Trying "redhat.com"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13010
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0

;; QUESTION SECTION:
;redhat.com.			IN	AAAA

;; AUTHORITY SECTION:
redhat.com.		1037	IN	SOA	a1-68.akam.net. noc.redhat.com. 2022080000 300 180 604800 14400

Received 82 bytes from 192.168.122.1#53 in 51 ms
Trying "redhat.com"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56767
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;redhat.com.			IN	MX

;; ANSWER SECTION:
redhat.com.		314	IN	MX	10 us-smtp-inbound-1.mimecast.com.
redhat.com.		314	IN	MX	10 us-smtp-inbound-2.mimecast.com.

Received 105 bytes from 192.168.122.1#53 in 50 ms

Kolejnym poleceniem jest nslookup. Polecenie to jest interaktywne. Pozwala ono na zmianę serwera w bardzo prosty sposób, ponieważ jego adres możemy podać jako argument polecenia lub już po jego uruchomieniu po poleceniu server.

[user@server10 ~]$ nslookup
> server 8.8.8.8 
Default server: 8.8.8.8
Address: 8.8.8.8#53
> redhat.com
Server:		8.8.8.8
Address:	8.8.8.8#53

Non-authoritative answer:
Name:	redhat.com
Address: 52.200.142.250
Name:	redhat.com
Address: 34.235.198.240
> exit

W wyniku polecenia pojawił się taki zapis jak Non-authoritative answer. Oznacza to, że rozwiązanie nazwy otrzymano od serwera, który nie zarządza tą domeną. Polecenie opuszczamy tylko za pomocą polecenia exit.

Ostatnim poleceniem jest getent. Polecenie to jest związane z plikiem /etc/nsswitch.conf, dlatego aby uzyskać za jego pomocą adres IP jednej z nazw należy podać jako pierwszy argument bazę danych z jakiej będziemy korzystać. Przypominam, że jest to hosts. Poniżej znajduje się przykład użycia tego polecenia.

[user@server10 ~]$ getent hosts redhat.com
34.235.198.240  redhat.com
52.200.142.250  redhat.com

Ćwiczenie 1: Zmiana daty i czasu

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jak server10, wyświetl obecną datę i godzine za pomocą obu poznanych narzędzi. Za pomocą polecenia timedatectl zmień datę i godzine na dowolną w przyszłość. Za pomocą polecenia date zwiększ godzinę o jedną naprzód. Ponownie wyświetl datę i godzinę za pomocą obu narzędzi. Przyróć datę i godzinę do rzeczywistych wartości.

Ćwiczenie 2: Konfiguracja Chrony

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jak server10. Jeśli jest taka potrzeba zainstaluj Chrony. W pliku konfiguracyjny wyłącz wszystkie dyrektywy (ujmij w komentarz) pool oraz server. Dodaj nową dyrektywę na server z adresem 127.127.1.0. Włącz/zrestartuj usługę. Potwierdź wykonanie zadania za pomocą wyświetlania źródeł Chrony - polecenie chronyc sources.

Podsumowanie

W tym rozdziale poznaliśmy sposóby na efektywną i automatyczną synchronizację czasu zegara systemowego. Za poznaliśmy się z najnowszą implementacją protokołu NTP jaką definitywnie jest Chrony poznaliśmy jego konfigurację oraz polecenia służące zarządzania nim. Poza tym poznaliśmy w jaki sposób uzyskiwane są adresy IP w systemie, czym jest DNS, pliki /etc/resolv.conf czy /etc/nsswitch.conf. Poznaliśmy również kilka narzędzi, dzięki którym możemy przeprowadzić diagnostykę rozwiązywania nazw na naszych systemach. W następnym rozdziale zajmiemy się obsługą bezpiecznego zdalnego logowania do powłoki - usługą SSH.

19. Usługa bezpiecznej powłoki

Usługa bezpiecznej powłoki, jest w obecnych czasach jedną z podstawowych rzeczy bez których nie wyobrażamy sobie pracy z jakimi kolwiek odległymi maszynami. SSH pozwala na bezpieczne zarządzania maszynami oraz przesyłanie informacji między nimi przez niezabezpieczone sieci takie jak internet. SSH pozwala na uwierzytelnienie użytkownika na zdalnym serwerze. Dba ona również o intergralność danych przesyłanych jej kanałami. Ta usługa dostarcza do systemów Uniksowych wielu narzędzi, które wyparły odpowiedniki korzystające ze zwykłych nieszyfrowanych kanałów.

19.1. Podstawy teoretyczne na temat SSH

W wielu Uniksach (w tym i dystrybucjach Linuksa) stosowana jest darmowa oraz wolna implementacja protokołu SSH - OpenSSH, jest ona odpowiednikiem własnościowej wersji. Obecnie najczęściej stoswany jest protokół SSH w wersji 2.

19.1.1. Kryptografia SSH

OpenSSH do zabezpiecznia transmisji danych wykorzystuje metody kryptograficzne zwane algorytmami. Są to serie rownań matematycznych mających za zadanie jak najbardziej zamaskować jawne dane aby nie mogły być one odczytane przez osoby nie znające klucza, czyli hasła bądź pliku, który za pomocą tych samych algorytmów pozwoli na odczytanie danych przeznaczonych dla ich odbiorców. Wśród metod szyfrowania możemy wymienić:

OpenSSH wykorzystuje głównie algorytm RSA, ponieważ może za jego pomocą szyfrować dane oraz uwierzytelniać użytkownika. Do uwierzytleniania możemy wykorzystać takie algorytmy jak DSA oraz ECDSA jednak służą one tylko temu celowi.

19.1.2. Metody uwierzytelniania OpenSSH

Aby użytkownik mógł pracować na zdalnym serwerze za pomocą SSH musi się uwierzytelnić. Uwierzytelnienia możemy dokonać na kilka różnych sposobów, a wsród nich możemy wyróżnić oparte na:

W tym materiale jednak skupimy się wyłącznie na domyślnej metodzie jaką jest hasło oraz klucz publiczny.

19.1.3. Oprogramowanie OpenSSH

W RHEL usługa SSH w implementacji OpenSSH jest podzielona na trzy pakiety: openssh - dostarczający niezbędne biblioteki oraz polecenie ssh-keygen; openssh-client - dostarczający takie polecenia jak: ssh, scp czy sftp. Ostatnim pakietem jest openssh-server dostarczający demona sshd.

Polecenie ssh zastępuje stare polecenia telnet czy rlogin. Z poleceniem telnet możemy się jeszcze kiedyś spotkać. Polecenia scp i sftp zastępują kolejno rcp oraz ftp. Mimo to ftp jest dalej stosowany, aby umożliwić publiczny dostęp na przykład do repozytoriów oprogramowania.

19.2. Serwer OpenSSH - sshd

Serwerem OpenSSH jest program sshd. Jego konfiguracja znajduje się w pliku /etc/ssh/sshd_config. Poniżej znajduje się jej fragment:

#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key
# Ciphers and keying
#RekeyLimit default none
# Logging
#SyslogFacility AUTH
#LogLevel INFO
# Authentication:
#LoginGraceTime 2m
#PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
#PubkeyAuthentication yes
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile	.ssh/authorized_keys
...

Zwróćmy uwagę na to, że prawie wszystkie dyrektywy są ujęte w komentarz po za jedną, która wskazuje gdzie należy złożyć wszystkie otrzymane klucz publiczne w celach uwierzytelniania. Demon OpenSSH jest domyślnie dostoswany do największej ilości przypadków jego użycia, więc zmian w plikach konfiguracyjnych najczęściej dokonuje się w specyficznych przypadkach.

Nazwa jednostki w RHEL nosi nazwę sshd.service. Każdą zmianę w tym pliku należy potwierdzić restartując demona. Opisy tych dyrektyw znajdują się na stronie podręcznika.

19.3. Konfiguracja klienta - ssh

Na każdej maszynie, na której znajduje się polecenie ssh znajduje się także plik konfiguracyjny /etc/ssh/ssh_config, który reguluje jego zachowanie. Posiada on też kilka dyrektyw sugerujących pewne domyślne ustawienia. Poniżej znajduje się jego fragment pobrany z jednej z moich maszyn.

# Host *
#   ForwardAgent no
#   ForwardX11 no
#   PasswordAuthentication yes
#   HostbasedAuthentication no
#   GSSAPIAuthentication no
#   GSSAPIDelegateCredentials no
#   GSSAPIKeyExchange no
#   GSSAPITrustDNS no
#   BatchMode no
#   CheckHostIP yes
#   AddressFamily any
#   ConnectTimeout 0
#   StrictHostKeyChecking ask
#   IdentityFile ~/.ssh/id_rsa
#   IdentityFile ~/.ssh/id_dsa
#   IdentityFile ~/.ssh/id_ecdsa
#   IdentityFile ~/.ssh/id_ed25519
#   Port 22
...

Konfiguracja klienta została ujęta w ten sam sposób jak w przypadku serwera. Konfiguracja klienta również jest przygotowana do użycia we większości przypadków, dlatego też zmiany w konfiguracją będą wymagane tylko w specyficznych przypadkach.

19.4. Obsługa połączeń zdalnych - programy OpenSSH

Na początku tego rozdziału wspomnieliśmy o ilości programów dostarczanych wraz z pakietem OpenSSH. Teraz sobie je przetestujemy. Ten podrozdział został również rozszerzony o jeden program, który korzysta z SSH do przesyłania danych.

19.4. Połączenie ze zdalnym serwerem z pomocą ssh

Połączenie ze zdalnym serwerem wymaga wydania pojedynczego polecenia, w którym podajemy adres serwera, wówczas polecenie spróbuje zalogować na użytkownika z tą samą nazwą z jakiej korzystamy w lokalnym systemie.

[user@server10 ~]$ ssh server20
The authenticity of host 'server20 (192.168.122.7)' can't be established.
ED25519 key fingerprint is SHA256:ZWgY+ro8oMwFh7vq56bS/Zk9CGpUe2sCrmxaukixr8M.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'server20' (ED25519) to the list of known hosts.
user@server20's password: 
Activate the web console with: systemctl enable --now cockpit.socket

Register this system with Red Hat Insights: insights-client --register
Create an account or view all your systems at https://red.ht/insights-dashboard
Last login: Wed Aug  3 10:59:57 2022 from 192.168.122.6
[user@server20 ~]$ 

Po wydaniu polecenia nastepuje połączenie. Klient nie może zweryfikować autentyczności serwera przedstawiającego się podanym odciskiem klucza. Tak dzieje się gdy łączymy się z serwerem po raz pierwszy. Polecenie zapyta nas czy chcemy kontynuować połączenie, wybierając Yes odcisk klucza wraz z adresem serwera zostanie złożony w pliku ~/.ssh/known_hosts. Następnie zostaniemy zapytani o hasło. Po jego podaniu jesteśmy podłączeni do serwera.

Chcąc podać innego użytkownika podczas połączenia SSH możemy to zrobić na dwa sposób. Na przykład:

[user@server10 ~]$ ssh -l user server20
#lub:
[user@server10 ~]$ ssh user@server20

19.4.2. Generowanie, dystrybucja oraz logowanie się za pomocą klucza publicznego

Inną metodą uwierzytelniania stosowaną obok haseł jest uwierzytelnianie za pomocą klucza publicznego. Ma ono klika zalet, otóż możemy logować się bez podawania hasła, wówczas musimy pamiętać aby zabezpieczyć w inny sposób plik klucza. Możemy również nadać jedno hasło - hasło klucza i używać jednego hasła do wielu serwerów. W tym materiale zajmiemy się wyłącznie kluczami bez haseł.

Na początek zajmiemy się wygenerowaniem klucza. Dokonujemy tego za pomocą polecenia ssh-keygen podając kolejno opcję -N, przyczym należy pamiętać, że aby nadać puste hasło należy podać puste podwójne apostrofy ("") jako wartość tej opcji. Do generowania kluczy użyjemy także opcji -q, która powoduje nie wyświetlanie komunikatów podczas generowania klucza.

[user@server10 ~]$ ssh-keygen -N "" -q
Enter file in which to save the key (/home/user/.ssh/id_rsa): 

Polecenie zapyta o ścieżkę oraz nazwę pliku klucza. Tę wartość pozostawiam domyślnie.

Następnym etapem będzie przesłanie oraz instalacja klucza na zdalnym serwerze. Wykonamy tę czynność przy użyciu polecenia ssh-copy-id, przyjmuje ono takie same argumenty jak polecenie ssh, zatem aby zainstalować klucz na serwerze należy wydać poniższe polecenie. Polecenie to w ramach potwierdzenia naszej tożsamości zażąda hasła do połączenia się ze zdalnym użytkownikiem na serwerze.

[user@server10 ~]$ ssh-copy-id user@server20
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
user@server20's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'user@server20'"
and check to make sure that only the key(s) you wanted were added.

Teraz kiedy klucz jest zainstalowany możemy bez przeszkód logować się na serwer.

[user@server10 ~]$ ssh user@server20
Activate the web console with: systemctl enable --now cockpit.socket

Register this system with Red Hat Insights: insights-client --register
Create an account or view all your systems at https://red.ht/insights-dashboard
Last login: Wed Aug  3 11:39:18 2022 from 192.168.122.6
[user@server20 ~]$ 

19.4.3. Wykonywanie pojedyńczych poleceń na zdalnym serwerze

Za pomocą SSH możemy również wykonywać zdalnie pojedyncze polecenia, jeśli nie potrzebujemy dla jednej informacji zestawiać interaktywnej powłoki. Polecenie do wykonania na zdalnym serwerze podajemy po adresie serwera.

[user@server10 ~]$ ssh user@server20 "whoami; hostname; pwd"
user
server20.example.com
/home/user

Najlepiej, jeśli ujmiemy polecenie w podwójne apostrofy. Wówczas mamy pewność, że zostanie ono w pełni przekazane do zdalnego systemu.

19.4.4. Bezpieczne kopiowanie plików

Za pomocą polecenia scp, możemy kopiować pliki z wykorzystaniem protokołu SSH w obu kierunkach. Tutaj dodatkową rolę odgrywa zainstalowanie klucza bez hasła. Jeśli to zrobimy wówczas będziemy mogli rozwiajać scieżki ze zdalnego systemu za pomocą klawisza Tab. Na poniższym przykładzie skopiuje plik /etc/chrony.conf do katalogu /tmp.

[user@server10 ~]$ scp server20:/etc/chrony.conf /tmp
chrony.conf                                   100% 1369     1.5MB/s   00:00    
[user@server10 ~]$ ls -l /tmp/chrony.conf 
-rw-r--r--. 1 user user 1369 08-03 11:58 /tmp/chrony.conf

Za pomocą opcji -r jesteśmy w stanie skopiować do całe katalogi.

[user@server10 ~]$ scp -r /home/user server20:/tmp/test/
.bash_logout                                  100%   18    40.4KB/s   00:00    
.bash_profile                                 100%  141   386.3KB/s   00:00    
.bashrc                                       100%  492     1.2MB/s   00:00    
.bash_history                                 100% 9742    17.0MB/s   00:00    
.lesshst                                      100%   93   182.5KB/s   00:00    
known_hosts                                   100%  822     1.6MB/s   00:00    
known_hosts.old                               100%   90   154.6KB/s   00:00    
id_rsa                                        100% 2610     6.2MB/s   00:00    
id_rsa.pub                                    100%  579     1.4MB/s   00:00    
gzip                                          100%   94KB  37.9MB/s   00:00    
.viminfo                                      100%   10KB  13.7MB/s   00:00    

[user@server10 ~]$ ssh user@server20 "ls -al /tmp/test/user"
razem 136
drwx------. 3 user user   139 08-03 12:00 .
drwxrwxr-x. 3 user user    18 08-03 12:00 ..
-rw-------. 1 user user  9742 08-03 12:00 .bash_history
-rw-r--r--. 1 user user    18 08-03 12:00 .bash_logout
-rw-r--r--. 1 user user   141 08-03 12:00 .bash_profile
-rw-r--r--. 1 user user   492 08-03 12:00 .bashrc
-rwxr-xr-x. 1 user user 95872 08-03 12:00 gzip
-rw-------. 1 user user    93 08-03 12:00 .lesshst
drwx------. 2 user user    80 08-03 12:00 .ssh
-rw-------. 1 user user 10036 08-03 12:00 .viminfo

19.4.5. Przesyłanie plików za pomocą polecenia sftp

Polecenie sftp działa na takiej samej zasadzie co polecenie ftp, jednak wykorzystuje do tego protokół SSH przy tym rozszerzając oraz usprawniając działanie przesyłania plików.

Polecenie to przyjmuje takie same argumenty jak ssh. Przy czym jest ono interaktywne, jak klasyczne ftp za pomocą znaku zapytania (?) możemy wyświetlić listę dostępnych poleceń.

[user@server10 ~]$ sftp user@server20
Connected to server20.
sftp> ?
Available commands:
bye                                Quit sftp
cd path                            Change remote directory to 'path'
chgrp [-h] grp path                Change group of file 'path' to 'grp'
chmod [-h] mode path               Change permissions of file 'path' to 'mode'
chown [-h] own path                Change owner of file 'path' to 'own'
df [-hi] [path]                    Display statistics for current directory or
                                   filesystem containing 'path'
exit                               Quit sftp
get [-afpR] remote [local]         Download file
help                               Display this help text
lcd path                           Change local directory to 'path'
lls [ls-options [path]]            Display local directory listing
lmkdir path                        Create local directory
ln [-s] oldpath newpath            Link remote file (-s for symlink)
lpwd                               Print local working directory
ls [-1afhlnrSt] [path]             Display remote directory listing
lumask umask                       Set local umask to 'umask'
mkdir path                         Create remote directory
progress                           Toggle display of progress meter
put [-afpR] local [remote]         Upload file
pwd                                Display remote working directory
quit                               Quit sftp
reget [-fpR] remote [local]        Resume download file
rename oldpath newpath             Rename remote file
reput [-fpR] local [remote]        Resume upload file
rm path                            Delete remote file
rmdir path                         Remove remote directory
symlink oldpath newpath            Symlink remote file
version                            Show SFTP version
!command                           Execute 'command' in local shell
!                                  Escape to local shell
?                                  Synonym for help

Zwróćmy uwagę to, iż wiele poleceń występuję podwójnie, i są one poprzedzonych literą l. Aby lepiej zarządzać transferem plików posiadmy polecenia, które odwołują się czynności wykonywanych na lokalnym hoście, dzięki czemu nie musimy się rozłączać aby na przykład stworzyć katalog specjalnie na dane pobrane z serwera. Pobierzemy teraz plik /etc/fstab do katalogu /tmp.

sftp> lcd /tmp
sftp>  get /etc/fstab 
Fetching /etc/fstab to fstab
fstab                                        100%  608   709.3KB/s   00:00    

Za pomocą opcji -r takich poleceń get lub put możemy pobierać oraz przesyłać całe sturktury katalogów:

sftp> cd /tmp/dir_user/
sftp> put -r /tmp/user/.??*
Uploading /tmp/user/.bash_history to /tmp/dir_user/.bash_history
.bash_history                                100% 9742    13.7MB/s   00:00    
Uploading /tmp/user/.bash_logout to /tmp/dir_user/.bash_logout
.bash_logout                                 100%   18    54.6KB/s   00:00    
Uploading /tmp/user/.bash_profile to /tmp/dir_user/.bash_profile
.bash_profile                                100%  141   443.4KB/s   00:00    
Uploading /tmp/user/.bashrc to /tmp/dir_user/.bashrc
.bashrc                                      100%  492     1.7MB/s   00:00    
Uploading /tmp/user/.lesshst to /tmp/dir_user/.lesshst
.lesshst                                     100%   93   325.6KB/s   00:00    
Uploading /tmp/user/.ssh/ to /tmp/dir_user/.ssh
Entering /tmp/user/.ssh/
known_hosts                                  100%  822     3.3MB/s   00:00    
known_hosts.old                              100%   90   351.5KB/s   00:00    
id_rsa                                       100% 2610     8.2MB/s   00:00    
id_rsa.pub                                   100%  579     1.9MB/s   00:00    
Uploading /tmp/user/.viminfo to /tmp/dir_user/.viminfo
.viminfo                                     100%   10KB  23.3MB/s   00:00    

Jak możemy zauważyć na powyższym przykładzie w sftp możemy używać nazw wieloznacznych.

19.4.6. Synchronizacja danych ze zdalnym serwerem

Jednym z programów wykorzystujących protokół SSH jest rsync, służy on do synchronizacji plików. Taka synchronizacja polega na skopiowaniu wszelkich danych znajdujących się na zdalnym serwerze do lokalnego katalogu lub odwrotnie i przy następnych synchronizacjach będą kopiowane tylko te pliki, które uległy zmianie. Na przykład skopiuje katalog domowy jednego z użytkowników na zdalnym serwerze.

[user@server10 ~]$ rsync -avrPz server20:/home/user/.??* /tmp/user_server20/
receiving incremental file list
.bash_history
          3,763 100%    3.59MB/s    0:00:00 (xfr#1, to-chk=7/8)
.bash_logout
             18 100%   17.58kB/s    0:00:00 (xfr#2, to-chk=6/8)
.bash_profile
            141 100%  137.70kB/s    0:00:00 (xfr#3, to-chk=5/8)
.bashrc
            492 100%  480.47kB/s    0:00:00 (xfr#4, to-chk=4/8)
.lesshst
             20 100%    9.77kB/s    0:00:00 (xfr#5, to-chk=3/8)
.viminfo
          2,263 100%    1.08MB/s    0:00:00 (xfr#6, to-chk=2/8)
.ssh/
.ssh/authorized_keys
            579 100%  188.48kB/s    0:00:00 (xfr#7, to-chk=0/8)

sent 165 bytes  received 3,064 bytes  2,152.67 bytes/sec
total size is 7,276  speedup is 2.25

Aby taka synchronizacja miała sens potrzebne jest kilka opcji takich jak -a, powodująca włączenie trybu archiwalnego, przez co pliki zostaną skopiowane wraz ze wszystkimi atrybutami; -v włączająca wyświetlanie komunikatów diagnostycznych; -r włączająca rekurencje, zostaną skopiowane katalogi wraz z podkatalogami; -P włączająca zwrócenie statystyk postępu dla każdego przesyłanego pliku; -z włączająca kompresje.

Jeśli jeszcze raz spróbujemy pobrać te plik. Nic tak nie zostanie przesłane, ponieważ pliki nie uległy zmianie.

[user@server10 ~]$ rsync -avrPz server20:/home/user/.??* /tmp/user_server20/
receiving incremental file list

sent 21 bytes  received 228 bytes  498.00 bytes/sec
total size is 7,276  speedup is 29.22

Ćwiczenie 1: Logowanie przez SSH z użyciem klucza

Jako użytkownik z możliwością podniesienia uprawnień na maszynach oznaczonych jako server10 oraz server20 utworz nowego użytkownika user20 oraz przypisz mu hasło. Następnie na server10, przełącz się na tego użytkownika oraz wygeneruj klucze SSH bez hasła. Zainstaluj klucz na server20 na koncie użytkowika user20 i spróbuj się na niego zalogować.

Ćwiczenie 2: Działanie dyrektywy PermitRootLogin

Jako użytkownika z możliwością podniesienia uprawnień na maszynie oznaczonej jako server20. Ustaw wartość dyrektywy PermitRootLogin serwera OpenSSH na no. Następnie z poziomu server10 spróbuj się zalogować przez SSH jako root. Następnie zmień wartość tej dyrektywy na yes i spróbuj ponownie.

Podsumowanie

W tym rodziale poruszyliśmy temat bezpieczengo zdalnego dostępu do odległych maszyn. Jest on realizowany za pomocą protokołu SSH, z którego teoretycznymi podstawami zapoznaliśmy się na początku tego rozdziału. Poruszyliśmy także temat plików konfiguracyjnych serwera jak i klienta SSH. Na koniec dowiedzieliśmy się jak korzystać z poleceń dostarczanych wraz z pakietem OpenSSH. W następnym rozdziale poznamy zaporę sieciową w systemie RHEL 9.

20. Zapora sieciowa w RHEL 9

Sieci komputerowe pozwalają na swobodną komunikację między komputerami, co w pewnych przypadkach jest niewskazane, ponieważ nie zawsze pożądane jest aby dowolny użytkownik internetu mógł połączyć się z naszym komputerem i wysyłać lub pobierać z niego dane. Swobodę ruchu kontrolują zapory sieciowe, które mogą być albo fizycznymi urządzeniam wpiętymi w wyższe segmenty sieci lub oprogramowaniem zainstalowanym na naszym komputerze czuwającym nad ruchem sieciowym skierowanym do niego.

Zapora sieciowa ma za zadanie monitorować oraz kontrolować ruch sieciowy. Jest to realizowane przez przeglądanie nagłówków pakietów przesyłanych przez sieć oraz analiza źródłowych adresów IP, używanych protokołów czy portów. W tym materiale skupimy się tylko i wyłącznie na oprogramowaniu firewall-a dostarczonym do Red Hat Enterprise Linux 9.

20.1. Teoria firewalld

Pakiet zapory sieciowej dostarczony do RHEL to firewalld. Zapory dostępne w dystrybucjach Linuks opierają swoją zasadę działania o dwa składniki: moduł jądra netfilter oraz framework służący do klasyfikacji oraz filtrowania pakietów - nftables. Dzięki czemu poza zwykłymi zasadami (za pomocą zasad organizuje się ruch wychodzący oraz przychodzący w systemie) możemy realizować bardziej zaawansowane funkcje takie jak translacja adresów (NAT) umożliwiająca połączenie sieci lokalnej z internetem czy przekierowanie portów pozwalające na kontrolowaną komunikację między użytkownikami internetu a serwerem wewnątrz sieci lokalnej.

Firewalld pozwala na kontrolowanie ruchu sieciowego bez wpływu na nawiązane już połączenia oraz bez potrzeby restartu żadnych usług. Możemy dodawać, usuwać i modyfikować zasady za pomocą albo specjalnego polecenia, albo za pomocą panelu sterowania RHEL, dostępnym przez przeglądarkę, który jest możliwy włączenia za poprzez aktywacje jednostki cockpit.service.

Zasada działania firewalld opiera się na strefach co pozwala na łatwiejsze oraz transparentne utrzymanie ruchu. Strefa określa poziom zaufania połączeń sieciowych oraz adresów źródłowych. W konfiguracji strefy znajdują się przypisane do nich połączenia sieciowe, przy czym jedno połączenie może być przypisane tylko do jednej strefy w tym samym czasie, ale jedna strefa może mieć wiele połączeń oraz zasady pozwalające na połączenia z wybraną usługą czy portem. Strefy definiują także domyślne zachowanie zapory. Kiedy pakiet trafia do zapory, sprawdza ona jego adres źródłowy czy nie występuje w którejś ze stref, jeśli nie ma przypasowania, ten sam pakiet konfrontowany jest podstawie innych kryteriów z regułami zapisanymi w strefie przypisanej do interfejsu sieciowego z którego ten pakiet przybył, jeśli dalej nie ma przypasowania, wówczas jest ono szukane w domyślnej strefie. Brak przypasowania na ostatnim etapie najczęściej oznacza, że pakiet zostanie zablokowany przez zaporę. W systemie możemy spotkać się z kilkoma predefiniowanym strefami, wypisanymi na poniższej liście:

Strefy na powyższej liście zostały uszeregowane od najbardzej zaufanej do najmniej. Domyślną strefą stosowaną w RHEL jest public. Ostatnie strefy są skierowane do środowisk o podwyższonym poziomie zabezpieczeń. Nie ma ograniczeń w ruchu wychodzącym w każdej ze stref.

Strefy z poziomu systemu operacyjnego, to zwykłe pliki tekstowe przechowywane w dwóch lokalizacjach. Pierwszą z nich jest katalog /usr/lib/firewalld/zones, w którym to są przechowywane systemowe pliki stref. Drugim katalogiem jest /etc/firewalld/zones gdzie przchowywane są pliki stref użytkowników. Jeśli zechcemy zmienić jeden z plików strefy np. za pomocą polecenia, to automatycznie zostanie otworzona jego kopia w katalogu /etc/firewalld/zones i to na niej zostaną zatwierdzone nasze zmiany. Każda ze stref może zostać przez nas zmieniona. Poniżej znajduje się lista dostępnych stref systemowych:

[user@server10 ~]$ ls -l /usr/lib/firewalld/zones
razem 40
-rw-r--r--. 1 root root 312 2021-11-23  block.xml
-rw-r--r--. 1 root root 306 2021-11-23  dmz.xml
-rw-r--r--. 1 root root 304 2021-11-23  drop.xml
-rw-r--r--. 1 root root 317 2021-11-23  external.xml
-rw-r--r--. 1 root root 410 2021-11-23  home.xml
-rw-r--r--. 1 root root 425 2021-11-23  internal.xml
-rw-r--r--. 1 root root 729 2022-01-14  nm-shared.xml
-rw-r--r--. 1 root root 356 2021-11-23  public.xml
-rw-r--r--. 1 root root 175 2021-11-23  trusted.xml
-rw-r--r--. 1 root root 352 2021-11-23  work.xml

Zawartość takiego pliku strefy prezentuje się w następujący sposób:

[user@server10 ~]$ cat /usr/lib/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other 
computers on networks to not harm your computer. Only selected incoming 
connections are accepted.</description>
  <service name="ssh"/>
  <service name="dhcpv6-client"/>
  <service name="cockpit"/>
  <forward/>
</zone>

Każde z plików zawiera krótką nazwę, opis oraz deklaracje wskazujące otwarcie ruchu dla wskazanych w dyrektywach services usług. Usługi też w firewalld mają swoje pliki, w których deklarowane są używane przez nie porty oraz protokoły. Lista plików usług znajduje się w katalogu /usr/lib/firewalld/services. Poniżej znajduje się jej fragment. W przypadku usług panuje taka sama zasada jak w przypadku stref.

[user@server10 ~]$ ls -l /usr/lib/firewalld/services/
razem 728
-rw-r--r--. 1 root root  399 2021-11-23  amanda-client.xml
-rw-r--r--. 1 root root  427 2021-11-23  amanda-k5-client.xml
-rw-r--r--. 1 root root  283 2021-11-23  amqps.xml
-rw-r--r--. 1 root root  273 2021-11-23  amqp.xml
-rw-r--r--. 1 root root  285 2021-11-23  apcupsd.xml
-rw-r--r--. 1 root root  301 2021-11-23  audit.xml
-rw-r--r--. 1 root root  320 2021-11-23  bacula-client.xml
-rw-r--r--. 1 root root  346 2021-11-23  bacula.xml
-rw-r--r--. 1 root root  429 2021-11-23  bb.xml
-rw-r--r--. 1 root root  339 2021-11-23  bgp.xml
-rw-r--r--. 1 root root  275 2021-11-23  bitcoin-rpc.xml
-rw-r--r--. 1 root root  307 2021-11-23  bitcoin-testnet-rpc.xml
-rw-r--r--. 1 root root  281 2021-11-23  bitcoin-testnet.xml
-rw-r--r--. 1 root root  244 2021-11-23  bitcoin.xml
-rw-r--r--. 1 root root  410 2021-11-23  bittorrent-lsd.xml
-rw-r--r--. 1 root root  294 2021-11-23  ceph-mon.xml
-rw-r--r--. 1 root root  329 2021-11-23  ceph.xml
-rw-r--r--. 1 root root  168 2021-11-23  cfengine.xml

Poniżej znajduje się zawartość przykładowego pliku usługi:

[user@server10 ~]$ cat /usr/lib/firewalld/services/nfs.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>NFS4</short>
  <description>The NFS4 protocol is used to share files via TCP 
networking. You will need to have the NFS tools installed and properly 
configure your NFS server for this option to be useful.</description>
  <port protocol="tcp" port="2049"/>
</service>

20.2. Zarządzanie ruchem sieciowym w firewalld

Do zarządzania ruchem sieciowym posłużymy się poleceniem firewall-cmd. Wymagana ono uprawnień superużytkownika aby móc coś zmienić w zaporze. To polecenie posiada dużą ilość różnych opcji, jednak nie ma sensu ich tu przytaczać. Poznamy je w trakcie wykonywania przykładów z tego podrozdziału.

20.2.1. Sprawdzenie status zapory sieciowej

Sprawdzenia statusu zapory sieciowej możemy dokonać na dwa sposóby: mozemy wydać polecenie firewall-cmd z odpowiednią opcją lub sprawdzić status jednostki systemd firewalld.service. Poniżej znajdują się wyniki działania obu tych poleceń:

[user@server10 ~]$ sudo systemctl status firewalld.service 
[sudo] hasło użytkownika user: 
● firewalld.service - firewalld - dynamic firewall daemon
     Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2022-08-04 08:47:25 CEST; 2h 55min ago
       Docs: man:firewalld(1)
   Main PID: 720 (firewalld)
      Tasks: 4 (limit: 5891)
     Memory: 46.3M
        CPU: 7.278s
     CGroup: /system.slice/firewalld.service
             └─720 /usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid

sie 04 08:47:24 server10.example.com systemd[1]: Starting firewalld - dynamic firewall daemon...
sie 04 08:47:25 server10.example.com systemd[1]: Started firewalld - dynamic firewall daemon.

[user@server10 ~]$ sudo firewall-cmd --state
running

20.2.2. Dodowanie usług, portów oraz zarządzanie strefami

Zanim jednak zaczniemy dodwać usługi i porty wyświetlimy sobie domyślną strefę, a do tego posłuży opcja --get-default-zone polecenia firewall-cmd:

[user@server10 ~]$ sudo firewall-cmd --get-default-zone 
[sudo] hasło użytkownika user: 
public

Aby zmiany dokonane w zaporze zostały na stałe, należy każdą zmianę poprzedzić opcją --permanent. Usługi możemy dodwać za pomocą opcji --add-service. Podczas zapisywania opcji polecenia firewall-cmd możemy posłużyć się klawiszem Tab w celu autouzupełnienia. W ramach przykładu dodałem usługę http do domyślniej strefy.

[user@server10 ~]$ sudo firewall-cmd --permanent --add-service=http
success

Aby ta reguła zadziałała firewalld musi ponownie wczytać plik strefy, aby to wymusić wydajemy polecenie firewall-cmd wraz opcją --reload.

[user@server10 ~]$ sudo firewall-cmd --reload
[sudo] hasło użytkownika user: 
success

Za pomocą opcji --list-services, możemy wyświetlić reguły skonfigurowane do wskazanych usług:

[user@server10 ~]$ sudo firewall-cmd --list-services
cockpit dhcpv6-client http ssh

Jak możemy zauważyć dodana usługa znajduje się na liście reguł.

Dodawanie portów wygląda podobnie do dodawania usług jednak używa się opcji --add-port=. Po podaniu portu należy również wskazać po ukośniku (/) protokół TCP czy UDP. Opcja ta pozwala również na podanie zakresu portów. Za pomocą poniższego polecenia dodałem porty TCP od 5901 do 5910, do strefy (--zone=) internal.

[user@server10 ~]$ sudo firewall-cmd --permanent --add-port=5901-5910/tcp --zone=internal
[sudo] hasło użytkownika user: 
success

Za pomocą opcji --list-ports, możemy wyswietlić zasady skonfigurowane pod konkretne porty. Natomiast za pomocą opcji --zone możemy zarządzać innymi strefami.

[user@server10 ~]$ sudo firewall-cmd --list-ports --zone=internal
5901-5910/tcp

Za pomocą opcji --set-default-zone= możemy ustawić dowolną strefę jako tę domyślną dla naszego systemu:

[user@server10 ~]$ sudo firewall-cmd --set-default-zone=internal
[sudo] hasło użytkownika user: 
success

20.2.3. Usuwanie portów oraz usług

Aby wycofać wcześniej wykonywane zmiany należy użyć opcji --remove-service= lub --remove-port=. Usunę na początku regułę odpowiedzialną za otworzenie zakresu portów z poprzedniego przykładu.

[user@server10 ~]$ sudo firewall-cmd --permanent --remove-port=5901-5910/tcp 
success
[user@server10 ~]$ sudo firewall-cmd --reload 
success
[user@server10 ~]$ sudo firewall-cmd --list-ports

Opcja --remove-port również pozwala na podanie zakresu. Również w tym przypadku wymagane jest podanie protokołu.

Teraz usunę również usługę HTTP, którą dodałem na początku, pamiętając o tym, że public to nie jest już moją domyślną strefą.

[user@server10 ~]$ sudo firewall-cmd --permanent --remove-service=http --zone=public
success
[user@server10 ~]$ sudo firewall-cmd --reload 
success
[user@server10 ~]$ sudo firewall-cmd --list-services --zone=public
cockpit dhcpv6-client ssh

Na koniec przywrócę moją domyślną strefę.

[user@server10 ~]$ sudo firewall-cmd --set-default-zone=public
success
[user@server10 ~]$ sudo firewall-cmd --get-default-zone 
public

20.2.4. Efektywność zapory firewalld

W tym przykładzie sprawdzimy sobie w jaki sposób zapora działa w praktyce. A przetestujemy to sobie usuwając regułę pozwalającą na połączenie się przez SSH na jednej z maszyn.

Czynności rozpoczynamy od ustalenia domyślnej strefy oraz czy występuje w niej usługa SSH.

[user@server10 ~]$ sudo firewall-cmd --get-default-zone 
public
[user@server10 ~]$ sudo firewall-cmd --list-services 
cockpit dhcpv6-client ssh

Jak widzimy reguła SSH znajduje się na liście usług. Za pomocą opcji --remove-service= możemy usunąć tą regułę.

[user@server10 ~]$ sudo firewall-cmd --permanent --remove-service=ssh
[sudo] hasło użytkownika user: 
success
[user@server10 ~]$ sudo firewall-cmd --reload
success
[user@server10 ~]$ sudo firewall-cmd --list-services 
cockpit dhcpv6-client

Próba połączenia z tą maszyną z innej nie dochodzi do skutku.

[user@server20 ~]$ ssh server10
ssh: connect to host server10 port 22: No route to host

Aby zakończyć to ćwiczenie, ten przykład, dodajmy z powrotem regułę SSH do domyślnej strefy i spróbujmy się połączyć ponownie.

[user@server10 ~]$ sudo firewall-cmd --permanent --add-service=ssh
success
[user@server10 ~]$ sudo firewall-cmd --reload
success
[user@server10 ~]$ sudo firewall-cmd --list-services 
cockpit dhcpv6-client ssh

Tym razem próba kończy się powodzeniem.

[user@server20 ~]$ ssh server10
user@server10's password: 
Activate the web console with: systemctl enable --now cockpit.socket

Register this system with Red Hat Insights: insights-client --register
Create an account or view all your systems at https://red.ht/insights-dashboard
Last login: Thu Aug  4 09:03:57 2022 from 192.168.122.7
[user@server10 ~]$ 

Ćwiczenie 1: Dodawanie usługi do zapory

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server10, dodaj na stałe regułę dla usługi HTTPS i aktywuj ją (--reload). Potwierdź wykonanie zadania wyświetlając plik strefy oraz za pomocą odpowiedniej opcji polecenia firewall-cmd.

Ćwiczenie 2: Dodawanie zakresu portów do zapory

Jako użytkownik z możliwością podniesienia uprawnień, na maszynie oznaczonej jako server10, dodaj zakres portów 8000-8005 UDP do strefy trusted i aktywuj go (--reload). Potwierdź wykonanie zadania wyświetlając plik strefy oraz za pomocą odpowiedniej opcji polecenia firewall-cmd.

Podsumowanie

W tym rodziale poruszyliśmy temat zapory sieciowej. Dowiedzieliśmy się jak ona działa oraz co realizuje jej funkcję w RHEL 9. Poznaliśmy podstawy teoretyczne oprogramowania firewalld oraz poznaliśmy najczęściej stosowane polecenia za pomocą pokazanych w tym rozdziale przykładów. W następnym rozdziale zapoznamy się mechanizmem SELinux.

21. Security Enchanced Linux

Security Enchanced Linux (SELinux) decyduje o tym co kto może zrobić w systemie. Ten mechanizm tworzy kolejną warstwę bezpieczeństwa ponad klasycznymi zabezpieczeniami jakie poznaliśmy do tej pory. Podstawowym celem SELinux jest możliwe jak największe zmniejszenie wpływu na system w momencie wystąpienia w nim nieuprawnionego dostępu.

21. Podstawy teoretyczne SELinux

SELinux jest implementacją mechanizmu MAC (Mandatory Access Control), który jest zintegrowany z jądrem Linuksa. MAC ogranicza możliwości dostępu podmiotu (użytkownik lub proces) do obiektu (pliki, katalogi, systemu plików, urządzenia, połączenia/interfejsy sieciowe, porty, potoki oraz gniazda). Dlatego też w przypadku wykorzystania podatności na przykład na serwerze WWW, atakujący będzie mieć wpływ tylko na pliki, do których miał dostęp ten proces.

Do zapewnienia kontroli, mechanizm MAC wykorzystuje zbiory zasad autoryzacyjnych zwanych polityką do sprawdzenia atrybutów podmiotu kiedy stara się on o dostęp do obiektu i decyduje czy udzielić tego dostępu. Atrybutu są zapisane w postaci kontekstu (lub etykiet) i dotyczą zarówno podmiotów jak i obiektów. Decyzje dokonane przez SELinux są zapisywane w specjalnym rodzaju pamięci podręcznej nazwanej AVC (Access Vector Control). W momencie gdy dochodzić do decyzji SELinux sprawdza tę pamięć podręczną czy nie udzielał już temu podmiotowi dostępu do tego obiektu. Jeśli tak obecną decyzje bazuje na tej podjętej wcześniej przez co nie musi on rozważać wszystkich możliwych zasad polityki wobec tej próby dostępu.

21.1.1. Pojęcia związane z SELinux

Aby obeznać się chociaż w podstawowym stopniu z SELinux należy poznać kilka pojęć, które będą używane podczas przedstawiania teorii tego mechanizmu ale również przykładów oraz ćwiczeń.

21.1.2. Kontekst SELinux użytkowników

Za pomocą opcji -Z polecenie id możemy poznać kontekst SELinux użytkowników.

[user@server10 ~]$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

W polu użytkownika SELinux jego nazwa to unconfined_u co oznacza, że ma nieograniczone możliwości. Superużytkownik również przypisanego ma tego samego użytkownika. Pomimo tego SELinux dostarcza siedemiu swoich użytkowników ograniczających wpływ zwykłych użytkowników na system. Aby wyświetlić ich nazwy musimy zainstalować pakiet setools-console.

[user@server10 ~]$ sudo seinfo -u
[sudo] hasło użytkownika user: 

Users: 8
   guest_u
   root
   staff_u
   sysadm_u
   system_u
   unconfined_u
   user_u
   xguest_u

Jak możemy zauwazyć unconfined_u również jest jednym z użytkowników SELinux dla tego też mówimy o siedmiu. Przypisanie go użytkownikom systemu wynika z mapowania w polityce, a te możemy podejrzeć za pomocą polecenia semanage wraz z pod poleceniem login oraz opcją -l.

[user@server10 ~]$ sudo semanage login -l

Login                Użytkownik SELinuksa Zakres MLS/MCS       Usługa

__default__          unconfined_u         s0-s0:c0.c1023       *
root                 unconfined_u         s0-s0:c0.c1023       *

W pierwszej kolumnie Login znajdują się nazwy użytkowników systemowych. Wartość _default_ tyczy się wszystkich zwykłych użytkowników.

21.1.3. Kontekst procesów

Za pomocą opcji -Z znanego już polecenia ps, możemy wyświetlić kontekst SELinux procesów. Ponizej znajduje się przykład:

[user@server10 ~]$ ps -eZ
LABEL                               PID TTY          TIME CMD
system_u:system_r:init_t:s0           1 ?        00:00:01 systemd
...

W polu LABEL znajduje się kontekst SELinux. Podmiotem w tym przypadku jest użytkownik SELinux system_u (mapowany do superużytkownika), rolą, rola systemowa - system_r z typem ochrony nałożonym przez domenę init_t i poziomem bepzieczeństwa wskazywanym przez s0.

Każdy niechroniony proces działa w domenie unconfined_t.

21.1.4. Kontekst SELinux plików

Pliki oraz katalogi to obiekty SELinux one również posiadają kontekst, który może zostać wyświetlony za pomocą opcji -Z polecenia ls. Poniżej znajdują się atrybuty oraz kontekst dla pliku /etc/passwd.

[user@server10 ~]$ ls -lZ /etc/passwd
-rw-r--r--. 1 root root system_u:object_r:passwd_file_t:s0 1764 08-03 08:46 /etc/passwd

Ten plik posiada rolę przeznaczoną dla zwykłych obiektów (object_r) oraz typ o nazwie passwd_file_t. Kontekst wszystkich plików w systemie składowany jest dwóch plikach file_context oraz file_context.local w katalogu /etc/selinux/targeted/context/files. Kontekst może zostać zmieniony za pomocą polecenia semanage.

21.1.5. Zachowanie kontekstu SELinux podczas zarządzania plikami

Domyślnie kopiując pliki z jednego katalogu do drugiego kontekst SELinux, zostanie odziedziczony z katalogu nadrzędnego, taka sama zasada tyczy się nadpisywania plików, wówczas plik źródłowy otrzyma kontekst SELinux od pliku, który został nadpisany. Aby zachować kontekst SELinux należy użyć opcji --preserve wraz z wartością context.

Przenoszenie nie ma wpływu na kontekst SELinux. Kontekst pozostaje w nienaruszonym stanie.

Jeśli istnieje potrzeba aby zachować kontekst SELinux w archiwach tar to należy użyć opcji --selinux.

Przetestujemy sobie część tych przypadków podczas przykładów.

21.1.6. Kontekst SELinux portów sieciowych

SELinux definiuje atrybuty bezpieczeństwa dla każdego rozdzaju obiektu w tym i dla portów. Typy (w ujęciu SELinux) dla portów możemy podejrzeć za pomocą polecenia semanage wraz z podpoleceniem port oraz opcją -l.

[user@rhel90 ~]$ sudo semanage port -l
Typ portu SELinuksa            Protokół Numer portu
ftp_data_port_t                tcp      20
ftp_port_t                     tcp      21, 989, 990
ftp_port_t                     udp      989, 990
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
ssh_port_t                     tcp      22

Wyjście prezentowane jest kolumnach, w przypadku portów spotykamy się z typem, tak jak w przypadku plików. Domyślnie SELinux pozwala na nasłuchiwanie usługom tylko na określonych przez typy portach.

21.1.7. Zmiana domeny SELinux

SELinux pozwala procesom na zmianę domeny, aby mogły uruchomić aplikację, która może działać tylko w jednej określonej domenie. Tego typu zachowanie jest oczywiście poparte regułami w polityce, które wspierają tego typu działania. Zmiana domeny możliwa jest za pomocą uprawnienia entrypoint, które kontroluje taką możliwość. Najprostszym tego przykładem jest zmiana hasła.

SELinux w swojej polityce posiada zasady, dzięki którym proces działający w domenie passwd_t może modyfikować plik z typem shadow_t. W tej polityce istnieje także uprawnienie entrypoint w domenie passwd_exec_t. Dzięki czemu polecenie passwd może modyfikować plik /etc/shadow, gdzie tak naprawdę nikt nie ma żadnych klasycznych uprawnień. Poniżej znajdują się atrybut wraz kontekstem zarówno polecenia passwd oraz pliku /etc/shadow.

[user@server10 ~]$ ls -lZ /usr/bin/passwd 
-rwsr-xr-x. 1 root root system_u:object_r:passwd_exec_t:s0 32648 2021-08-10  /usr/bin/passwd
[user@server10 ~]$ ls -lZ /etc/shadow
----------. 1 root root system_u:object_r:shadow_t:s0 1368 08-03 08:47 /etc/shadow

21.1.8. Wartości logiczne SELinux

Wartości logiczne SELinux są używane jako przełączniki włączające lub wyłączające pozwolenie na wykonanie pewnej akcji. W ujęciu polityki przełączają one zasady bez potrzeby ich pownownego wczytywania. Za pomocą tych wartości możemy pozwolić aby anonimowi użytkownicy FTP mogli zapisywać na serwerze dane. Wszyskie te wartości są złożone w katalogu /sys/fs/selinux/booleans. Poniżej znajduje się fragment listingu tego katalogu, w systemie takim jak RHEL znajduje się wiele tych wartości

[user@server10 ~]$ ls -l /sys/fs/selinux/booleans/ | head 
razem 0
-rw-r--r--. 1 root root 0 08-06 09:07 abrt_anon_write
-rw-r--r--. 1 root root 0 08-06 09:07 abrt_handle_event
-rw-r--r--. 1 root root 0 08-06 09:07 abrt_upload_watch_anon_write
-rw-r--r--. 1 root root 0 08-06 09:07 antivirus_can_scan_system
-rw-r--r--. 1 root root 0 08-06 09:07 antivirus_use_jit
-rw-r--r--. 1 root root 0 08-06 09:07 auditadm_exec_content
-rw-r--r--. 1 root root 0 08-06 09:07 authlogin_nsswitch_use_ldap
-rw-r--r--. 1 root root 0 08-06 09:07 authlogin_radius
-rw-r--r--. 1 root root 0 08-06 09:07 authlogin_yubikey
...

Nazwa kazdego pliku odpowiada nazwie wartości używanej do zmiany jej stanu. Jeśli na podstawie nazwy nie możemy wywnioskować do czego służy podana wartość, to możemy zainstalować pakiet selinux-policy-doc i wyszukać odpowiednią stronę podręcznika podając nazwę wartości poleceniu man po opcji -K.

Możemy przeglądać wartości logiczne i zmieniać ich stan tymczasowo co nię będzie miało wpływu na politykę lub na stałe, wówczas te będzie musiało mieć już swoje odzwierciedlenie w polityce.

21.2. Zarządzanie SELinux

Zarządzanie SELinux to wiele zadań obejmujących aktywację mechanizmu, zmianę jego stanu operacyjnego, aktualizacje kontekstu zarówno podmiotów jak i obiektów oraz przełączanie wartości logicznych. W tym celu w RHEL znajduje się wiele narzędzi, które możemy wykorzystać do tego celu.

21.2.1. Polecenia zarządzające

RHEL dostarcza wiele poleceń, dzięki którym możemy realizować nasze zadania związane z SELinux. Wiele z nich odpowiada za konkretne czynności, jednak jedno semanage pozwala na zrealizowanie tych zadań dobierając odpowiednie podpolecenie. Innym poleceniem, na które należy zwrócić uwagę jest chcon, które pozwala na zmianę contekstu obiektu oraz restorcon, które przywraca kontekst z bazy (plików wymienionych w poprzednim podrozdziale, podczas omawiania kontektu plików). Poniżej znajdują się przykłady, dzięki którym poznamy jak należy zarządzać SELinux w praktyce oraz niezbędne do tego polecenia.

21.2.2. Zmiana stanu operacyjnego SELinux

Stan operacyjny tego mechanizmu możemy zmienić, zmieniając wartość jednej z dyrektyw w pliku konfigracyjny SELinux. W tym pliku możemy również wyłączyć ten mechanizm. Na poniższym przykładzie znajduje się jego zawartość:

[user@server10 ~]$ cat /etc/selinux/config 

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
# See also:
# https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-selinux/
#getting-started-with-selinux-selinux-states-and-modes
#
# NOTE: In earlier Fedora kernel builds, SELINUX=disabled would also
# fully disable SELinux during boot. If you need a system with SELinux
# fully disabled instead of SELinux running with no policy loaded, you
# need to pass selinux=0 to the kernel command line. You can use grubby
# to persistently set the bootloader to boot with selinux=0:
#
#    grubby --update-kernel ALL --args selinux=0
#
# To revert back to SELinux enabled:
#
#    grubby --update-kernel ALL --remove-args selinux
#
SELINUX=enforcing
# SELINUXTYPE= can take one of these three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

Zachowanie SELinux możemy kontrolować za pomocą dyrektywy SELINUX. Dyrektwa może przyjmować takie wartości jak:

Inna wartością kontrolujcą ten mechanizm jest dyrektywa SELINUXTYPE wymuszająca typ polityki. Domyślnie stosowaną jest polityka ukierunkowana (targeted). Pozostałe wartości znajdują się w komentarzu nad dyrektywą.

Do uzyskania informacji na temat działania SELinux możemy skorzystać z polecenia getenforce.

[user@server10 ~]$ sudo getenforce
[sudo] hasło użytkownika user: 
Enforcing

Tymczasem do chwilowej zmiany stanu tego mechanizmu możemy użyć polecenia setenforce.

[user@server10 ~]$ sudo setenforce
usage:  setenforce [ Enforcing | Permissive | 1 | 0 ]
[user@server10 ~]$ sudo setenforce 0
[user@server10 ~]$ sudo getenforce
Permissive

Przy czym do wyboru mamy tylko Enforcing (1) lub Permissive (0).

Aby jednak na stałe zmienić stan operacyjny SELinux musimy zmienić to pliku konfiguracyjnym a następnie uruchomić ponownie system.

Odnośnie egzaminu
Jeśli zajdzie taka potrzeba możemy przełączać się między stanami operacyjnymi, aby na przykład rozwiązać problem z usługą. Po rozwiązaniu problemów, możemy powrócić do normalnego stanu.

21.2.3. Sprawdzanie stanu SELinux

Z pomocą polecenia sestats, mozemy sprawdzić stan mechanizmu SELinux oraz wyświetlić kilka dodatkowych informacji na jego temat.

[user@server10 ~]$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      33

Z informacji zwracanych przez to polecenie możemy wywnioskować w jakim trybie działa SELinux (Current mode:) oraz rodzaj używanej polityki (Loaded policy name:).

Używając opcji -v możemy wyświetlić kontekst, niektórych plików oraz procesów. Są one zapisane w pliku /etc/sestatus.conf.

21.2.4. Modyfikowanie kontekstu SELinux plików

Do modyfikowania kontekstu słuzy polecenie chcon. Chcąc zmienić użytkownika SELinux używamy opcji -u, do zmiany typu wykorzystujemy opcję -t. Jeśli zmiany mają być rekurencyjne wówczas należy uzyć opcji -R. Na poniższym przykładzie zobrazowałem to zmieniając kontekst katalogu /tmp/testsedir1 w którym znajdował się plik testsefile2.

[user@server10 ~]$ ls -ldZ /tmp/testsedir1/
drwxr-xr-x. 2 user user unconfined_u:object_r:user_tmp_t:s0 25 08-06 12:34 /tmp/testsedir1/
[user@server10 ~]$ ls -lZ /tmp/testsedir1/testsefile2 
-rw-r--r--. 1 user user unconfined_u:object_r:user_tmp_t:s0 0 08-06 12:34 /tmp/testsedir1/testsefile2

[user@server10 ~]$ sudo chcon -v -u user_u -t public_content_t /tmp/testsedir1 -R
[sudo] hasło użytkownika user: 
zmiana kontekstu bezpieczeństwa '/tmp/testsedir1/testsefile2'
zmiana kontekstu bezpieczeństwa '/tmp/testsedir1'

[user@server10 ~]$ ls -ldZ /tmp/testsedir1/
drwxr-xr-x. 2 user user user_u:object_r:public_content_t:s0 25 08-06 12:34 /tmp/testsedir1/
[user@server10 ~]$ ls -lZ /tmp/testsedir1/testsefile2 
-rw-r--r--. 1 user user user_u:object_r:public_content_t:s0 0 08-06 12:34 /tmp/testsedir1/testsefile2

Jak możemy zauważyć na powyższym przykładzie zmieniliśmy typ oraz użytkownika SELinux.

21.2.5. Dodwanie oraz zatwierdzanie kontekstu plików SELinux

Aby zatwierdzić zmieniony kontekst plików należy dodać go do bazy kontekstów. Do tego posłużymy się poleceniem semanage wraz z podpoleceniem fcontext, za pomocą opcji -a wskazujemy akcje dodania nowego kontekstu, opcja -s wskazuje na użytkownika SELinux, opcja -t wskazuje na typ w tym przypadku. Na końcu podajemy ścieżkę do pliku lub katalogu. Jeśli zmiany mają obejmować katalog wraz z jego zawartością to wówczas należy użyć wyrażenia regularnego: (/.*)? w ścieżce katalogu. Teraz dodam zmiany z wcześniejszego podrozdziału do bazy.

[user@server10 ~]$ sudo semanage fcontext -a -s user_u -t public_content_t '/tmp/testsedir1(/.*)?'
[sudo] hasło użytkownika user: 

[user@server10 ~]$ sudo semanage fcontext -Cl
fcontext SELinuksa       typ                Kontekst

/tmp/testsedir1(/.*)?    all files          user_u:object_r:public_content_t:s0 

Za pomocą opcji -Cl wyświetliłem ostatnie zmiany w bazie kontekstów plików SELinux. Teraz jeśli zmienimy kontekst tego katalogu lub któregoś z zawartych w nim plików to będziemy mogli powrócić do poprzedniego stanu za pomocą polecenia restorecon bazując na wpisie w bazie danych, który dodaliśmy we wcześniejszym przykładzie. Aby to za prezentować zmienię teraz kontekst zarówno katalogu jak i jego zawartości.

[user@server10 ~]$ ls -ldZ /tmp/testsedir1/
drwxr-xr-x. 2 user user user_u:object_r:public_content_t:s0 25 08-06 12:34 /tmp/testsedir1/
[user@server10 ~]$ ls -lZ /tmp/testsedir1/testsefile2 
-rw-r--r--. 1 user user user_u:object_r:public_content_t:s0 0 08-06 12:34 /tmp/testsedir1/testsefile2

[user@server10 ~]$ sudo chcon -v -u user_u -t usr_t /tmp/testsedir1 -R
zmiana kontekstu bezpieczeństwa '/tmp/testsedir1/testsefile2'
zmiana kontekstu bezpieczeństwa '/tmp/testsedir1'

[user@server10 ~]$ ls -ldZ /tmp/testsedir1/
drwxr-xr-x. 2 user user user_u:object_r:usr_t:s0 25 08-06 12:34 /tmp/testsedir1/
[user@server10 ~]$ ls -lZ /tmp/testsedir1/testsefile2 
-rw-r--r--. 1 user user user_u:object_r:usr_t:s0 0 08-06 12:34 /tmp/testsedir1/testsefile2

[user@server10 ~]$ sudo restorecon -Rv /tmp/testsedir1/
Relabeled /tmp/testsedir1 from user_u:object_r:usr_t:s0 to user_u:object_r:public_content_t:s0
Relabeled /tmp/testsedir1/testsefile2 from user_u:object_r:usr_t:s0 to user_u:object_r:public_content_t:s0

[user@server10 ~]$ ls -ldZ /tmp/testsedir1/
drwxr-xr-x. 2 user user user_u:object_r:public_content_t:s0 25 08-06 12:34 /tmp/testsedir1/
[user@server10 ~]$ ls -lZ /tmp/testsedir1/testsefile2 
-rw-r--r--. 1 user user user_u:object_r:public_content_t:s0 0 08-06 12:34 /tmp/testsedir1/testsefile2

Opcja -R polecenia restorecon włącza rekurencję, natomiast -v włącza wyświetlanie komunikatów diagnostycznych.

Odnośnie egzaminu:
Podczas zmiany kontekstu plików oraz katalogów warto korzystać z polecenia semanage oraz restorecon aby dodawać kontekst do bazy a następnie przywrócić go na pliku. Przez co będziemy mieć pewność że zmiany z pewnością przetrwają ponowne uruchomienie serwera.

21.2.6. Dodawanie i usuwanie portów sieciowych w ujęciu SELinux

Jak mogliśmy się dowiedzieć domyślnie SELinux pozwala na nasłuchiwanie tylko na określonych typami portach więc jeśli nasza usługa musi nasłuchiwać na inny niż te w określonym type musimy dodać ten port do mechanizmu SELinux.

Dodawnie portów możemy zrealizować za pomocą polecenia semanage, ale na początku określimy typ portu SELinux. Za pomocą podpolecenia port wraz z opcją -l możemy wylistować wszystkie porty wraz z protokołami oraz typami portów. Załóżmy, że chcę dodać port do usługi http, wówczas możemy wyszukać portów powiązanych z tą usługą za pomocą polecenie grep.

[user@server10 ~]$ sudo semanage port -l | grep 'http'
[sudo] hasło użytkownika user: 
http_cache_port_t              tcp      8080, 8118, 8123, 10001-10010
http_cache_port_t              udp      3130
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t            tcp      5988
pegasus_https_port_t           tcp      5989

Potrzebny port dodam do typu http_port_t.

[user@server10 ~]$ sudo semanage port -at http_port_t -p tcp 8010
[user@server10 ~]$ sudo semanage port -l | grep 'http_port_t'
http_port_t                    tcp      8010, 80, 81, 443, 488, 8008, 8009, 8443, 9000

Aby dodać port użyłem podpolecenia port wraz z opcjami -at, które nakazują dodanie portu oraz wskaznie na typ portu SELinux. Następnie po opcji -p podałem protokół oraz numer portu. Na koniec wyświetliłem jeszce ten typ portu, aby sprawdzić czy zmiany zostały zatwierdzone.

Jeśli port przestanie nam być potrzebny możemy go usunąć, za pomocą opcji -d oraz opcji -p po której podajemy protokół i numer portu. Teraz usunę ten port, który wcześniej dodałem.

[user@server10 ~]$ sudo semanage port -d -p tcp 8010
[sudo] hasło użytkownika user: 
[user@server10 ~]$ sudo semanage port -l | grep 'http_port_t'
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

Jak możemy zauważyć nie ma już portu 8010 na początku listy przydzielonej do typu http_port_t.

Odnośnie egzaminu
Aby korzystać z niestandarowych portów dla usług należy dodać je do typów tych usług w SELinux.

21.2.7. Zachowanie kontekstu SELinux podczas kopiowania

Aby zachować kontekst SELinux dla kopiowanych plików, należy użyć opcji --preserve=context polecenia cp. W tym podrozdziale sobie to zobrazujemy. Nasz plik testowy w katalogu /tmp/testsedir1 prezentuje się z następującymi atrybutami:

[user@server10 ~]$ ls -lZ /tmp/testsedir1/testsefile2 
-rw-r--r--. 1 user user user_u:object_r:public_content_t:s0 0 08-06 12:34 /tmp/testsedir1/testsefile2

Teraz jeśli przekopiuje go z pomocą polecenia cp do katalogu /var/local jego atrybuty będą prezentować się w następujący sposób.

[user@server10 ~]$ ls -ldZ /var/local/testsefile2 
-rw-r--r--. 1 root root unconfined_u:object_r:var_t:s0 0 08-06 14:36 /var/local/testsefile2

Jak widzimy kontekst uległ zmianie. Jeśli jest to dla nas ważne z pewnych względów, to aby temu zapobiec należy używać opcji --preserve=context. Teraz usunę wcześniej skopiowany plik następnie skopiuje go jeszcze raz tym raz wykorzystując do tego wspomnianą opcję.

[user@server10 ~]$ sudo cp --preserve=context /tmp/testsedir1/testsefile2 /var/local/
[user@server10 ~]$ ls -ldZ /var/local/testsefile2 
-rw-r--r--. 1 root root user_u:object_r:public_content_t:s0 0 08-06 14:40 /var/local/testsefile2

Jak widzimy, w tym przypadku kontekst nie uległ zmianie. Ta opcja tyczy się wyłącznie kopiowania, ponieważ przenoszenie nie zmienia kontekstu.

[user@server10 ~]$ sudo mv /tmp/testsedir1/testsefile2 /var/local/
[user@server10 ~]$ ls -ldZ /var/local/testsefile2
-rw-r--r--. 1 user user user_u:object_r:public_content_t:s0 0 08-06 12:34 /var/local/testsefile2

21.2.8. Zmiana wartości logicznych SELinux

Zabierając się za zmianę wartości logicznych SELinux warto, na początku upewnić się jaki stan mają wartości. Możemy to zrobić za pomocą trzech różnych narzędzi. Na potrzeby przykładu wybrałem wartość use_nfs_home_dirs. Poniżej znajdują się trzy możliwe sposoby na sprawdzenie stanu tej wartości.

[user@server10 ~]$ sudo semanage boolean -l | grep 'use_nfs_home_dirs'
use_nfs_home_dirs              (wyłączone,wyłączone)  Allow use to nfs home dirs
[user@server10 ~]$ sudo getsebool -a | grep 'use_nfs_home_dirs'
use_nfs_home_dirs --> off
[user@server10 ~]$ sudo sestatus -b | grep 'use_nfs_home_dirs'
use_nfs_home_dirs                           off

Najlepszym sposóbem wydaje mi się, że jest użycie pierwszego polecenia, gdyż domyślnie zwraca więcej informacji. W nawiasie wyjścia tego polecenia możemy zauważyć dwa stany pierwszy z nich to stan obecny, natomiast drugi to stan pobrany z polityki.

Aby zmienić stan wartości logicznej należy użyć polecenia setsebool, jako argument to polecenie przyjmuje nazwę wartości oraz stan, w tym przypadku 0 lub 1.

[user@server10 ~]$ sudo setsebool use_nfs_home_dirs 1
[sudo] hasło użytkownika user: 
[user@server10 ~]$ sudo semanage boolean -l | grep 'use_nfs_home_dirs'
use_nfs_home_dirs              (włączone,wyłączone)  Allow use to nfs home dirs

Teraz ta wartość jest włączona, ale jeśli uruchomie komputer ponownie, to wartość wróci do stanu określonego w polityce. Jeśli chcemy aby, zmiany były trwałe należy dodać do wcześniej użytego polecenia opcję -P.

[user@server10 ~]$ sudo setsebool -P use_nfs_home_dirs 1
[user@server10 ~]$ sudo semanage boolean -l | grep 'use_nfs_home_dirs'
use_nfs_home_dirs              (włączone,włączone)  Allow use to nfs home dirs

Teraz stan wartości został ustawiony na stałe.

21.2.9. Monitorowanie naruszeń SELinux

Nie zależnie od trybu działania SELinux, generuje ostrzeżenia i zapisuje je za pomocą demona auditd do pliku /var/log/audit/audit.log lub przy użyciu rsyslog do pliku /var/log/messages jeśli demon audit jest niedostępny. Ostrzeżenia generowane są podczas odmowy dostępu i zapisywane wraz z odpowiednimi informacjami w wyżej wymienionych plikach. SELinux analizuje te ostrzeżenia w celach identyfikacji powodu odmowy dostępu. Wyniki tych analiz są zapisywane wraz z możliwymi rekomendacjami jak naprawić napotkamy problem.

Za pomocą polecenia sealert wraz z opcją -a możemy sprawdzić podany plik pod kątem występowania ostrzeżeń. Jeśli takowe wstąpią zostanie nam zwrócony na ten temat raport.

[user@server10 ~]$ sudo sealert -a /var/log/audit/audit.log
100% done
found 0 alerts in /var/log/audit/audit.log

W moim systemie niestety brak alertów.

Ćwiczenie 1: Zmiana trybu operacyjnego SELinux

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1 wyłącz SELinux z poziomu pliku konfiguracyjnego, następnie uruchom ponownie komputer i sprawdź stan operacyjny SELinux. Następnie przywróć go z powrotem do trybu enforcing i znów uruchom ponownie komputer. Potwierdź zmianę trybu za pomocą odpowiedniego polecenia.

Ćwiczenie 2: Modyfikacja kontekstu plików

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1. Utwórz w katalogu /tmp katalog d1 zawierający podkatalog d2. Następnie zmień rekurencyjnie kontekst katalogu /tmp/d1 ustawiając typ SELinux etc_t. Potwierdź to następnie dodaj zmiany bazy kontekstów.

Ćwiczenie 3: Dodawanie portów

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1, dodaj port 8010 do typu SELinux dla usługi HTTP. Potwierdź wykonanie tej czynności.

Ćwiczenie 4: Kopiowanie plików z zachowaniem kontekstu

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1, utworz plik o nazwie sef2 w katalogu /tmp, wyświetl jego kontekst. Skopiuj go do katalogu /usr z zachowaniem kontekstu, ponownie wyświetl kontekst pliku w katalogu /usr aby upewnić się, że kontekst rzeczywiście się nie zmienił.

Ćwiczenia 5: Zmiana stanu wartości logicznej SELinux

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server1, wyświetl stan wartości logicznej ssh_use_tcpd za pomocą polecenia getsebool oraz sestatus. Użyj odpowiedniego polecenia aby zmienić jej stan. Potwierdź to, wyświetlając go na wszystkie trzy sposoby.

Podsumowanie

Ten rozdział został poswięcony w pełni podstawom znajomości oraz obsługi mechanizmu SELinux. Poznaliśmy podstawy teoretyczne łącznie wymaganą do rozeznania w temacie terminologią. W dalszej części rozdzialu zapoznaliśmy się w jaki sposób możemy manipulować elementami tego mechanizmu w praktyce. Na koniec poznaliśmy metodę na monitorowanie ostrzeżeń generowanych przez SELinux. W następnym rozdziale zapoznamy się z podstawami tworzenia skryptów powłoki.

22. Skrypty powłoki

Skrypty powłoki są to listy poleceń zapisywane w jednym pliku tekstowym. Polecenia zapisane w skryptach są najzwyklejszymi poleceniami, tak jakbyśmy chcieli wykonać je z poziomu wiersza polecenia. Skrypty służą głównie automatyzacji nudnych oraz powtarzalnych zadań, na przykład w systemie należy utworzyć konta dla 100 użytkowników. Skrypty składają się z trzech głownych składników: poleceń/instrukcji, konstrukcji kontroli (tj. konstrukcji warunkowych oraz pętli) i komentarzy, które służą wyłącznie opisowi tego co się dzieje w skrypcie i nie biorą udziału w jego interpretacji. Skrypt powłoki są interpretowane przez powłokę, która jest uruchamiana wraz uruchomieniem skryptu. Rozwijane są użyte w skryptach wyrażania a polecenia są uruchamiane po kolei, jedno po drugim tak jak wpisywalibyśmy je ręczenie. W przypadku błędów powłoka zwróci odpowiedni komunikat oraz numer linii, w której ten błąd wystąpił.

22.1. Podstawy skryptów powłoki

Jeśli spytamy się kogo kolwiek jak jest najlepsza metoda nauki jakiegoś języka programowania, zapewne usłyszymy kilka zdań, które można streścić do: czytaj, zrozum, pisz, powtórz. Tak może być i w tym przypadku. Obecnie skrypty powłoki BASH uznawane są branży IT jako podstawowe narzędzie do automatyzacji i konstrukcje używane do budowy skryptów uznaje się za język programowania. Zatem zaczniemy od czegoś prostego, skryptu który wpisze nam podstawowe informacje o systemie.

[user@server10 ~]$ cat -n /usr/local/bin/sys_info.sh
     1	#!/bin/bash
     2	
     3	# Ten skrypt ma nazwe sys_info.sh i został napisany przez xf0r3m 8 sierpnia 2022
     4	# Ten skrypt powinien znajdować się w katalogu /usr/local/bin
     5	# Ten skrypt wyświetla podstawowe informacje o systemie:
     6	
     7	echo "Podstawowe informacje o systemie:";
     8	echo "================================";
     9	echo;
    10	echo "Nazwa komputera, informacje o sprzęcie oraz systemie operacyjnym:";
    11	/usr/bin/hostnamectl;
    12	echo;
    13	echo "Obecnie w systemie zalogowani są następujący użytkownicy:";
    14	/usr/bin/who;

Na początku każdego skryptu znajduje się nagłówek, w którym jest ścieżka, wskazująca na program interpretera zawartości skryptu. Nagłówek zaczyna się od specjalnego typu kometarza jakim jest shebang (#!). W tym przypadku jest wskazaniem na program bash w katalogu /bin. W ten sposób wskazujemy, że zawartością tego pliku będzie skrypt powłoki BASH. Kolejnymi elementami skryptu są komentarze (zwykłe). Komentarz poprzedzony jest tylko i wyłącznie krzyżykiem (#), w komentarzach najczęściej umieszczane są wyjaśnienia bardziej skomplikowanych konstrukcji lub toku myślenia programisty. Na początku skryptu umieszcza się bardziej informacyjne treści jak nazwę skryptu, imię, nazwisko, bądź pseudomnim autora; date jego wytworzenia; domyślną lokalizacje; krótki opis co on robi oraz przykłady użycia. W dalszej części przykładu znajdują się już właściwe polecenia. Polecenie echo służy do wypisywania na stdout tego co zostanie podane jako argument polecenia. W skryptach to polecenie służy do wyświetlania informacji w terminalu. Później znajdują się polecenia, które odpowiadają za czynności, do których skrypt został stworzony. Zwrócimy uwagę na to, że są one zapisane w postaci plików, które rzeczywiście z tego polecenia są odpowiedzialne. Ten zapis zapewnia, że napewno zostanie uruchmiony ten program, a nie np. jakiś alias lub polecenie wbudowane.

hostnamectl uruchomiony bez żadnych argumentów, zwróci podsumowanie na temat używanego sprzętu oraz systemu operacyjnego. Natomiast polecenie who wyświetla zalogowanych użytkowników.

Dobrym miejscem na nasze skrypty jest katalog /usr/local/bin. Prze co będzie nam łatwiej je uruchamiać, ponieważ ten katalog znajduje się w ścieżce wyszukiwania poleceń każdego użytkownika. Niestety zapis do tego katalogu wymaga podniesienia uprawnień. Po zapisaniu takie skryptu jeśli zwrócimy uwagę na jego atrybuty, brakuje w nich bitu wykonania a on jest niezbędny aby można było taki program uruchomić.

[user@server10 ~]$ ls -l /usr/local/bin/sys_info.sh
-rw-r--r--. 1 root root 494 08-08 08:38 /usr/local/bin/sys_info.sh

Bit nadamy każdej z grupie, żebyśmy mogli uruchomić go jako zwykły użytkownik. Do nadania bitu użyjemy polecenie chmod.

[user@server10 ~]$ sudo chmod +x /usr/local/bin/sys_info.sh
[user@server10 ~]$ ls -l /usr/local/bin/sys_info.sh
-rwxr-xr-x. 1 root root 494 08-08 08:38 /usr/local/bin/sys_info.sh

Teraz wszyscy mogą uruchomić ten plik, a z racji tego iż znajduje się on na ścieżce wyszukiwania poleceń wystarczy, że podamy w wierszu polecenia nazwę pliku.

[user@server10 ~]$ sys_info.sh 
Podstawowe informacje o systemie:
================================

Nazwa komputera, informacje o sprzęcie oraz systemie operacyjnym:
 Static hostname: server10.example.com
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: 301347671a7c47a5934351c451479d32
         Boot ID: bb68cbe389874bebaa4cf60693187c89
  Virtualization: kvm
Operating System: Red Hat Enterprise Linux 9.0 (Plow)     
     CPE OS Name: cpe:/o:redhat:enterprise_linux:9::baseos
          Kernel: Linux 5.14.0-70.13.1.el9_0.x86_64
    Architecture: x86-64
 Hardware Vendor: Red Hat
  Hardware Model: KVM

Obecnie w systemie zalogowani są następujący użytkownicy:
user     pts/0        2022-08-08 08:28 (192.168.122.1)

22.1.1. Debugowanie skryptu

Jeśli w skrypcie występuje jakiś problem możemy uruchomić go w trybie debugowania. Taki tryb dostępny jest albo poprzez dodanie do nagłówka skryptu opcji -x lub uruchomienia skryptu za pomocą polecenia bash wraz z opcją -x. W tym trybie będą wyświetlane wykonywane koleno polecenia wraz z rozwinięciami wyrażeń, czy przypisywane wartości zmiennym. Osobiście jako, że BASH jest moim podstawowym językiem to uważam, że ta opcja jest szczególnie przydatna oczywiście w bardziej zaawansowanych skryptach. Poniżej znajduje się wcześniej analizowany skrypt uruchomiony w trybie debugowania.

[user@server10 ~]$ bash -x /usr/local/bin/sys_info.sh
+ echo 'Podstawowe informacje o systemie:'
Podstawowe informacje o systemie:
+ echo ================================
================================
+ echo

+ echo 'Nazwa komputera, informacje o sprzęcie oraz systemie operacyjnym:'
Nazwa komputera, informacje o sprzęcie oraz systemie operacyjnym:
+ /usr/bin/hostnamectl
 Static hostname: server10.example.com
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: 301347671a7c47a5934351c451479d32
         Boot ID: bb68cbe389874bebaa4cf60693187c89
  Virtualization: kvm
Operating System: Red Hat Enterprise Linux 9.0 (Plow)     
     CPE OS Name: cpe:/o:redhat:enterprise_linux:9::baseos
          Kernel: Linux 5.14.0-70.13.1.el9_0.x86_64
    Architecture: x86-64
 Hardware Vendor: Red Hat
  Hardware Model: KVM
+ echo

+ echo 'Obecnie w systemie zalogowani są następujący użytkownicy:'
Obecnie w systemie zalogowani są następujący użytkownicy:
+ /usr/bin/who
user     pts/0        2022-08-08 08:28 (192.168.122.1)

Linie oznaczone plusem, są poleceniami zapisanymi w skrypcie. Pod nimi znajduje się wynik działania tego polecenia.

22.1.2. Zmienne

Jak pamiętamy albo nie z rozdziału o powłoce BASH, dostępne są dwa rodzaje zmiennych. Zmienne powłoki nazywane także zmiennymi prywatnymi lub lokalnymi, mogą być wykorzystywane w skryptach, nie tylko w obsłudze wiersza polecenia. W skryptach możemy również uzyskać dostęp do zmiennych środowiskowych.

[user@server10 ~]$ cat -n /usr/local/bin/use_var.sh
     1	#!/bin/bash
     2	
     3	echo "Ustawienie zmiennej powłoki";
     4	echo "===========================";
     5	SYSNAME="server10.example.com";
     6	echo "Nazwa komputera tej maszyny to: $SYSNAME";

W powyższym skrypcie utworzyłem jedną zmienną której nadałem przedstawioną wartość a następnie odwołałem się do niej w poleceniu echo. Warto tutaj też wspomnieć o tym, że każdy skrypt uruchamiany jest jako oddzielny proces powłoki BASH - uruchamiany jest w podpowłoce. Zmienne zdefiniowane w skrypcie nie będą dostępne, kiedy skończy on swoje działanie (chyba że do wykonania skryptu użyjemy polecenia source).

Dostęp do zmiennych środowiskowych wymaga jedynie odwołnia się do nich.

[user@server10 ~]$ cat -n /usr/local/bin/pre_env.sh 
     1	#!/bin/bash
     2	
     3	echo "Lokalizacja mojej powłoki znajduje się: ";
     4	echo $SHELL;
     5	echo "Jestem zalogowany w systemie jako: $LOGNAME";

W bardziej zaawansowanych skryptach pożądane jest aby przechować wynik działania jakiegoś polecenia lub polecenia potokowego w zmiennej, żeby można było go użyć do różnych celów. Wynik działania tego polecenia może trafić do zmiennej za sprawą podstawienia polecenia. Poniżej znajduje się skrypt, w którym pokazano dwa sposoby na podstawienie polecenia do zmiennej.

[user@server10 ~]$ cat -n /usr/local/bin/cmd_out.sh 
     1	#!/bin/bash
     2	
     3	SYSNAME=$(hostname);
     4	KERNVER=`uname -r`;
     5	
     6	echo "Nazwa komputera to: $SYSNAME";
     7	echo "Wersja jądra to: $KERNVER";

Jak widzimy podstawienie polecenia możemy dokonać na dwa sposoby za pomocą nawiasów poprzedzonych znakiem dolara ($()) lub za pomocą ukośnego apostrofu (``). Ten skrypt przechowuje w nazwę komputera w zmiennej SYSNAME oraz wersję jądra w zmiennej KERNVER. Te informacje zostały zdefiniowane w zmiennych za pomocą mecanizmu podstawienia polecenia, następnie zostały on wyświetlone przy użyciu polecenia echo.

22.1.3. Parametry powłoki

Chcąc przekazać jakieś informacje do skryptu z zewnątrz, możemy wykorzystać mechanizm parametrów powłoki. Wywołując jakieś polecenie w wierszu polecenia, zazwyczaj zapisujemy jego nazwę. To jest parametr powłoki. Wśrod parametrów powłoki możemy wyróżnić takie rzeczy jak zmienne, parametry specjalne oraz parametry pozycjne. Na potrzeby tego materiału zajmiemy się tylko parametrami pozycyjnymi oraz obsługującymi je parametrami specjalnymi. Nazwa polecenia lub skryptu jest pierwszym parametrem pozycyjnym. Każdy kolejny argument zapisany po nazwie polecenia to kolejny parametr pozycyjny. Parametry pozycyjne są liczone od zera. Możemy odwołać się do nich podając po znaku dolara numer parametru. Parametrów pozycyjnych może być wiele. Przy czym, aby odwołać się do parametru powyżej 9 należy umieść numer parametru w nawiasach klamrowych np. (${11}). Poniżej znajduje się skrypt oraz jego wywołanie, który obrazuje przekazanie parametrów do skryptu.

[user@server10 ~]$ cat -n /usr/local/bin/cmd_line_arg.sh 
     1	#!/bin/bash
     2	
     3	echo "Do skryptu wprowadzono $# argumenty pozycyjne";
     4	echo "Są nimi kolejno: $*";
     5	echo "Pierwszym argument jest: $1";
     6	echo "Identyfikator powłoki uruchamiającej ten skrypt to: $$";

[user@server10 ~]$ /usr/local/bin/cmd_line_arg.sh test test2 test3 1 2 3 4 5 6 7 8 9 0
Do skryptu wprowadzono 13 argumenty pozycyjne
Są nimi kolejno: test test2 test3 1 2 3 4 5 6 7 8 9 0
Pierwszym argument jest: test
Identyfikator powłoki uruchamiającej ten skrypt to: 1874

W skrypcie odwołano się do drugiego parametru pozycjnego, czyli do argumentu o numerze 1 (linia: 5). W skrypcie użyto parametrów specjalnych takich jak: liczba przekazanych argumentów pozycyjnych - ($#); wszystkie przekazane argumenty - ($*) oraz numer PID podpowłoki, która jest odpowiedzialna za wykonanie tego skryptu - ($$).

Chcąc używać po kolei parametrów pozycyjnych, musimy tak też je zapisać w skrypcie, a co jeśli ilość tych zmiennych nie będzie nam znana. W tym przypadku wówczas możemy przesuwać wszystkie parametry o jeden w lewo. Jeśli użyjemy wartość drugiego parametru pozycjnego ($1) i nie będzie nam już ona potrzebna, możemy użyć polecenia shift, które przesunie wszystkie parametry o jeden w lewo (domyślnie, ponieważ polecenie to przyjmuje argument w postaci liczby, o ile wartości przesunąć parametry). Dlatego też wartość trzeciego parametru przejdzie na drugą pozycję, czwartego na trzecią itd. Po użyciu tego polecenia oryginalna wartość drugie parametru zostanie utracona. Poniżej znajduje się skrypt, w którym zobrazowano działanie tego polecenia.

[user@server10 ~]$ cat -n /usr/local/bin/cmd_line_arg_shift.sh
     1	#!/bin/bash
     2	
     3	echo "Do skryptu wprowadzono $# argumenty pozycyjne";
     4	echo "Są nimi kolejno: $*";
     5	echo "Pierwszym argument jest: $1";
     6	echo "Identyfikator powłoki uruchamiającej ten skrypt to: $$";
     7	shift;
     8	echo "Nowym pierszym argumentem po pierwszym przesunięciu jest: $1";
     9	shift;
    10	echo "Nowym pierwszy argumentem po drugim przesunięciu jest: $1";

Jak widzimy przy użyciu polecenia shift nie musimy używać innych odwołań do parametrów poza odwołaniem do drugiego (parametr o numerze 1).

22.2. Konstrukcje logiczne w skryptach powłoki

Konstrukcje logiczne nie tylko w skryptach powłoki mają za zadanie warunkować na podstawie wyrażeń logicznych wykonanie pewnych bloków kodu. Konstrukcje logiczne kontolują przepływ wykonania poleceń w skryptach. W BASH możemy spotkać z dwoma kontrukcjami logicznymi: konstrukcją if-then-fi oraz konstrukcją case. Omawianie konstrukcji case wychodzi poza ramy tego materiału, wiec skupimy się tylko if-then-fi i jej rozszerzeniach, ale najpierw zaczniemy od przedstawienia czym są kody wyjściowe oraz wyrażenia warunkowe.

22.2.1. Kody wyjściowe

Każde polecenie uruchomione w powłoce po zakończeniu pracy, nie ważne czy pomyślnie, czy nie zwraca do powłoki tzw. exit code - kod wyjściowy. Możemy użyć tych kodów w wyrażeniach warunkowych aby mieć pewność, że działanie polecenia uruchomionego w poprzedniej linii zakończyło się sukcesem i można kontynuować wykonywanie przez skrypt kolejnych zadań. Kod wyjściowy poprzedniego polecenia znajduje się w specjalnym parametrze powłoki $?.

[user@server10 ~]$ pwd
/home/user
[user@server10 ~]$ echo $?
0
[user@server10 ~]$ man
Jakiej strony podręcznika potrzebujesz?
For example, try 'man man'.
[user@server10 ~]$ echo $?
1

Pierwsze polecenie, którego wykonanie zakończyło sie sukcesem zwróciło kod wyjściowy 0. Natomiast w przypadku polecenia man przy którym zapomniałem o podaniu argumentu zwróciło wartość 1. W przypadku zastrzeżeń co do poprawnego wykonania zwracanym kodem wyjściowym jest najczęściej wartość niezerowa, nie zawsze jest to 1. Te wartości mogą być różne i zależne od programu.

Dlaczego mówie o kodach wyjściowych w przypadku konstrukcji logicznych, otóż powody są dwa. Można użyć kodów wyjściowych do budowania warunków, oraz konstrukcja if-then-fi podejmuje decyzje na podstawie kodu wyjściowego wyrażenia warunkowego. Zatem jeśli wyrażenie zwróci 0, to wówczas wykona się blok kodu zapisany po instrukcji then.

22.2.2. Wyrażenia warunkowe

Zadaniem wyrażenia warunkowego jest obliczenie równania logicznego. Na przykład czy: 3 < 4. Wynikiem takiego równia jest 1, czyli prawda, jeśli odwrócimy operandy równiania wówczas wówczas otrzymamy 0 czyli fałsz. Kiedy wyrażenie warunkowe obliczy prawdę wówczas zwraca kod wyjścia równy 0, a w przeciwnym wypadku jest 1. Elementami powłoki odpowiedzialnymi za wyrażenia warukowe jest polecenie test oraz rozszerzenie nawiasu kwadratowego ([]) i to tego rozszerzenia będziemy używać w konstrukcjach warunkowych. Na stronie podręcznika polecenia BASH w sekcji CONDITIONAL EXPRESSIONS znajdują się wszystkie dostępne operatory wyrażeń warunkowych jakie możemy użyć w skryptach.

Odnośnie egzaminu
Bardzo ważna jest dobra znajomość instrukcji logicznych oraz ich operatorów.

22.2.3. Konstrukcja if-then-fi

Za pomocą konstrukcji if-then-fi możemy warunkować na podstawie kodu wyjściowego wykonanie poleceń zapisanych w konstrukcji. Poniżej znajduje się przykładowy skrypt zawierający tę konstrukcję.

[user@server10 ~]$ cat -n /usr/local/bin/if_then_fi.sh
     1	#!/bin/bash
     2	
     3	if [ $# -ne 2 ]; then 
     4		echo "Błąd. Nie poprawna liczba podanych argumentów";
     5		echo "Użycie $0 plik_zrodlowy plik docelowy";
     6		exit 2;
     7	fi
     8	echo "Skrypt zakończył swoje działanie";

Konstrukcję rozpocznamy od słowa kluczowego if następnie podajemy polecenie lub wyrażenie warunkowe, którego kod wyjściowy będzie decydować czy wykonać polecenia zawarte po słowie klczowym then na przykładzie są to linie od 4 do 6. Konstrukcja kończy się słowem kluczowym fi, które musi być zawarte inaczej próba uruchomienia takiego skryptu zakończy się błędem. Jako ciekawostkę podam, że blok zawarty po słowie then konczy się poleceniem exit, dzięki któremu możemy zakończyć działanie skryptu z określonym kodem wyjściowym.

Na zamieszczonym przykładzie użyłem do przedstawienia warunku rozszerzenia nawiasu kwadratowego, w ktorym sprawdzam czy podano dokładnie dwa paramentry pozycjne, operator którego użyłem -ne, który sprawdza czy lewy operand jest nierówny prawemu. Jeśli tak, to kod wyjściowy warunku będzie równy 0 i dojdzie do wykonania poleceń, co tym przypadku oznacza, że podaliśmy jeden lub więcej niż dwa parametry pozycyjne podczas uruchamiania tego skryptu.

22.2.4. Konstrukcja if-then-else-fi

Ta konstrukcja jest rozszerzeniem standardowej konstrukcji if. Zawiera ona dodatkowy blok else, który będzie wykonywany w momencie gdy kod wyjściowy warunku/polecenia będzie niezerowy.

[user@server10 ~]$ cat -n /usr/local/bin/if_then_else_fi.sh
     1	#!/bin/bash
     2	
     3	if [ $1 -gt 0 ]; then
     4		echo "$1 jest wartością dodatnią";
     5	else
     6		echo "$1 jest wartością ujemną";
     7	fi

W tym przypadku warunek w postaci rozszerzenia nawiasów kwadartowych (najpopularniejsza forma warunków w skryptach powłoki) sprawdza czy wartość podana jako drugi parametr pozycyjny jest większa od 0. Jeśli tak, to wykonane zostanie polecenie znajdujące się w po słowie then a jeśli nie to polecenie po słowie kluczowym else.

22.2.5. Konstrukcja if-then-elif-fi

Rozszerzenie konstrukcji if o słowo kluczowe elif pozwala na zdefiniowane kolejnego warunku. W przypadku konstrukcji if-then-elif-fi, warunków możebyć nieskończeniei wiele i dodatkowo możemy zakończyć całą konstrukcję blokiem else.

[user@server10 ~]$ cat -n /usr/local/bin/if_then_elif_fi.sh 
     1	#!/bin/bash
     2	
     3	if [ $1 -gt 0 ]; then
     4		echo "$1 jest dodatnią wartością.";
     5	elif [ $1 -lt 0 ]; then
     6		echo "$1 jest ujemną wartością.";
     7	elif [ $1 -eq 0 ]; then
     8		echo "Podana wartość to 0.";
     9	else
    10		echo "Podano wartość, która nie jest cyfrą";
    11	fi

Na powyższym przykładzie drugi parametr pozycyjny poddawany jest wielu warunkom. Początkowy warunek sprawdza czy jest on większy od 0 (operator -gt), następnie czy może jest mniejszy od 0 (operator -lt) i ostatni czy jest on równy 0 (operator -eq). W bloku else znajduje się komunikat mówiący, że podana wartość nie jest liczbą, będzie on wykonywany gdy jako argument podamy literę lub znak specjalny. Podczas uruchomienia tego skryptu wraz z literowym argumentem możemy zauważyć błąd. Ten błąd jest generowany z tego względu, iż wymienione wyżej operatory są przeznaczone dla liczb całkowitych, a litera jest znakiem.

22.3. Konstrukcje pętli

Za pomocą konstrukcji pętli możemy wykonywać ten sam blok poleceń dla określonych, zmienianych w trakcie działania bloku pętli zmiennych. Zmienne w trakcie działania pętli najczęściej są iterowane - ich wartość zmienia się o jeden lub o jakąś stałą określoną wartość. W BASH-u dostępne są trzy rodzaje pętli: for-do-done, while-do-done oraz until-do-done. W tym materiale skupimy się tylko na pętli for-do-done.

22.3.1. Konstrukcja pętli for-do-done

Pętla for jest inną pętlą niż te dwie pozostałe, gdyż działa ona na określonej liście wartości, które są kolejno przypisywane zmiennej pętli. Pętla działa do momentu, aż wyczerpią się wszystkie elementy na liście. Poniżej znajduje się przykład skryptu w którym użyłem pętli for.

[user@server10 ~]$ cat -n /usr/local/bin/for_do_done.sh 
     1	#!/bin/bash
     2	
     3	count=0;
     4	for LETTER in {A..Z}; do
     5		count=$((count + 1));
     6		echo "$count literą alfabetu jest [${LETTER}]";
     7	done

Pętla zaczyna się od słowa kluczowego for następnie zapisywana jest zmienna pętli (LETTER) po słowie kluczowym in występuje lista elementów, na której operuje pętla ({A..Z}) zwróćmy uwagę na to, iż możemy definiować tutaj zakresy co pokazano na przykładzie. Dla celów tego skryptu jeszcze przed pętlą utworono zmienną, która jest w tym przypadku jej licznikiem. Jest ona bowiem iterowana na początku bloku pętli, za pomocą wyrażenia arytmetycznego ($(())), w tym wyrażeniu nie odwołujemy się do zmiennej tylko podajemy jej nazwę. Odwołanie nastąpi automatycznie podczas interpretacji tego wyrażenia. Następnie wyświetlany jest komunikat, zawierający licznik oraz zmienną pętli. Pętla wypisze wszystkie litery alfabetu łacińskiego, poprzedzając je numerem w kolejności.

Aby lepiej zobrazować sobie działanie pętli możemy uruchomić ten skrypt w trybie debugowania.

Ćwiczenie 1: Poprawienie skryptu

Jako użytkownik z możliwością podniesienia uprawnień na maszynie oznaczonej jako server10. Popraw skrypt z podrozdziału o konstrukcji if-then-elif-fi, tak aby nie wyświetlał błędu. Możesz posłużyć się stroną podręcznika powłoki BASH oraz trybem debugowania.

Podsumowanie

W tym rozdziale poznaliśmy podstawy tworzenie skryptów powłoki. Dowiedziliśmy się jaką budowę ma skrypt powłoki oraz poznaliśmy ich podstawowe elementy. Poznaliśmy konstrukcje warunkowe oraz prostą konstrukcję pętli jąką jest pętla for. Jeśli domyślnie nie korzystamy z powłoki BASH przestawione tutaj przykłady po drobnych przeróbkach będą działać na pozostałych powłokach Uniksa. W następnym już ostatnim rozdziale zapoznamy się z mechanizmem konteneryzacji dostępnym w RHEL.

23. Kontenery

Kontener to sposób na uruchomienie aplikacji w odrębnym środowisku dzięki czemu, może ona działać i wykonywać swoje zadania bez wpływu na inne procesy przy zachowaniu jak największej oszczędności zasobów systemu. Kontenery dostarczają do systemu właściwą aplikację wraz cały środowiskiem niezbędnym do jej działania, wygląda ona tak samo jakby była zainstalowana na fizycznym serwerze. Aplikacja uruchamiana w kontenerze nazywana jest aplikacją konteneryzowaną.

Kontenery do swojego działania wykorzystują funkcje jądra Linux, takie jak grupy kontrolne (przydzielanie zasobów procesom), przestrzeń nazw (udostępnienie zasobów systemowych takich jak interfejsy sieciowe) oraz moduł seccomp i SELinux, które odpowiadają za kontrolowanie dostępu procesów. Wykorzystując te komponenty kontenery zapewniają w pełni kontrolowane środowisko o wysokim stopniu bezpieczeństwa.

Korzystanie z kontenerów przynosi ze sobą wiele korzyści takich jak izolacja aplikacji w kontenerach, małe powiązanie z system w którym kontener działa, niezależność kontenerów wobec siebie czy mniejsze obciążenie systemu. Korzyści jest naprawdę bardzo dużo, dlatego też konterneryzacja wypiera wirtualizację, ale oczywiście to wszystko zależy od konkretnego przypadku.

Przy wyborze miejsca docelowego dla konternerów możemy zastanawiać się nad fizycznym serwerem albo maszyną wirtualna. Korzystanie z maszyn wirtualnych wprowadza dodatkową warstwę hipernadzorcy, który je kontroluje. W przypadku jeśli kontenery mają swiadczyć usługi, mogą on spokojnie rezydować na fizycznym serwerze, bez dodatkowych komplikacji. Więc wybór wydaje się dość łatwy.

Kontenery są rozprowadzane na zasadzie obrazów (ang. images) zawierających całe środowisko określonej aplikacji, dzięki czemu może poprawnie pracować w izolowanym środowisku. Mechnizmy konteneryzacji dostarczane wraz z RHEL, przestrzegają zasad Inicjatywy Otwartych Kontenerów - OCI, dzięki czemu użytkownicy mogą tworzyć obrazy o przemysłowych standardach. Do zarządzania obrazami OCI wykorzystywane są takie narzędzia Docker oraz dostarczany wraz z Red Hat podman. Obrazy OCI korzystają z kontroli wersji, tak więc możemy skorzystać z niekoniecznie najnowszej wersji, a pobrany obraz może być wykorzystywany w więcej niż jednym kontenerze.

Obrazy konternerów przechowywane są w rejestrach. Rejestry mogą być prywatne i aby pobrać obraz należy się zalgować lub mogą być ogólno dostępne, publiczne i każdy może pobierać z nich obrazy. W internecie dostępnych jest kilka rejestrów, z czego tylko trzy z nich wykorzystuje się w RHEL: Red Hat Container Catalog występujący w dwóch różnych rejestrach oraz Docker Hub. Przyczym tylko Docker Hub jest rejestrem publicznym. Obrazy posiadają swoje nazewnictwo, nazwane FQIN (ang. Full Qualified Image Name). Ta nazwa składa się czterech części:

  1. Nazwy rejestru
  2. Nazwy właściciela/Nazwy organizacji
  3. Nazwy repozytorium
  4. Wersji (opcjonalnie)

Po złożeniu tych elementów ścieżka może wyglądać w następujący sposób: nazwa_rejestru/nazwa_organizacji/nazwa_repozytorium:wersja.

Kontenery mogą być uruchamiane za równo przez superużytkownika - mowimy wtedy o kontenerach uprzywilejowanych oraz przez zwykłych użytkowników - wówczas mówi o kontenerach nieuprzywilejowanych. Główną różnicą jest brak możlwości mapowania portów (przekierowania) poniżej portu 1024. Warto dodać, że uruchamianie kontenerów uprzywilejowanych może powodować potencjalne problemy z bezpieczeństwem w systemie. Kontenery nieuprzywilejowanene dają możliwość interakcji z nimi z poziomu superużytkownika i jeśli nie będzie takiej potrzeby lepiej korzystać z tego rodzaju kontenerów.

23.1. Praca z obrazami oraz kontenerami

Do pracy z kontenerami RHEL dostarcza dwa narzędzia: wspomniany już wcześniej podman oraz skopeo. Przyczym wszystkie czynności będą operać się na wykorzystaniu polecenia podman. Polecenie skopeo posłuży nam tylko do pobrania bardziej szczegółowych informacji na temat dostępnego w rejestrze obrazu. W tym ostatnim rodziale skupimy się na praktyce. Jeśli będzie trzeba coś dopowiedzieć, zostanie to dopisane podczas omawiania danej kwestii. Do rozpoczęcia pracy z konterami, potrzebujemy oprogramowania oraz dostępu do rejestrów Red Hat. Najlepiej więc zarejestrować tę instalację systemu na koncie firmy Red Hat, wtedy bez problemu będziemy mogli zaktualizować system a następnie zainstalować potrzebne oprogramowanie.

23.1.1. Rejestrowanie systemu Red Hat

Za pomocą konsoli wirtualne również możemy zarejestrować system w sieci Red Hat, do tego służy polecenie subscription-manager wraz z podpoleceniem register. Polecenie to wydajemy z podniesieniem uprawnień. Do rejestracji potrzebna jest nazwa użytkownika (login) podawany podczas logowania do Red Hat, podajemy ją po opcji --username. Na poniższym przykładzie przedstawiam składnie polecenia oraz zwracane przez nie informacje

[xf0r3m@primeb450 ~]$ sudo subscription-manager register --username
Registering to: subscription.rhsm.redhat.com:443/subscription
Password: 
The system has been registered with ID: 12d0865f-3b78-4ce3-93db-b836b50ef879
The registered system name is: primeb450.morketsmerke.net

Moja nazwa użytkownika została ocenzurowana na tym przykładzie, także po opcji --username znajduje się login do sieci Red Hat. Poprawność rejestracji możemy sprawdzić za pomocą polecenia attach. Poniżej pokazałem jak to wygląda w przypadku mojego serwera.

[xf0r3m@primeb450 ~]$ sudo subscription-manager attach
[sudo] password for xf0r3m: 
All installed products are covered by valid entitlements. No need to update subscriptions at this time.
Installed Product Current Status:
Product Name: Red Hat Enterprise Linux for x86_64
Status:       Subscribed

Teraz możemy korzystać z repozytoriów sieci Red Hat i na początek radzę zaktualizować system. Jeśli będziemy korzystać z tego systemu w celach innych niż testy jego i jego możliwości oraz zawartości, możemy podłączyć ten system już podczas instalacji.

[xf0r3m@primeb450 ~]$ sudo dnf update
[sudo] password for xf0r3m: 
Updating Subscription Management repositories.
Red Hat Enterprise Linux 9 for x86_64 - BaseOS (RPMs)                   2.5 MB/s | 3.6 MB     00:01    
Red Hat Enterprise Linux 9 for x86_64 - AppStream (RPMs)                6.1 MB/s | 9.2 MB     00:01    
Dependencies resolved.
...

23.1.2. Instalacja oprogramowania kontenerów

Moduły RHEL 9 nie są jeszcze dostępne. Pojawią się dopiero w kolejnym mniejszym wydaniu (9.1), ale w repozytoriach znajduje się pakiet, który wraz z zależnościami odpowiada za obsługę kontenerów w tej wersji systemu Red Hat. Tym pakietem jest container-tools.

[xf0r3m@primeb450 ~]$ sudo dnf install container-tools
[sudo] password for xf0r3m: 
Updating Subscription Management repositories.
Red Hat Enterprise Linux 9 for x86_64 - BaseOS (RPMs)                                        8.2 kB/s | 4.1 kB     00:00    
Red Hat Enterprise Linux 9 for x86_64 - AppStream (RPMs)                                      10 kB/s | 4.1 kB     00:00    
Dependencies resolved.
============================================================================================================================
 Package                Architecture       Version                        Repository                                    Size
============================================================================================================================
Installing:
 container-tools        noarch             1-10.el9                       rhel-9-for-x86_64-appstream-rpms             8.7 k
Installing dependencies:
 podman-docker          noarch             2:4.1.1-1.el9_0                rhel-9-for-x86_64-appstream-rpms              42 k
 podman-remote          x86_64             2:4.1.1-1.el9_0                rhel-9-for-x86_64-appstream-rpms             8.1 M
 python3-podman         noarch             3:4.0.0-1.el9                  rhel-9-for-x86_64-appstream-rpms             171 k
 python3-pyxdg          noarch             0.27-3.el9                     rhel-9-for-x86_64-appstream-rpms             108 k
 python3-toml           noarch             0.10.2-6.el9                   rhel-9-for-x86_64-appstream-rpms              46 k
 skopeo                 x86_64             2:1.8.0-4.el9_0                rhel-9-for-x86_64-appstream-rpms             6.7 M
 toolbox                x86_64             0.0.99.3-3.el9_0               rhel-9-for-x86_64-appstream-rpms             2.2 M
 udica                  noarch             0.2.6-4.el9                    rhel-9-for-x86_64-appstream-rpms              54 k

Transaction Summary
============================================================================================================================
Install  9 Packages

Total download size: 17 M
Installed size: 65 M
Is this ok [y/N]: 

Teraz w systemie dostępne będą takie narzędzia jak podman oraz skopeo.

23.1.3. Polecenie podman

Polecenie podman jest podstawowym polecenień związanym z obsługą kontenerów na RHEL. Za jego pomocą będziemy wykonywać wiekszość czynności związanych z kontenerami. Poniżej znajdują się najczęściej wykorzystywane podpolecenia.

Odnośnie egzaminu:
Zrozumienie przedstawionych wyżej podpoleceń oraz swobodne używanie polecenia podman jest kluczowe do wykonania zadań związanych z kontenerami.

23.1.4. Polecenie skopeo

Polecenie skopeo służy do obsługi obrazów zarówno tych znajdujących się na dysku, jak i tych, które znajdują się w rejestrach. Nie mniej jednak w tym materiale skupimy się tylko na jednym podpoleceniu jakim jest podpoleceniem inspect, które zwraca szczegóły na temat obrazu.

23.1.5. Plik registries.conf

W pliku registries.conf przechowywane są adresy rejestrów, z których możemy pobierać obrazy kontenerów. Plik znajduje się w katalogu /etc/containers jest on plikem globalnym, a każdy z użytkowników może przychowywać swoją kopię pliku w katalogu ~/.config/containers, ustawienia zawarte w plikach użytkowników nadpisują te wartości z pliku globalnego, co może być przydatne w przypadku kontenerów nieuprzywilejowanych. Poniżej znajduje się zawartości tego pliku:

[xf0r3m@primeb450 ~]$ grep -v '^#' /etc/containers/registries.conf 

unqualified-search-registries = ["registry.access.redhat.com", "registry.redhat.io", "docker.io"]

short-name-mode = "enforcing"

Plik zawiera masę komentarzy wyjaśniających wiele rzeczy. Nas jednak będzie interesować wyłącznie pierwsza linia zawierająca adresy rejestrów. W momencie poszukiwania obrazu polecenie podman odpytuje rejestry w zapisanej kolejności. Oczywiście jeśli jest taka potrzeba możemy dopisać swoje prywatne rejestry do tego pliku.

Odnośnie egzaminu:
Podczas egzaminu nie będzie dostępu do sieci, więc prawdopodbnie będzie trzeba ustawić rejestr prywatny znajdujący się w sieci egzaminacyjnej.

23.1.6. Informacje nt. konfiguracji kontenerów w systemie

Pierwszą czynnością jaką poznamy odnośnie kontenerów jest sprawdzenie konfiguracji programu podman, możemy zrealizować to za pomocą podpolecenia info. Poniżej znajduje się fragment wyniku działania tego polecenia:

[xf0r3m@primeb450 ~]$ podman info
host:
  arch: amd64
  buildahVersion: 1.26.2
  cgroupControllers:
  - memory
  - pids
  cgroupManager: systemd
  cgroupVersion: v2
  conmon:
    package: conmon-2.1.2-2.el9_0.x86_64
    path: /usr/bin/conmon
    version: 'conmon version 2.1.2, commit: 1ed53517f446a779f9d0edafe090ce821a41e255'
  cpuUtilization:
    idlePercent: 99.94
    systemPercent: 0.02
    userPercent: 0.04
  cpus: 8
  distribution:
    distribution: '"rhel"'
    version: "9.0"
  eventLogger: journald
  hostname: primeb450.morketsmerke.net
...

Polecenie to zwraca masę informacji od architektury przez informacje o zużyciu procesora, informacje o dystrybucji po informacje na temat pakietów odpowiedzialnych za kontenery w systemie oraz miejsce składowania obrazów. Warto wspomnieć że te informację mogą się różnić kiedy wydamy to polecenie z sudo, wówczas te informacje będą dotyczyć konfiguracji globalnej, przeznaczonej dla kontenerów uprzywilejowanych.

Po za tymi informacjami możemy za pomocą podpolecenia version zwrócić wersję polecenia podman oraz kilku dostarczanych wraz z nim składników.

[xf0r3m@primeb450 ~]$ podman version
Client:       Podman Engine
Version:      4.1.1
API Version:  4.1.1
Go Version:   go1.17.12
Built:        Wed Jul 27 16:26:10 2022
OS/Arch:      linux/amd64

23.2. Zarządzanie obrazami

Jedną z podstawowych czynności jakie możemy wykonać w obrębie kontenerów jest zarządzanie obrazami, czyli: wyszukanie, sprawdzenie szczegółów, pobranie oraz jego usunięcie. Tym zajmiemy się w tym podrozdziale.

Za pomocą polecenia podman z podpoleceniem search wyszukamy kontenera systemu bazodanowego MySQL.

[xf0r3m@primeb450 ~]$ podman search mysql
registry.redhat.io/rhel8/mysql-80                     This container image provides a containerize...
registry.redhat.io/rhel9/mysql-80                     rhcc_registry.access.redhat.com_rhel9/mysql-...

Te dwa obrazy wydają się najbardziej trafne. Opisy możemy rozwinąć dodając opcję --no-trunc. W pierwszej kolumnie wyjścia tego polecenia znajduje się FQIN w drugiej zaś opis obrazu. Obraz jest przeznaczony dla rhel9 co możemy wywnioskować po napisie znajującym się w drugiej części nazwy obrazu. Aby poznać więcej szczegółów na temat tego obrazu posłużymy się poleceniem skopeo wraz podpoleceniem inspect. To polecenie przyjmuje jako argument FQIN poprzedzonym wskazaniem protokołu Docker (docker://). Poniżej znajdują się informacje jakie udało się uzyskać poleceniu skopeo na temat tego obrazu.

[xf0r3m@primeb450 ~]$ skopeo inspect docker://registry.redhat.io/rhel9/mysql-80
FATA[0000] Error parsing image name "docker://registry.redhat.io/rhel9/mysql-80": unable to retrieve auth token: invalid username/password: unauthorized: 
Please login to the Red Hat Registry using your Customer Portal credentials. 
Further instructions can be found here: https://access.redhat.com/RegistryAuthentication 

Jeśli rejestrowaliśmy system za pomocą polecenia subscription-manager potrzebne może być zalogowanie się za do rejestrów. Logowania dokonujemy za pomocą polecenia podman wraz podpolecenie login oraz polecenia skopeo wraz podpoleceniem login, przy czym przy poleceniu skopeo należy podać adres rejestru.

[xf0r3m@primeb450 ~]$ sudo podman login
[sudo] password for xf0r3m: 
Username: 
Password: 
Login Succeeded!

[xf0r3m@primeb450 ~]$ skopeo login registry.redhat.io
Username: 
Password: 
Login Succeeded!

Polecenia zapytaja nas o nazwę użytkownika sieci Red Hat oraz o hasło. Moja nazwa użytkownika została ocenzurowana. Teraz powinniśmy otrzymać interesujące nas informacje na temat żądanego obrazu.

[xf0r3m@primeb450 ~]$ skopeo inspect docker://registry.redhat.io/rhel9/mysql-80
{
    "Name": "registry.redhat.io/rhel9/mysql-80",
    "Digest": "sha256:e6dbbce33d195df0380cb5b0207167be1ba45e4fe1229e50fc9d769b928e5cb7",
    "RepoTags": [
        "1-190-source",
        "1-197-source",
        "1-190.1655192188-source",
        "1-206",
        "1",
        "1-206-source",
        "1-190.1655192188",
        "1-197",
        "1-190",
        "latest"
...

Na przykładzie znajduje się tylko fragment, ale to polecenie zwraca bardzo dużo przydatnych informacji, na przykład przy kluczu RepoTags znajdują się dostępne wersje tego obrazu jakie możemy pobrać podając je pod dwukropku na końcu FQIN, domyślnie pobieraną wersją jest latest. Inną ciekawą wartością jest klucz usage, który zwraca nam całe polecenie, za pomocą którego możemy uruchomić ten kontener.

Za pomocą podpolecenia pull polecenia podman możemy pobrać wybrany przez nas obraz. To polecenie wymaga podania FQIN jako argumentu. Poniżej znajduje się wynik działania tego polecenia:

[xf0r3m@primeb450 ~]$ podman pull registry.redhat.io/rhel9/mysql-80
Trying to pull registry.redhat.io/rhel9/mysql-80:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob a021685edffe done  
Copying blob c6e5292cfd5f done  
Copying blob bf30f05a2532 done  
Copying blob f540faa799cb done  
Copying config 536612cfc7 done  
Writing manifest to image destination
Storing signatures
536612cfc795266464d7828ff38ab12c44a55620174a769243d4cf31edadc8db

A tak przezentuje się obraz na liście obrazów polecenia podman:

[xf0r3m@primeb450 ~]$ podman images
REPOSITORY                         TAG         IMAGE ID      CREATED     SIZE
registry.redhat.io/rhel9/mysql-80  latest      536612cfc795  7 days ago  558 MB

Aby poznać szczegóły pobranego już obrazu należy skorzystać z podpolecenia inspect polecenia podman. Jako argument wystarczy że podamy ostatni człon FQIN w tym przypadku będzie to: mysql-80.

[xf0r3m@primeb450 ~]$ podman inspect mysql-80
[
     {
          "Id": "536612cfc795266464d7828ff38ab12c44a55620174a769243d4cf31edadc8db",
          "Digest": "sha256:e6dbbce33d195df0380cb5b0207167be1ba45e4fe1229e50fc9d769b928e5cb7",
          "RepoTags": [
               "registry.redhat.io/rhel9/mysql-80:latest"
          ],
...

Jeśli obraz nie będzie nam już potrzebny możemy usunąć go z dysku za pomocą podpolecenia rmi polecenia podman. Podobnie jak w przypadku podpolecenia inspect wystarczy, że podamy ostatnią część FQIN.

[xf0r3m@primeb450 ~]$ podman rmi mysql-80
Untagged: registry.redhat.io/rhel9/mysql-80:latest
Deleted: 536612cfc795266464d7828ff38ab12c44a55620174a769243d4cf31edadc8db

Aby potwierdzić usunięcie obrazu możemy jeszcze raz wyświetlić ich listę.

[xf0r3m@primeb450 ~]$ podman images
REPOSITORY  TAG         IMAGE ID    CREATED     SIZE

23.3. Podstawy zarządzania kontenerami

Po opanowaniu obsługi obrazów przyszedł czas na pierwszy kontakt z kontenerami. Kontenery działają tak długo, jak działa aplikacja dla której są zbudowane. Trzeba sobie to wyjaśnić. Kontenery możemy podzielić na te nazwane oraz te bez nazw. Kontenery bez nazwy są jednorazowe i czesto usuwane po wykonaniu swojego zadania. Nie mniej jednak w tym pod rozdziale zajmiemy się zarówno jednym i drugim. Dla kontenerów na nazwanych wybrałem najnowszą wersję obrazu UBI czyli uniwersalnej bazy, która służy jako podstawa dla innych kontenerów zawierających już aplikacje.

23.3.1. Zarządzanie konterami nazwanymi

Aby uruchomić swój pierwszy kontenery należy na początku upewnić się, że w systemie w katalogu /etc nie znajduje się katalog docker. Jego obecność może powodować problemy z uprawnieniami.

[xf0r3m@primeb450 ~]$ ls -l /etc/docker
ls: cannot access '/etc/docker': No such file or directory

Kiedy tego katalogu nie ma w systemie możemy przejść do uruchomienia kontenera za pomocą poniższego polecenia.

[xf0r3m@primeb450 ~]$ podman run -it --name rhel9-base-os ubi9
Resolved "ubi9" as an alias (/etc/containers/registries.conf.d/001-rhel-shortnames.conf)
Trying to pull registry.access.redhat.com/ubi9:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob bf30f05a2532 done  
Copying blob c6e5292cfd5f done  
Copying config 168c58a383 done  
Writing manifest to image destination
Storing signatures
[root@baf4f328600d /]# 

Za pomocą podpolecenia run uruchomiłem kontener w trybie interaktywnym (-it). Kontener nazwałem za pomocą opcji --name, został on zbudowany w oparciu o obraz ubi9, czyli obraz bazowy RHEL 9. Nazwa obrazu została podana w krótkiej wersji, bez FQIN. W przypadku innych kontenerów niż tez zbudowane na UBI nie zaleca się używania tej formy przekazywania nazw obrazów. Polecenie samo rozwiązało FQIN na podstawie informacji zawartych w pliku /etc/containers/registries.conf.d/001-rhel-shortnames.conf. Po pobraniu obrazu oraz uruchomieniu na jego podstawie kontenera, została na zwrócona powłoka wewnątrz kontenera. W celach testowych możemy wykonać kilka podstawowych poleceń:

[root@baf4f328600d /]# ls
afs  bin  boot	dev  etc  home	lib  lib64  lost+found	media  mnt  opt  proc  root  run  sbin	srv  sys  tmp  usr  var
[root@baf4f328600d /]# pwd
/
[root@baf4f328600d /]# whoami
root
[root@baf4f328600d /]# cat /etc/redhat-release 
Red Hat Enterprise Linux release 9.0 (Plow)
[root@baf4f328600d /]# cat /etc/os-release 
NAME="Red Hat Enterprise Linux"
VERSION="9.0 (Plow)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="9.0"
PLATFORM_ID="platform:el9"
PRETTY_NAME="Red Hat Enterprise Linux 9.0 (Plow)"
ANSI_COLOR="0;31"
LOGO="fedora-logo-icon"
CPE_NAME="cpe:/o:redhat:enterprise_linux:9::baseos"
HOME_URL="https://www.redhat.com/"
DOCUMENTATION_URL="https://access.redhat.com/documentation/red_hat_enterprise_linux/9/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"

REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 9"
REDHAT_BUGZILLA_PRODUCT_VERSION=9.0
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="9.0"

W znaku zachęty, w miejscu nazwy hosta znajduje się ciąg znaków przedstawiających identyfikator kontenera. Jeśli opuścimy powłokę kontenera, zostanie on zatrzymany ponieważ wykonane zostało jego domyślne zadaniem jakim była obsługa procesu powłoki. Zatem tego kontenera trzeba szukać na liście zatrzymanych kontenerów.

[root@baf4f328600d /]# exit
exit
[xf0r3m@primeb450 ~]$ podman ps -a
CONTAINER ID  IMAGE                                   COMMAND     CREATED         STATUS                    PORTS       NAMES
baf4f328600d  registry.access.redhat.com/ubi9:latest  /bin/bash   14 minutes ago  Exited (0) 9 seconds ago              rhel9-base-os
[xf0r3m@primeb450 ~]$ 

Kontener ten możemy włączy ponownie, za pomocą podpolecenia start oraz nazwy kontenera.

[xf0r3m@primeb450 ~]$ podman start rhel9-base-os 
rhel9-base-os
[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE                                   COMMAND     CREATED         STATUS            PORTS       NAMES
baf4f328600d  registry.access.redhat.com/ubi9:latest  /bin/bash   16 minutes ago  Up 3 seconds ago              rhel9-base-os

Na kontenerach możemy wykonywać polecenia, bez podłączania się do ich powłoki, temu celowi służy podpolecenie exec. Wymaga ono podania nazwy uruchomionego kontenera oraz polecenia.

[xf0r3m@primeb450 ~]$ podman exec rhel9-base-os cat /etc/redhat-release
Red Hat Enterprise Linux release 9.0 (Plow)

Jeśli jednak chcielibyśmy podłączyć się do powłoki kontenera, to wówczas należy użyć podpolecenia attach wraz z nazwą kontenera.

[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE                                   COMMAND     CREATED         STATUS            PORTS       NAMES
baf4f328600d  registry.access.redhat.com/ubi9:latest  /bin/bash   18 minutes ago  Up 2 minutes ago              rhel9-base-os
[xf0r3m@primeb450 ~]$ podman attach rhel9-base-os 
[root@baf4f328600d /]# 

Po opuszczeniu powłoki, kontener zostanie zatrzymany. Jeśli nie potrzebujemy już w systemie kontenerów, możemy je usunąć za pomocą podpolecenia rm, przy czym należy upewnić się, że kontener jest zatrzymany inaczej polecenie niepowiedzie się.

[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES
[xf0r3m@primeb450 ~]$ podman rm rhel9-base-os 
baf4f328600dad7e8fce03d854a6c4e480453679181f97233dccee5ea5a9e5dc
[xf0r3m@primeb450 ~]$ podman ps -a
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES

Za pomocą pierwszego wywołania podpolecenia ps upewniłem się, że kontener nie jest uruchomiony, następnie za pomocą podpolecenia rm usunąłem kontener. Za pomocą podpolecenia ps z opcją -a sprawdziłem czy kontener jest rzeczywiście usunięty.

23.3.2. Zarządzanie kontenerami bez nazw

Zarządzanie kontenerami bez nazw, jest znacznie prostsze, ponieważ może zostać sprowadzone do jednego polecenia. Poniżej przedstawiłem w jaki sposób można uruchomić kontener bez nazwy, aby wykonał proces do które został stworzony następnie został usunięty z systemu.

[xf0r3m@primeb450 ~]$ podman run --rm docker.io/library/hello-world
Trying to pull docker.io/library/hello-world:latest...
Getting image source signatures
Copying blob 2db29710123e done  
Copying config feb5d9fea6 done  
Writing manifest to image destination
Storing signatures

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES
[xf0r3m@primeb450 ~]$ podman ps -a
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES

Kontener zwrócił informacje powitalną podobnego systemu kontenerów jakim jest Docker, następnie został usunięty co zostało potwierdzone za pomocą dwóch ostatnich poleceń na przykładzie.

23.4. Zaawansowane zarządzanie kontenerami

Po poznaniu podstaw, możemy przejść do bardziej zaawansowanych czynności wykonywanych na kontenerach, takich jak mapowanie portów, przekazywanie zmiennych środowiskowych, katalogu hostującej maszyny czy zarządania kontenerami z poziomu systemd.

23.4.1. Mapowanie portów hosta na porty kontenera

Odnośnie egzaminu:
Podczas wykonywania tej czynność, należy pamiętać o bardzo ważnej rzeczy. Mianowicie, kontenery nieuprzywilejowane nie mogą mapować portów na porty hosta poniżej 1024 portu.

Mapowanie portów spowoduje, że jaki kolwiek ruch sieciowy, który będzie skierowany do określonego portu hosta zostanie przekierowny do portu kontenera. Tą czynność wykonuje się poprzez użycie opcji -p podpolecenia run. Rozważmy poniższym przykład.

Na początek pobiorę obraz serwera HTTP.

[xf0r3m@primeb450 ~]$ podman pull registry.redhat.io/rhel9/httpd-24
Trying to pull registry.redhat.io/rhel9/httpd-24:latest...
Getting image source signatures
...

Następnie na podstawie tego obrazu przygotuje nazwany kontener z mapowaniem portów.

[xf0r3m@primeb450 ~]$ podman run -d -p 8080:80 --name rhel9-http-map registry.redhat.io/rhel9/httpd-24
cdb3fe727ec39de7323da49bb230f2dc22bdc51da43bc48e9218d2eeb09b166b
[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE                                     COMMAND               CREATED        STATUS            PORTS                 NAMES
cdb3fe727ec3  registry.redhat.io/rhel9/httpd-24:latest  /usr/bin/run-http...  3 seconds ago  Up 4 seconds ago  0.0.0.0:8080->80/tcp  rhel9-http-map

Za pomocą -d kontener zostaje odłączony od terminala i kontynuje pracę w tle. Przyczym ma to zastosowanie wyłącznie w przypadku programów, które oczekują na połączenia od innych np. demonów. Teraz ruch przychodzący na port 8080 hosta zostanie przekierowany do kontenera. Mapowania widać w kolumnie PORTS. Mapowania możemy zobrazować lepiej za pomocą podpolecenia port polecenia podman, przy czym to podpolecenie wymaga podania nazwy kontenera.

[xf0r3m@primeb450 ~]$ podman port rhel9-http-map 
80/tcp -> 0.0.0.0:8080

Na koniec usunąłem ten kontener, za pomocą poniższych poleceń.

[xf0r3m@primeb450 ~]$ podman stop rhel9-http-map 
rhel9-http-map
[xf0r3m@primeb450 ~]$ podman rm rhel9-http-map 
cdb3fe727ec39de7323da49bb230f2dc22bdc51da43bc48e9218d2eeb09b166b

23.4.2. Przekazywanie zmiennych środowiskowych do kontenerów

Nie które z konteneryzowanych aplikacji wymagają podania od użytkownika pewnych parametrów do uruchomienia. Te parametry przekazuje się za pomocą zmiennych środowiskowych, a są one przezkazywane za pomocą opcji -e podpolecenia run.

Odnośnie egzaminu:
Warto dodać, że przekazanie każdej wartości do kontenera wymaga podania opcji -e.

Dobrze obrazuje to poniższy przykład.

[xf0r3m@primeb450 ~]$ podman run -dt -e VAR1="test1" -e VAR2="test2" --name rhel9-cont-env ubi9
13a244c2d73d2d604591ebbdf2112e222392f3f046b90e220e55397888cae80a
[xf0r3m@primeb450 ~]$ podman exec rhel9-cont-env env
TERM=xterm
container=oci
VAR1=test1
VAR2=test2
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOME=/root

Za pomocą opcji -dt umożliwiłem działanie powłoki w tle (głownym procesem obrazów UBI jest powłoka). Przy użyciu opcji -e przekazałem do kontenera przedstawione na przykładzie zmienne środowiskowe. W drugim poleceniu używając podpolecenia exec uruchomiłem na kontenerze polecenie env, które zwraca zmienne środowiskowe. Jak wskazuje wynik działania tego polecenia, przekazane przez nas wartości są obecne w środowisku kontenera jako zmienne środowiskowe. Teraz możemy zatrzymać i usunąć ten kontener.

[xf0r3m@primeb450 ~]$ podman stop rhel9-cont-env 
rhel9-cont-env
[xf0r3m@primeb450 ~]$ podman rm rhel9-cont-env 
13a244c2d73d2d604591ebbdf2112e222392f3f046b90e220e55397888cae80a

23.4.3. Konfigurowanie miejsca na dane dla kontenerów

Kontenery działają tak długo, jak długo działa główny proces. Kiedy on kończy swoje działanie, tak i kontener zostaje zatrzymany. Zatrzymanie albo restart kontenera powodują utratę wszystkich wytworzonych przez niego danych. Jeśli te dane mogą być potrzebne, to należy zaopatrzyć kontenery w miejsce gdzie będą mogły je składować. Takim miejscem może być katalog znajdujący się gdzieś na hoście.

Odnośnie egzaminu:
Konfigurując taki katalog należy pamiętać o trzech czynnościach jakie należy wykonać: 1. Odpowiednie uprawnienia, w przykładach będę stosować raczej pełnych uprawnień, tj. 777; 2. Prawo własności, jeśli jest to kontener nieuprzywilejowany to włascicielem oraz grupą, do której ten katalog należy powinien być użytkownik, który uruchamia kontener; 3. Właściwy typ pliku SELinux, tej wartości nie trzeba konfigurować ręcznie, wystarczy że podamy na koniec wartości przekazywania katalogu opcję :Z, spowoduje ona samodzielne przypisanie odpowiedniego kontesktu SELinux.

Chcąc zobrazować jak może wyglądać takie przekazanie katalogu. Czynności należy rozpocząć od utworzenia katalogu, nadania mu odpowiednich uprawnień oraz prawa własności.

[xf0r3m@primeb450 ~]$ sudo mkdir /host_dir
[sudo] password for xf0r3m: 
[xf0r3m@primeb450 ~]$ sudo chmod 777 /host_dir/
[xf0r3m@primeb450 ~]$ sudo chown xf0r3m:xf0r3m /host_dir/

Następnie uruchomie kontener przekazując ten katalog jako katalog /container_dir w głównym systemie plików kontenera.

[xf0r3m@primeb450 ~]$ podman run -dt --name rhel9-persistent-data -v /host_dir:/container_dir:Z ubi9
96e45fa0558c0d2da216de2c91747aae1fc177301ea58eb81571f200f8022fb2

Za pomocą opcji -v dokonujemy przekazania katalogu do kontenera. Zwróćmy uwagę na dodatkową opcję :Z, powoduje ona dobranie odpowiedniego kontekstu SELinux. Teraz podłącze się pod kontener aby sprawdzić uprawnienia przekazanego katalogu, kontekst SELinux oraz to czy możemy przechowywać w nim dane.

[root@96e45fa0558c /]# ls -l 
total 0
dr-xr-xr-x.   2 root   root     6 Aug  9  2021 afs
lrwxrwxrwx.   1 root   root     7 Aug  9  2021 bin -> usr/bin
dr-xr-xr-x.   2 root   root     6 Aug  9  2021 boot
drwxrwxrwx.   2 root   root     6 Aug 11 11:52 container_dir
...
[root@96e45fa0558c /]# cd container_dir/
[root@96e45fa0558c container_dir]# echo "This is a test message!" > test_file.txt
[root@96e45fa0558c container_dir]# ls -ldZ /container_dir
drwxrwxrwx. 2 root root system_u:object_r:container_file_t:s0:c417,c733 27 Aug 11 11:55 /container_dir
[root@96e45fa0558c container_dir]# ls -lZ /container_dir/
total 4
-rw-r--r--. 1 root root system_u:object_r:container_file_t:s0:c417,c733 24 Aug 11 11:55 test_file.txt

Jak możemy zauważyć na powyższym przykładzie katalog przekazany do kontenera znajduje się w katalogu głównym tak jak to zostało zapisane podczas jego utworzenia. Następnie sprawdziłem czy da się utworzyć jakiś plik z zawartością w tym katalogu. Po wydaniu polecenia echo powłoka nie zwróciła żadnego błędu więc można było przypuszczać, że zapis do pliku powiódł się. Na koniec jeszcze sprawdziłem konteskt SELinux. Po zamknięciu powłoki, sprawdziłem czy utworzony plik znajduje się w katalogu przeznaczonym dla kontenera oraz plik posiada wcześniej zapisaną zawartość. Możemy się o tym przekonać analizując poniższy przykład:

[xf0r3m@primeb450 ~]$ ls -l /host_dir/
total 4
-rw-r--r--. 1 xf0r3m xf0r3m 24 Aug 11 13:55 test_file.txt
[xf0r3m@primeb450 ~]$ cat /host_dir/test_file.txt 
This is a test message!

Jak widzimy wszystko zadziałało. Teraz możemy usunąć ten kontener.

23.4.4. Kontrolowanie kontenerów przez systemd

Jeśli konterów w systemie jest bardzo wiele, łatwiej jest nimi zarządać za pomocą programu typu init, jakim jest systemd. Za pomocą podpolecenia generate polecenia podman, możemy wygenerować pliki jednostki usługi, dla każdego kontenera i zarządzać nimi jak usługami. Tego typu funkcja ma jedną bardzo ważną cechę przez co jest dość mocno wykorzystywana, mianowicie automatyczne uruchomienie kontenerów podczas startu systemu. Przy okazji tego zagadnienia również zauważymy różnicę między kontenerami nieuprzywilejowanymi oraz uprzyprzywilejowanymi, ponieważ przekazanie ich kontroli do systemd wygląda nieco inaczej.

Kontenery uprzywilejowane:

Na początku konfiguracji potrzebujemy kontenera. Utwórzmy prosty kontener na bazie UBI.

[xf0r3m@primeb450 ~]$ sudo podman run -dt --name rhel9-cont-systemd ubi9
[sudo] password for xf0r3m: 
Resolved "ubi9" as an alias (/etc/containers/registries.conf.d/001-rhel-shortnames.conf)
Trying to pull registry.access.redhat.com/ubi9:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob bf30f05a2532 done  
Copying blob c6e5292cfd5f done  
Copying config 168c58a383 done  
Writing manifest to image destination
Storing signatures
d9ef99614a37c58d55edbca805cbc4da5bd99dae9c1674ed3c336893dbe901d6

Nie ma się co dziwić jeśli polecenie to pobierze jeszcze raz obraz. Wynika to z tego, że konfiguracja kontenerów nieuprzywilejowanych i tych uprzywilejowanych jest zupełnie inna, te kontenery również mają oddzielny katalog, który przechowuje ich obrazy. Po utworzeniu katalogu generujemy plik jednostki, zapisujemy go katalogu konfiguracji lokalnej /etc/systemd/system.

sudo podman generate systemd --new rhel9-cont-systemd | sudo tee /etc/systemd/system/rhel9-cont-systemd.service

Podpolecenie generate wymaga podania typu generowanego pliku w tym przypadku jest systemd. Użyłem tutaj również opcji --new, co powoduje za każdym startem utworzenie nowego kontenera. Wynik musiałem przepuścić przez polecenie tee, ponieważ przy użyciu operatora przekierowania nie damy rady zapisać nic w tej lokalizacji. Po utworzeniu tego pliku możemy załadować plik jednostki i kontrolować kontener z poziomu systemd.

[xf0r3m@primeb450 ~]$ sudo systemctl daemon-reload 
[sudo] password for xf0r3m: 
[xf0r3m@primeb450 ~]$ sudo systemctl enable --now rhel9-cont-systemd.service
Created symlink /etc/systemd/system/default.target.wants/rhel9-cont-systemd.service → /etc/systemd/system/rhel9-cont-systemd.service.
[xf0r3m@primeb450 ~]$ sudo systemctl status rhel9-cont-systemd.service --no-pager
● rhel9-cont-systemd.service - Podman container-d9ef99614a37c58d55edbca805cbc4da5bd99dae9c1674ed3c336893dbe901d6.service
     Loaded: loaded (/etc/systemd/system/rhel9-cont-systemd.service; enabled; vendor preset: disabled)
     Active: active (running) since Thu 2022-08-11 14:44:32 CEST; 12s ago
       Docs: man:podman-generate-systemd(1)
    Process: 9123 ExecStartPre=/bin/rm -f /run/rhel9-cont-systemd.service.ctr-id (code=exited, status=0/SUCCESS)
   Main PID: 9294 (conmon)
      Tasks: 2 (limit: 48032)
     Memory: 980.0K
        CPU: 246ms
     CGroup: /system.slice/rhel9-cont-systemd.service
             └─9294 /usr/bin/conmon --api-version 1 -c fa9a83e06b6c9b75b64c69d80925b67e6d7458f3de5d78075b180bb2da0156fb -u fa9a83e06…

[xf0r3m@primeb450 ~]$ sudo podman ps
CONTAINER ID  IMAGE                                   COMMAND     CREATED         STATUS             PORTS       NAMES
fa9a83e06b6c  registry.access.redhat.com/ubi9:latest  /bin/bash   37 seconds ago  Up 37 seconds ago              rhel9-cont-systemd

Teraz możemy kontenery zatrzymywać oraz uruchamiać ponownie z poziomu polecenia systemctl. Zatrzymanie kontenera z tego poziomu spowoduje jego usunięcie z systemu. Utworzony plik usługi można traktować wówczas jako pliki szablonu dla kontenerów.

Kontenery nieuprzywilejowane:

W przypadku tego rodzaju kontenerów sprawa się dodatkowo komplikuje z powodu tego, iż kontenery są zatrzymywane w momencie wylogowania się użytkownika. Jednak w tym przypadku stosuje się sztuczkę, która powoduje pojawienia się użytkownika jeśli tylko jest potrzebny. Ta metoda nazywana jest lingering. Inną komplikacją jest fakt, że do obsługi systemd z poziomu zwykłego użytkownika, musi być on zalogowany albo fizycznie, albo za pomocą SSH, nie może być to przełączony użytkownik za pomocą poleceń sudo lub su.

Czynności tym razem zaczniemy od utworzenia katalogu ~/.config/systemd/user oraz włączenia lingeringu.

[xf0r3m@primeb450 ~]$ mkdir -p ~/.config/systemd/user
[xf0r3m@primeb450 ~]$ loginctl enable-linger

Lingering włącza się za pomocą pojedynczego polecenia, wiec nie jest jakoś zbytnio skomplikowane, a naprawdę robi robotę. Teraz możemy przejść do utworzenia kontenera.

[xf0r3m@primeb450 ~]$ podman run -dt --name rhel9-unpriv-cont-systemd ubi9
fbe3ab11d3728470f1fcd38f07b99bd7e151ac2fc0c4a6a8dad85c486c3bce60

Tego przykładu chyba nie muszę tłumaczyć. Teraz wygeneruje plik jednostki dla tego kontenera w utworzonym wcześniej katalogu.

podman generate systemd --new rhel9-unpriv-cont-systemd > ~/.config/systemd/user/rhel9-unpriv-cont-systemd.service

Teraz mogłem użyć przekierowania, ponieważ cały czas działam w obrębie tych samych uprawnień. Teraz załaduje plik jednostki i uruchomię nowy kontener, tutaj też użyłem opcji --new.

[xf0r3m@primeb450 ~]$ systemctl --user daemon-reload 
[xf0r3m@primeb450 ~]$ systemctl --user enable --now rhel9-unpriv-cont-systemd.service 
Created symlink /home/xf0r3m/.config/systemd/user/default.target.wants/rhel9-unpriv-cont-systemd.service → /home/xf0r3m/.config/systemd/user/rhel9-unpriv-cont-systemd.service.
[xf0r3m@primeb450 ~]$ systemctl --user status rhel9-unpriv-cont-systemd.service --no-pager
● rhel9-unpriv-cont-systemd.service - Podman container-fbe3ab11d3728470f1fcd38f07b99bd7e151ac2fc0c4a6a8dad85c486c3bce60.service
     Loaded: loaded (/home/xf0r3m/.config/systemd/user/rhel9-unpriv-cont-systemd.service; enabled; vendor preset: disabled)
     Active: active (running) since Thu 2022-08-11 15:02:52 CEST; 31s ago
       Docs: man:podman-generate-systemd(1)
    Process: 9876 ExecStartPre=/bin/rm -f /run/user/1000/rhel9-unpriv-cont-systemd.service.ctr-id (code=exited, status=0/SUCCESS)
   Main PID: 9914 (conmon)
      Tasks: 3 (limit: 48032)
     Memory: 1.4M
        CPU: 211ms
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/rhel9-unpriv-cont-systemd.service
             ├─9910 /usr/bin/slirp4netns --disable-host-loopback --mtu=65520 --enable-sandbox --enable-seccomp --enable-ipv6 -c -e 3…
             └─9914 /usr/bin/conmon --api-version 1 -c 73435f815694db611703dca5e48b52321f95849de15e6ac3cd2859f469a2d116 -u 73435f815…

[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE                                   COMMAND     CREATED         STATUS             PORTS       NAMES
73435f815694  registry.access.redhat.com/ubi9:latest  /bin/bash   42 seconds ago  Up 43 seconds ago              rhel9-unpriv-cont-systemd

Jak widzimy na przykładzie kontener został uruchomiony, teraz uruchomie go ponownie za pomocą polecenia systemctl.

[xf0r3m@primeb450 ~]$ systemctl --user restart rhel9-unpriv-cont-systemd.service 
[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE                                   COMMAND     CREATED        STATUS            PORTS       NAMES
810555dd08f1  registry.access.redhat.com/ubi9:latest  /bin/bash   3 seconds ago  Up 4 seconds ago              rhel9-unpriv-cont-systemd
[xf0r3m@primeb450 ~]$ systemctl --user restart rhel9-unpriv-cont-systemd.service && podman ps
CONTAINER ID  IMAGE                                   COMMAND     CREATED                 STATUS                     PORTS       NAMES
433ac061577e  registry.access.redhat.com/ubi9:latest  /bin/bash   Less than a second ago  Up Less than a second ago              rhel9-unpriv-cont-systemd

Różne identyfikatory kontenerów w pierwszej kolumnie, wyjścia polecenia podman ps wskazuje, że za każdym ponownym aktywowaniem jednostki, tworzony jest nowy kontener. Po wylogowaniu i zalogowaniu się ponownie, kontener nadal działa, co oznacza, że lingering jest dobrze skonfigurowany.

[xf0r3m@primeb450 ~]$ exit
logout
Connection to 192.168.8.7 closed.
[xf0r3m@latitude-e5270 ~]$ ssh 192.168.8.7
xf0r3m@192.168.8.7's password: 
Activate the web console with: systemctl enable --now cockpit.socket

Register this system with Red Hat Insights: insights-client --register
Create an account or view all your systems at https://red.ht/insights-dashboard
Last login: Thu Aug 11 08:37:43 2022 from 192.168.8.168
[xf0r3m@primeb450 ~]$ podman ps
CONTAINER ID  IMAGE                                   COMMAND     CREATED        STATUS            PORTS       NAMES
433ac061577e  registry.access.redhat.com/ubi9:latest  /bin/bash   3 minutes ago  Up 3 minutes ago              rhel9-unpriv-cont-systemd

Ćwiczenie 1: Utwórzenie laboratorium kontenerów

Na fizycznym komputerze lub maszynie wirtualne, zainstaluj system RHEL 9, zarejestruj go w sieci Red Hat podczas instalacji oraz utwórz użytkownika z uprawnieniami administratora. Po zainstalowaniu systemu zaktualizuj go i zainstaluj niezbędne oprogramowanie do obsługi kontenerów w systemie. Wykonanie tego ćwiczenia jest niezbędne do wykonania pozostałych.

Ćwiczenie 2: Nazwany kontener z mapowaniem portów

W laboratorium kontenerów, utworz uprzywilejowany kontener z mapowaniem portu 80:8080 oraz dowolną nazwą. Do tworzenia kontenera wykorzystaj obraz UBI dla RHEL 7. Wykonaj kilka poleceń takich jak: ls, pwd, df, cat /etc/redhat-release oraz cat /etc/os-release. Sprawdź mapowanie portów. Nie usuwaj jeszcze tego kontenera.

Ćwiczenie 3: Kontener bez nazwy z przekazanymi zmiennymi

W laboratorium kontenerów, na podstawie UBI dla RHEL 8 utwórz nieuprzywilejowany kontener bez nazwy z przekazaniem dwóch zmiennych VAR1="lab1" oraz VAR2="lab2". Działanie kontenera powinno polegać na wyświetleniu zmiennych środowiskowych, a kontener powinien zostać usunięty po skończeniu zadania. Na koniec usuń także obraz UBI dla RHEL 8.

Ćwiczenie 4: Kontener ze stałym miejscem przechowywania

W laboratorium kontenerów, utwórz katalog o nazwie /host_perm1 wraz z plikiem str1 przeznaczony na stałe miejsce dla danych kontenera uprzywilejowanego, następnie na podstawie UBI dla RHEL 9 utwórz kontener o nazwie priv-cont-str przekazując do niego ten katalog pod punkt montowania /cont_perm1. Przez powłokę kontenera sprawdź uprawnienia przekazanego katalogu oraz obecność pliku, następnie utwórz w nim dodatkowy podkatalog. Opuść powłokę kontenera i na hoście sprawdź obecność utworzonego podkatalogu. Na koniec usuń kontener oraz utworzony na potrzeby tego ćwiczenia katalog.

Ćwiczenie 5: Kontener z przekazywaniem portów, zmiennymi oraz stałym miejscem przechowywania

W laboratorium kontenerów, utworz katalog o nazwie /host_perm2 na stałe miejsce przechowywania dla kontenera nieuprzywilejowanego, na podstawie UBI dla RHEL 9 utwórz kontener o nazwie rootless-adv-cont w trybie interaktywym przekaż do niego dwie zmienne HISTSIZE=100 oraz MYNAME=RedHat zmapuj port hosta 9000 na port 8080 kontenera, przekaż wcześniej utworzony katalog pod punkt montowania /cont_perm2, sprawdź zmienne oraz punkt montowania, następie opuść powłokę i sprawdź czy mapowanie portów było prawidłowe. Nie usuwaj tego kontenera jeszcze.

Ćwiczenie 6: Konfigracja kontroli systemd nad kontenerami uprzywilejowanymi

Korzystając z kontenera z ćwiczenia drugiego, w laboratorium kontenerów skonfiguruj kontrolę tego kontenera przez systemd. Aby potwierdzić działanie konfiguracji zrestartuj system.

Ćwiczenie 7: Konfigracja kontroli systemd nad kontenerami nieuprzywilejowanymi

Korzystając z kontenera z ćwiczenia piątego, w laboratorum kontenerów skonfiguruj kontrolę tego kontenera przez systemd, zwróć uwagę na to iż jest to kontener nieuprzywilejowany. Aby potwierdzić działanie konfiguracji zrestartuj system.

Podsumowanie

W tym ostatnim już rozdziale zapoznaliśmy się technikami kontenerów w RHEL. Poznalismy niezbędną wiedzę teoretyczną oraz narzędzia potrzebne w praktyce. Poznaliśmy wiele przypadków praktycznej obsługi kontenerów, od przekazywania portów do przekazania kontroli nad konterami do programu typu init, jakim jest systemd zapoznaliśmy się też z konfiguracją plików jednostek użytkownika. Ten rozdział zamyka ten materiał. Jeśli chcemy więcej technologii Red Hat, możemy przystąpić kolejnego certyfikatu po zdaniu RHCSA. Powodzenia!