Przejdź do treści
Lekcja 80 z 117

Kurs › Moduł 9 › Lekcja 80

Kto się logował

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

Po tej lekcji: kursant sprawdza, kto jest zalogowany i kto się logował (who, w, last, lastb), czyta logi logowania w /var/log/auth.log i /var/log/secure, rozumie, jak PAM decyduje o wpuszczeniu, blokuje konto po nieudanych próbach przez faillock i wie, którym sposobem zamknąć konto, żeby było naprawdę zamknięte.

Wstęp

przewijający się w nieskończoność log z tysiącami linijek Failed password for invalid user admin

Stawiasz serwer z publicznym adresem. Po godzinie zaglądasz do logów. Tysiące prób logowania na konta admin, root, test, oracle, z adresów z drugiego końca świata. Nikt Cię nie zna, a już Cię odwiedzają.

Spokojnie, to normalne. Boty skanują cały internet bez przerwy. Ale admin musi umieć odpowiedzieć na trzy pytania: kto jest teraz w środku, kto był i kto się dobijał. I zamknąć drzwi przed tymi, którzy dobijają się za mocno.

Kto jest teraz: who i w

Wyobraź sobie recepcję w biurowcu. Jest tablica „kto jest w budynku” i zeszyt wejść i wyjść. Linux ma jedno i drugie.

who
kasia    pts/1        Nov  3 16:30 (127.0.0.1)

Kto, na jakim terminalu, od kiedy i skąd. pts/1 to terminal wirtualny, czyli sesja przez sieć albo okno terminala. Logowanie na fizycznej konsoli wyglądałoby jak tty1.

w
 16:30:59 up 13 min,  0 user,  load average: 4.46, 3.04, 1.31
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU  WHAT

na maszynie testowej w pokazuje tylko nagłówek, na prawdziwej VM pod nim są wiersze sesji

w to who na sterydach. Pierwsza linijka to uptime, który znasz z lekcji 26. Potem tabela: kto, skąd, od kiedy, jak długo nic nie robi (IDLE) i co ma właśnie uruchomione (WHAT). Jeśli widzisz na serwerze kogoś, kogo się nie spodziewasz, z WHAT pokazującym wget z dziwnego adresu, to masz bardzo ciekawy dzień.

Kto był: last

last -n 5
kasia    pts/1        127.0.0.1        Tue Nov  3 16:30 - 16:30  (00:00)
root     pts/4        10.0.0.20        Tue Nov  3 16:26    gone - no logout

wtmp begins Tue Nov  3 16:26:46 2026

Zeszyt wejść i wyjść. Kto, skąd, kiedy wszedł, kiedy wyszedł i jak długo był. still logged in znaczy, że jest dalej. gone - no logout znaczy, że sesja się urwała bez wylogowania, na przykład przez restart. Na dole data początku zeszytu, bo stare zeszyty są co jakiś czas archiwizowane.

last kasia          # tylko jedna osoba
last reboot         # historia restartów

last reboot to świetne narzędzie do śledztw typu „czy ten serwer w nocy się restartował, czy ktoś mi wmawia”.

Kto się dobijał: lastb

sudo lastb -n 4
root     ssh:notty    127.0.0.1        Tue Nov  3 16:44 - 16:44  (00:00)
admin    ssh:notty    127.0.0.1        Tue Nov  3 16:44 - 16:44  (00:00)
admin    ssh:notty    127.0.0.1        Tue Nov  3 16:44 - 16:44  (00:00)
oracle   ssh:notty    127.0.0.1        Tue Nov  3 16:44 - 16:44  (00:00)

b jak bad. Lista nieudanych prób. Potrzebuje sudo, bo czasem ktoś przez pomyłkę wpisuje hasło w pole loginu, a takiej listy nie powinien czytać każdy.

I jeszcze ostatnie logowanie każdego konta:

lastlog -u kasia
Username         Port     From                                       Latest
kasia            pts/1    127.0.0.1                                 Tue Nov  3 16:30:57 +0100 2026

Przydaje się przy porządkach: lastlog bez argumentów pokaże konta, które nie logowały się nigdy albo od roku. Kandydaci do zamknięcia.

Dziennik portiera: auth.log i secure

last to zeszyt w skrócie. Pełny dziennik portiera jest w logach:

[Ubuntu] /var/log/auth.log [RHEL] /var/log/secure

A na obu w dzienniku systemd: journalctl -u ssh na Ubuntu, journalctl -u sshd na RHEL. Tak, usługa ma inną nazwę. Nie pytaj.

Trzy rodzaje linijek, które musisz rozpoznawać:

