Przejdź do treści
Lekcja 79 z 117

Kurs › Moduł 9 › Lekcja 79

sudo i root

⏱ ok. 11 min czytania· moduł 9: Użytkownicy i bezpieczeństwo

Po tej lekcji: kursant rozumie różnicę między rootem, su i sudo, sprawdza swoje uprawnienia przez sudo -l, bezpiecznie dodaje reguły w /etc/sudoers.d przez visudo i daje ludziom tylko te komendy, których naprawdę potrzebują.

Wstęp

Każdy admin dostał kiedyś taką wiadomość. „Tylko zrestartować”. A po root przychodzi się z całą resztą: można czytać hasła, kasować bazy, zmienić konfigurację, a potem nikt nie wie, kto to zrobił.

Dziś nauczysz się dawać dokładnie to, o co ktoś prosi. Ani komendy więcej. I sprawdzać, co kto naprawdę może.

Root, czyli dyrektor z kluczem do wszystkiego

Pamiętasz klucze do pokojów z lekcji o uprawnieniach? Root to dyrektor z pękiem kluczy do wszystkich drzwi w budynku. Jądro sprawdza uprawnienia przez porównanie numerów, a jeśli Twój UID to 0, większość tych sprawdzeń po prostu się pomija. Root nie dostaje „odmowa dostępu” przy cudzych plikach. Może skasować cokolwiek, również system, na którym stoi.

Dlatego na roocie się nie pracuje. Pracuje się na zwykłym koncie, a po klucz dyrektora sięga się na chwilę, do jednej czynności. Jak w banku: kasjer nie nosi przy sobie klucza do skarbca, tylko prosi o niego, a wydanie klucza jest zapisywane.

[Ubuntu] Na Ubuntu konto root ma domyślnie zablokowane hasło:

sudo passwd -S root
root L 2026-10-24 0 99999 7 -1

L jak locked. Hasłem na roota się nie zalogujesz. Do władzy prowadzi tylko sudo. [RHEL] Na RHEL instalator pozwala ustawić hasło roota albo zostawić konto roota wyłączone, a Twoje konto zrobić administratorem. W praktyce na serwerach i tak pracujesz przez sudo.

su: przebieranka

su to „substitute user”, zamień użytkownika. Wpisujesz su - i podajesz hasło roota. Nie swoje. Jeśli root nie ma hasła, jak na Ubuntu, to:

Password:
su: Authentication failure

Różnica między su a su - jest ważna. Zobacz, jak to wygląda dla przejścia na inne konto:

cd /tmp
su kasia -c 'echo "pwd=$(pwd) HOME=$HOME"'
su - kasia -c 'echo "pwd=$(pwd) HOME=$HOME"'
pwd=/tmp HOME=/home/kasia
pwd=/home/kasia HOME=/home/kasia

su bez minusa to przebranie się w cudzy mundur, ale zostajesz w swoim pokoju, z częścią swoich rzeczy w kieszeniach, czyli zmiennych środowiskowych. su - to pełne zalogowanie: idziesz do pokoju tej osoby, z jej ustawieniami, z jej PATH. Z rootem tak samo: su bez minusa potrafi zostawić Ci PATH zwykłego użytkownika i wtedy komendy z /usr/sbin udają, że ich nie ma. Zasada: jeśli już su, to z minusem.

Problem z su jest inny. Żeby z niego korzystać, wszyscy admini muszą znać jedno wspólne hasło roota. Pięć osób, jedno hasło. Ktoś odchodzi z firmy, trzeba zmieniać hasło i rozsyłać nowe. A w logach widać tylko „root coś zrobił”. Który root? Wszyscy jesteśmy rootem.

sudo: przepustka na jedną czynność

sudo działa inaczej. Podajesz swoje hasło. sudo sprawdza w regulaminie, czyli w pliku /etc/sudoers, czy Tobie wolno tę konkretną komendę, i jeśli tak, uruchamia ją jako root. I zapisuje w dzienniku: kto, kiedy, co.

sudo whoami
root

Każdy admin ma własne hasło, zabiera mu się dostęp przez wypisanie z grupy, a w logach widać nazwisko. Jak karta dostępu zamiast jednego klucza pod wycieraczką.

Po podaniu hasła sudo przez chwilę go nie pyta ponownie. W samym sudo domyślnie jest to 5 minut, Ubuntu ma ustawione 15. Swoje ustawienia zobaczysz, wpisując jako root sudo -V. Chcesz od razu oddać przepustkę? sudo -k.

Kto może używać sudo? Członkowie grupy admińskiej:

getent group sudo     # Ubuntu
getent group wheel    # RHEL
sudo:x:27:ubuntu

[Ubuntu] Grupa sudo. [RHEL] Grupa wheel. Dopisanie do niej to usermod -aG. Z -a, pamiętasz.

Powłoka roota: sudo -i, sudo -s

Czasem masz dziesięć komend z rzędu do zrobienia jako root i pisanie sudo przed każdą męczy. Wtedy:

sudo -i     # pełne logowanie jako root, jak su -
sudo -s     # powłoka roota, ale zostajesz w swoim folderze
cd /tmp
sudo -i pwd
sudo -s pwd
/root
/tmp

sudo -i to odpowiednik su -, tylko z Twoim hasłem zamiast hasła roota. I już wiesz, czemu sudo su - to dwa autobusy. sudo robi Cię rootem, a potem root robi su - na roota. Ten sam efekt daje jedno sudo -i.

Ostrzeżenie: kiedy już siedzisz w powłoce roota, każda literówka ma pełną moc. Znak zachęty zmienia się z $ na #. Ten krzyżyk to nie hashtag, to tabliczka „uwaga, ostre narzędzia”. Zrób, co miałeś zrobić, i wyjdź exit.

Co mi wolno: sudo -l

sudo -l

Pokazuje Twoje reguły. Admin może sprawdzić kogoś innego:

sudo -l -U kasia
User kasia may run the following commands on vm:
    (root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx

A konkretną komendę sprawdzisz bez uruchamiania jej. Kod wyjścia 0 znaczy „wolno”, 1 znaczy „nie wolno”:

sudo -l systemctl restart nginx; echo $?
sudo -l systemctl stop nginx; echo $?
/usr/bin/systemctl restart nginx
0
1

Kciuk w górę, kciuk w dół. Przydatne w skryptach.

Regulamin: /etc/sudoers i visudo

sudo grep -vE '^#|^$' /etc/sudoers
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
Defaults        use_pty
root    ALL=(ALL:ALL) ALL
%sudo   ALL=(ALL:ALL) ALL
@includedir /etc/sudoers.d

Jedna reguła czyta się tak:

kto   na_jakim_serwerze=(jako_kto:jako_jaka_grupa)   jakie_komendy
%sudo ALL=(ALL:ALL) ALL

„Członkowie grupy sudo (procent znaczy grupę), na każdym serwerze, jako dowolny użytkownik i grupa, mogą wszystko”. Pole serwera istnieje, bo jeden plik sudoers można rozesłać na sto maszyn. Na jednej maszynie piszesz tam ALL.

Defaults to ustawienia. env_reset czyści zmienne środowiskowe, żeby nikt nie przemycił czegoś przez nie do roota. secure_path ustala PATH dla komend przez sudo, żeby nikt nie podłożył fałszywego systemctl w swoim folderze.

Ostatnia linijka jest najważniejsza dla Ciebie: @includedir /etc/sudoers.d. Głównego pliku nie ruszasz. Swoje reguły kładziesz jako osobne pliki w /etc/sudoers.d. Aktualizacja systemu może podmienić główny plik, a Twoje pliki zostaną. I łatwo je usunąć, kiedy ktoś odchodzi.

A edytujesz je tylko przez visudo:

sudo visudo -f /etc/sudoers.d/kasia

Czemu nie przez nano? Bo visudo przed zapisaniem sprawdza składnię. Zobacz, co się dzieje, kiedy zrobię literówkę, przecinek na końcu linijki:

/etc/sudoers.d/kasia:5:51: syntax error
kasia ALL=(root) /usr/bin/systemctl restart nginx,
                                                  ^
What now? Options are:
  (e)dit sudoers file again
  e(x)it without saving changes to sudoers file
  (Q)uit and save changes to sudoers file (DANGER!)

e wraca do edycji. x wychodzi bez zapisu. Q zapisuje mimo błędu i ma dopisek DANGER nie bez powodu. Zepsuty sudoers potrafi odciąć dostęp wszystkim adminom naraz. Nowsze wersje sudo próbują pominąć zepsuty fragment i działać dalej, ale nie testuj tego na produkcji. Plik sprawdzisz też bez edycji:

sudo visudo -c
sudo visudo -cf plik_do_sprawdzenia
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
/etc/sudoers.d/kasia: parsed OK

Bezpieczeństwo: zanim zaczniesz grzebać w sudo na zdalnym serwerze, otwórz drugą sesję SSH i zrób w niej sudo -i. Jeśli coś zepsujesz, masz otwarte drzwi, żeby to naprawić. Najlepiej ćwicz na maszynie wirtualnej.

Zasada najmniejszych uprawnień

Wracamy do programisty, który „musi tylko zrestartować nginxa”. Zasada najmniejszych uprawnień mówi: daj dokładnie tyle, ile trzeba do pracy, i nic więcej. Kurier dostaje kod do bramy, a nie klucze do mieszkania.

sudo visudo -f /etc/sudoers.d/kasia
Cmnd_Alias WWW = /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
kasia ALL=(root) NOPASSWD: WWW

Cmnd_Alias to nazwana lista komend, taka paczka, którą można dać wielu osobom. NOPASSWD: znaczy „bez pytania o hasło”. Dawaj go tylko przy wąskich, konkretnych komendach, na przykład dla skryptów z automatu. Nigdy przy ALL.

Zawsze pełne ścieżki do programów, /usr/bin/systemctl, a nie systemctl. Sprawdzisz je przez command -v systemctl.

Test:

sudo systemctl stop nginx
sudo bash
Sorry, user kasia is not allowed to execute '/usr/bin/systemctl stop nginx' as root on vm.
Sorry, user kasia is not allowed to execute '/usr/bin/bash' as root on vm.

Restart tak, zatrzymanie nie, powłoka roota nie. Dokładnie o to chodziło.

Dziury w płocie

Najmniejsze uprawnienia łatwo zepsuć. Trzy klasyczne dziury.

Programy, które umieją uruchomić inne programy. Dajesz komuś przez sudo „tylko” podgląd pliku przez less. A less ma komendę !, która uruchamia powłokę. Sprawdziłem:

kasia ALL=(root) NOPASSWD: /usr/bin/less /etc/hostname

Kasia wpisuje w less znak wykrzyknika i id:

uid=0(root)

Kasia jest rootem. To samo zrobią vim, find -exec, awk, tar z odpowiednimi opcjami i masa innych. Dałeś lupę, a w rączce lupy był schowany wytrych. Na to jest znacznik NOEXEC:, który blokuje uruchamianie kolejnych programów:

kasia ALL=(root) NOPASSWD: NOEXEC: /usr/bin/less /etc/hostname
!done  (press RETURN)

Tym razem less próbuje, ale nic się nie uruchamia. A do edycji plików konfiguracyjnych jest osobne, bezpieczne narzędzie:

kasia ALL=(root) sudoedit /etc/nginx/nginx.conf

sudoedit kopiuje plik do tymczasowego, Kasia edytuje kopię swoim edytorem jako ona sama, a sudo podmienia oryginał przy zapisie. Edytor nigdy nie działa jako root, więc nie ma przez co uciec.

Gwiazdki. kasia ALL=(root) /usr/bin/systemctl restart * wygląda niewinnie. Ale gwiazdka pasuje też do spacji i kolejnych argumentów. Dokumentacja sudo wprost ostrzega, żeby z gwiazdkami w argumentach uważać. Wypisuj komendy w całości.

Skrypty, które użytkownik może edytować. Pozwalasz na sudo /home/kasia/restart.sh? Kasia zmieni treść skryptu i ma roota. Skrypty uruchamiane przez sudo muszą należeć do roota i leżeć tam, gdzie zwykły użytkownik nie pisze.

Jak to działa pod spodem

sudo to zwykły plik, /usr/bin/sudo, z ustawionym bitem SUID i właścicielem root. Pamiętasz uprawnienia specjalne z lekcji 16? Bit SUID sprawia, że program działa z prawami właściciela pliku, a nie tego, kto go uruchomił. Więc sudo startuje jako root, nawet gdy uruchamia go Kasia.

Potem sudo przez PAM sprawdza hasło Kasi, czyta /etc/sudoers i pliki z /etc/sudoers.d, szuka pasującej reguły, czyści środowisko, zapisuje wpis do dziennika i dopiero wtedy uruchamia komendę jako root. Kiedy reguł pasuje kilka, wygrywa ostatnia. Dlatego kolejność ma znaczenie.

Logi: [Ubuntu] w /var/log/auth.log, [RHEL] w /var/log/secure, a na obu w dzienniku systemd, na przykład journalctl _COMM=sudo. Każde sudo zostawia tam ślad z nazwą użytkownika, katalogiem i pełną komendą. Na to spojrzymy w następnej lekcji.

Ściąga flag

KomendaFlagaCo robi
sudokomendauruchamia komendę jako root
sudo-u userjako inny użytkownik, np. sudo -u postgres psql
sudo-ipowłoka logowania roota (jak su -)
sudo-spowłoka roota w obecnym folderze
sudo-lco mi wolno
sudo-l -U userco wolno komuś (dla admina)
sudo-l komendaczy wolno tę komendę (kod 0/1)
sudo-kzapomnij hasło od razu
sudo-e plik / sudoeditbezpieczna edycja pliku
su-pełne logowanie jako inny użytkownik
su-c "komenda"jedna komenda jako inny użytkownik
visudo(bez flag)edycja /etc/sudoers ze sprawdzeniem
visudo-f plikedycja pliku z /etc/sudoers.d
visudo-c / -cf pliktylko sprawdzenie składni
sudoersNOPASSWD:bez pytania o hasło
sudoersNOEXEC:program nie uruchomi kolejnych programów
sudoersCmnd_Aliasnazwana lista komend
sudoers%grupareguła dla grupy

⚠ Typowy błąd

Robisz wszystko porządnie. Plik reguły, uprawnienia 0440, visudo -c mówi OK. A Kasia dalej:

sudo install -m 0440 kasia /etc/sudoers.d/kasia.conf
sudo visudo -c
sudo -l
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
Sorry, user kasia may not run sudo on vm.

Przyjrzyj się wynikowi visudo -c. Pliku kasia.conf w ogóle nie ma na liście. Nie dlatego, że jest zły. Dlatego, że sudo go pominęło. Zasada z dokumentacji: pliki w /etc/sudoers.d, które mają w nazwie kropkę albo kończą się tyldą, są ignorowane. Po to, żeby kopie zapasowe edytorów i pliki .dpkg-old nie stawały się nagle regułami.

Naprawa:

sudo mv /etc/sudoers.d/kasia.conf /etc/sudoers.d/kasia
sudo visudo -c
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK
/etc/sudoers.d/kasia: parsed OK

Nazwy bez kropek: kasia, zespol-www, backup_nocny. A druga lekcja: kiedy coś „nie działa, choć jest poprawne”, sprawdź, czy system w ogóle to coś czyta.

✎ Zadanie

  1. Sprawdź sudo -l, co sam możesz, i znajdź w logach (/var/log/auth.log albo /var/log/secure, ewentualnie journalctl _COMM=sudo) swoje ostatnie użycie sudo.
  2. Załóż użytkownika operator1. Przez visudo -f /etc/sudoers.d/operator1 pozwól mu bez hasła wyłącznie na /usr/bin/systemctl status ssh (na RHEL sshd) i na sudoedit /etc/motd.
  3. Sprawdź sudo -l -U operator1. Zaloguj się jako on i sprawdź, że status działa, a sudo systemctl stop ssh i sudo -i są odrzucone. Na koniec usuń konto i jego plik z /etc/sudoers.d.
Pokaż rozwiązanie
sudo -l
sudo grep sudo /var/log/auth.log | tail -3     # Ubuntu
sudo grep sudo /var/log/secure | tail -3       # RHEL
sudo journalctl _COMM=sudo -n 3                # wszędzie z systemd

Teraz operator1:

sudo useradd -m -s /bin/bash operator1
sudo passwd operator1
sudo visudo -f /etc/sudoers.d/operator1
operator1 ALL=(root) NOPASSWD: /usr/bin/systemctl status ssh, sudoedit /etc/motd
sudo -l -U operator1
User operator1 may run the following commands on vm:
    (root) NOPASSWD: /usr/bin/systemctl status ssh, sudoedit /etc/motd

Logujemy się jako on, su - operator1, i sprawdzamy. sudo systemctl status ssh pokazuje stan usługi. sudoedit /etc/motd otwiera edytor. Zobacz, co ciekawego pokazuje mały testowy edytor, który podstawiłem, żeby wypisał, kim jest i co edytuje:

edytuje jako operator1: /var/tmp/motd.2K2WFFJ3

Edytor działa jako operator1, nie jako root, i dostaje kopię w /var/tmp. Po zapisie sudo podmienia oryginał:

-rw-r--r-- 1 root root 33 Nov  3 16:43 /etc/motd

Plik dalej należy do roota. Dokładnie tak, jak obiecywałem w lekcji 79. A sudo systemctl stop ssh i sudo -i kończą się odmową, bo ich na liście nie ma. Na koniec sprzątanie: sudo userdel -r operator1 i sudo rm /etc/sudoers.d/operator1. Plik w sudoers.d po usuniętym koncie to jak tabliczka z nazwiskiem na drzwiach kogoś, kto dawno odszedł. Niby nic, ale jak przyjdzie nowy operator1, to tabliczka nagle zacznie działać.

Co dalej

Wiesz już, kto może co. W następnej lekcji sprawdzamy, kto był. who, w, last i lastb, czyli lista obecności i lista nieudanych prób, czytanie logów logowania, PAM, czyli bramki przy wejściu, i faillock, który zamyka drzwi po trzech złych hasłach.