Failed password for kasia from 127.0.0.1 port 48898 ssh2
Invalid user admin from 127.0.0.1 port 48908
Failed password for invalid user admin from 127.0.0.1 port 48908 ssh2
Accepted password for kasia from 127.0.0.1 port 48914 ssh2

Failed password for kasia znaczy: konto istnieje, hasło złe. Invalid user admin znaczy: takiego konta w ogóle nie ma, ktoś zgaduje w ciemno. Accepted password albo Accepted publickey to udane wejście. Jeśli widzisz Accepted z adresu, którego nie znasz, odłóż kawę.

Kto się dobija najczęściej? Rura i awk z lekcji 7 i 47:

sudo awk '/Failed password/ {print $(NF-5)}' /var/log/auth.log | sort | uniq -c | sort -rn
      5 admin
      2 root
      1 test
      1 oracle
      1 kasia

$(NF-5) to szóste pole od końca linijki, czyli login. Liczenie od końca działa niezależnie od tego, czy w środku jest „invalid user”, czy nie. Zamień na $(NF-3) i dostaniesz ranking adresów IP. U mnie wszystkie próby szły z jednego adresu, bo atakowałem sam siebie. Na prawdziwym serwerze zobaczysz pół internetu.

PAM: bramki przy wejściu

Kto właściwie decyduje, czy wpuścić? Nie sshd, nie login, nie sudo. Wszystkie pytają PAM, czyli Pluggable Authentication Modules, wymienne moduły uwierzytelniania.

Pomyśl o lotnisku. Najpierw kontrola dokumentów, potem bramka bezpieczeństwa, potem sprawdzenie karty pokładowej, a na końcu ktoś zakłada Ci opaskę. Każde stanowisko to osobny moduł, a lotnisko może dołożyć nowe stanowisko bez przebudowy terminalu.

Każdy program ma swój plik w /etc/pam.d/:

grep -vE '^#|^$' /etc/pam.d/sshd | head -5
@include common-auth
account    required     pam_nologin.so
@include common-account
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so close
session    required     pam_loginuid.so

Pierwsza kolumna to rodzaj stanowiska:

  • auth: kim jesteś? Sprawdzenie hasła, klucza, karty.
  • account: czy wolno Ci teraz wejść? Konto nie wygasło, nie jest zablokowane, nie ma zakazu wejścia.
  • password: zmiana hasła i reguły jego jakości.
  • session: co zrobić przy wejściu i wyjściu. Założyć folder, ustawić limity, zapisać w dzienniku.

Druga kolumna to, jak bardzo dane stanowisko się liczy:

  • required: musi przepuścić, ale sprawdzamy dalej, żeby intruz nie wiedział, na której bramce odpadł.
  • requisite: musi przepuścić, a jak nie, koniec od razu.
  • sufficient: jeśli przepuści, to wystarczy, dalej nie sprawdzamy.
  • optional: liczy się tylko, jeśli nie ma nic innego.

Ten pam_nologin.so z drugiej linijki to ciekawostka admina: jeśli istnieje plik /etc/nologin, zwykli użytkownicy nie wejdą, a zobaczą jego treść. Root wejdzie. Tabliczka „zamknięte z powodu remanentu”.

[Ubuntu] Wspólne ustawienia są w /etc/pam.d/common-auth, common-account, common-password i common-session, dołączanych przez @include. Zmienia się je przez pam-auth-update.

[RHEL] Wspólne ustawienia są w /etc/pam.d/system-auth i password-auth, a te pliki generuje authselect. Na górze jest napisane wprost, żeby nie edytować ręcznie, bo zmiany zostaną nadpisane. O authselect więcej w następnej lekcji.

Ostrzeżenie: zepsuty PAM potrafi zamknąć przed Tobą wszystkie drzwi naraz, również sudo. Zmiany w /etc/pam.d rób na maszynie wirtualnej, z otwartą drugą sesją roota, i testuj nowe logowanie w trzecim oknie, zanim zamkniesz te dwa.

faillock: trzy złe hasła i drzwi zamknięte

Bankomat po trzech złych PIN-ach zatrzymuje kartę. pam_faillock robi to samo z kontem.

Żeby nie psuć prawdziwego logowania, zrobiłem osobną usługę PAM tylko do testów i sprawdzam ją narzędziem pamtester, które udaje program logujący:

sudo tee /etc/pam.d/proba <<'EOF'
auth     required                    pam_faillock.so preauth
auth     [success=1 default=bad]     pam_unix.so
auth     [default=die]               pam_faillock.so authfail
auth     sufficient                  pam_faillock.so authsucc
auth     required                    pam_deny.so
account  required                    pam_faillock.so
account  required                    pam_unix.so
EOF

Kolejność czytamy tak: najpierw preauth sprawdza, czy konto już jest zablokowane. Potem pam_unix sprawdza hasło. Dobre hasło przeskakuje jedną linijkę, złe idzie do authfail, który zapisuje porażkę i kończy. authsucc przy sukcesie czyści licznik.

Trzy złe hasła:

for h in zle1 zle2 zle3; do echo $h | sudo pamtester proba kasia authenticate; done
sudo faillock --user kasia
pamtester: Authentication failure
pamtester: Authentication failure
pamtester: Authentication failure
kasia:
When                Type  Source                                           Valid
2026-11-03 16:30:23 SVC   proba                                                V
2026-11-03 16:30:25 SVC   proba                                                V
2026-11-03 16:30:26 SVC   proba                                                V

A teraz dobre hasło:

pamtester: Authentication failure
The account is locked due to 3 failed logins.
(10 minutes left to unlock)

Dobre hasło, a i tak odmowa. O to chodzi. Złodziej, który za czwartym razem trafi, nie dowie się, że trafił. Odblokowanie ręczne:

sudo faillock --user kasia --reset
pamtester: successfully authenticated

Ustawienia są w /etc/security/faillock.conf. Domyślne wartości stoją tam w komentarzach: deny = 3, czyli trzy próby, fail_interval = 900, czyli w ciągu 15 minut, unlock_time = 600, czyli blokada na 10 minut. Root domyślnie nie jest blokowany, bo zablokowany root to zamknięty serwer. Włącza to opcja even_deny_root, tylko jeśli wiesz, co robisz.

Jak włączyć faillock naprawdę:

sudo authselect enable-feature with-faillock     # RHEL

[RHEL] Jedna komenda, authselect sam dopisze moduł w dobre miejsca.

[Ubuntu] Moduł jest w systemie, ale domyślnie nie jest włączony. Trzeba dopisać linijki do /etc/pam.d/common-auth i common-account według przykładu z man pam_faillock. Na maszynie wirtualnej, z drugą sesją roota otwartą.

Jak zamknąć konto naprawdę

Pracownik odchodzi, ale jego pliki trzeba jeszcze przejrzeć, więc konta nie usuwasz od razu. Blokujesz. Tylko jak? Sposobów jest kilka i działają na różne drzwi.

Blokada hasła: passwd -l albo usermod -L. Dopisuje wykrzyknik przed skrótem hasła w /etc/shadow:

sudo passwd -l kasia
sudo getent shadow kasia | cut -c1-12
passwd: password changed.
kasia:!$y$j9T

Hasło przestaje pasować do czegokolwiek. Ale to blokuje tylko hasło. Za chwilę w typowym błędzie zobaczysz, co to znaczy.

Wygaśnięcie konta: usermod --expiredate 1 albo chage -E 0. Data wygaśnięcia ustawiona na początek 1970 roku. Konto jest martwe dla każdej metody logowania, bo sprawdza to etap account w PAM:

sudo usermod --expiredate 1 kasia
ssh kasia@serwer
Your account has expired; please contact your system administrator.
Connection closed by 127.0.0.1 port 2278

Powłoka bez wejścia: usermod -s /usr/sbin/nologin. Zamiast powłoki program, który grzecznie odmawia:

This account is currently not available.

Ale uwaga: to blokuje powłokę, a nie na przykład przesyłanie plików czy tunele SSH. Dobre dla kont usług, za słabe jako jedyna blokada człowieka.

Wszystkie naraz. Przy odejściu pracownika: wygaś konto, zablokuj hasło, zabij jego procesy (pkill -u login), sprawdź crontab -l -u login, zabierz klucze z ~/.ssh/authorized_keys, usuń jego pliki z /etc/sudoers.d. Przy usunięciu po miesiącu find -nouser z lekcji 78.

Jak to działa pod spodem

Lista „kto jest teraz” to binarny plik /run/utmp. Leży w pamięci i znika przy restarcie, jak tablica na recepcji, którą się co rano wyciera. Zeszyt wejść to /var/log/wtmp, nieudane próby /var/log/btmp. Nie są tekstowe, dlatego nie da się ich przeczytać cat. Pisze do nich program, który Cię wpuszcza, czyli sshd, login albo moduł PAM.

Kiedy łączysz się przez SSH, sshd przekazuje Twój login do PAM. PAM przechodzi przez stos auth z /etc/pam.d/sshd moduł po module, potem account, potem session. Każdy moduł to biblioteka .so, którą PAM ładuje i pyta: „i jak?”. Moduł odpowiada sukcesem, porażką albo „nie wiem”, a druga kolumna mówi, co z tą odpowiedzią zrobić.

Logowanie kluczem SSH to ważny wyjątek: klucz sprawdza sam sshd, a PAM nie odpala wtedy etapu auth. Etapy account i session idą normalnie. Stąd cała różnica między blokadą hasła a wygaśnięciem konta.

Logi tekstowe pisze z kolei sshd i spółka do dziennika systemu. Na Ubuntu demon rsyslog rozdziela te wpisy do /var/log/auth.log, na RHEL do /var/log/secure. Oba czytają z kanału authpriv, a journalctl widzi to samo z drugiej strony.

Ściąga flag

KomendaFlagaCo robi
who(bez flag)kto jest teraz zalogowany
w-hkto jest i co robi (bez nagłówka)
last-n 10ostatnie 10 logowań
lastlogin / rebootlogowania jednej osoby / restarty
lastb-n 10ostatnie nieudane próby (sudo)
lastlog-u loginostatnie logowanie konta
journalctl-u ssh / -u sshdlogi SSH (Ubuntu / RHEL)
journalctl_COMM=sudowpisy z sudo
faillock--user loginliczba nieudanych prób
faillock--user login --resetodblokowuje konto
authselectenable-feature with-faillockwłącza faillock (RHEL)
passwd-l / -ublokuje / odblokowuje hasło
usermod-L / -Uto samo co wyżej
usermod--expiredate 1 / ""wygasza konto / przywraca
chage-E 0wygasza konto
usermod-s /usr/sbin/nologinpowłoka bez wejścia
pkill-u loginkończy procesy użytkownika

⚠ Typowy błąd

Pracownik odszedł. Admin robi passwd -l i idzie do domu spokojny. Sprawdźmy:

sudo passwd -S kasia
ssh -i klucz kasia@serwer "echo weszlem mimo blokady hasla"
kasia L 2026-11-03 0 99999 7 -1
weszlem mimo blokady hasla

Status L, zablokowane. A logowanie kluczem przechodzi, jakby nigdy nic. Dokumentacja passwd mówi to wprost: blokada hasła nie wyłącza konta, użytkownik może wejść innym sposobem, na przykład kluczem SSH. Zamknąłeś drzwi wejściowe na klucz, a on ma pilota do garażu.

Naprawa:

sudo usermod --expiredate 1 kasia
Your account has expired; please contact your system administrator.

Wygaśnięcie konta sprawdzane jest na etapie account, przez który przechodzi każde logowanie, kluczem czy hasłem. Do tego zabierz klucze z ~/.ssh/authorized_keys i zakończ jego działające sesje. Otwarta sesja SSH sprzed blokady dalej sobie żyje, bo blokada działa przy wejściu, a nie wyrzuca ludzi, którzy już są w środku.

✎ Zadanie

  1. Na maszynie wirtualnej załóż konto testowe i trzy razy zaloguj się na nie przez SSH ze złym hasłem, a raz z dobrym. Znajdź te próby w lastb, w last i w logu (auth.log albo secure).
  2. Jedną rurą wypisz ranking loginów z nieudanych prób z całego logu.
  3. Zablokuj konto tak, żeby nie dało się wejść ani hasłem, ani kluczem. Sprawdź chage -l, a potem przywróć konto do życia.
Pokaż rozwiązanie

Trzy złe hasła i jedno dobre, z drugiej maszyny albo z tej samej:

ssh kasia@serwer     # 3 razy złe hasło, raz dobre
sudo lastb -n 3
last -n 1 kasia
sudo grep -E 'Failed|Accepted' /var/log/auth.log | tail -4     # na RHEL /var/log/secure
Failed password for kasia from 127.0.0.1 port 48898 ssh2
...
Accepted password for kasia from 127.0.0.1 port 48914 ssh2

Ranking loginów jedną rurą:

sudo awk '/Failed password/ {print $(NF-5)}' /var/log/auth.log | sort | uniq -c | sort -rn

Blokada, która trzyma i hasło, i klucz:

sudo usermod --expiredate 1 kasia
sudo passwd -l kasia
sudo chage -l kasia | grep 'Account expires'
Account expires                                         : Jan 02, 1970

Konto wygasło w drugim dniu epoki uniksowej. Nawet Elvis był wtedy jeszcze na trasie. A przywrócenie:

sudo usermod --expiredate "" kasia
sudo passwd -u kasia
sudo chage -l kasia | grep 'Account expires'
Account expires                                         : never

Pusty cudzysłów znaczy „bez daty wygaśnięcia”. Pamiętaj o obu krokach. Odblokowane hasło przy wygasłym koncie to jak zdjęty łańcuch z drzwi, które dalej są zamurowane.

Co dalej

Na jednym serwerze konta zakłada się ręcznie. Ale co, jeśli masz pięćdziesiąt serwerów i trzystu pracowników? W następnej lekcji konta z jednego miejsca: LDAP i Active Directory, SSSD, realm join i authselect. Jeden pracownik, jedno hasło, wszystkie serwery.