Przejdź do treści
Lekcja 108 z 117

Kurs › Moduł 12 › Lekcja 108

Pytania rekrutacyjne: sieć i bezpieczeństwo

⏱ ok. 18 min czytania· moduł 12: Rozmowa rekrutacyjna

Po tej lekcji: kursant odpowiada na głos na trzynaście pytań o DNS, TCP i UDP, porty, zaporę, SSH, utwardzanie serwera, SELinux i AppArmor oraz limity, i przy każdym wie, jaką komendą by to sprawdził.

Wstęp

rekruter odwraca laptopa, na ekranie Permission denied (publickey)

„Kolega skopiował klucz SSH na serwer, a ten dalej go nie wpuszcza. Co sprawdzasz?”

To jedno z moich ulubionych pytań, bo nie ma w nim nic do wykucia. Albo kiedyś to naprawiałeś, albo nie. Dziś zrobimy tak, żebyś naprawiał. Postawię testowy serwer SSH, zepsuję go i pokażę, gdzie szukać.

A zanim do tego dojdziemy: sieć. Bo w połowie rozmów pierwsze pytanie sieciowe brzmi niewinnie: „co się dzieje, kiedy wpisujesz adres strony w przeglądarce?”. A potem rekruter drąży, aż trafi na coś, czego nie wiesz. Nie da się wiedzieć wszystkiego. Da się wiedzieć, jak szukać.

Pytanie 1: „Jak Linux zamienia nazwę na adres IP?”

Odpowiedź na głos: Program pyta bibliotekę systemową, a ona patrzy do /etc/nsswitch.conf, w linijkę hosts. Zwykle jest tam „najpierw files, potem dns”. files to plik /etc/hosts, czyli lokalna książka telefoniczna, która wygrywa ze wszystkim. Jeśli tam nazwy nie ma, idzie pytanie do serwera DNS z /etc/resolv.conf. Na Ubuntu w resolv.conf stoi zwykle lokalny pośrednik 127.0.0.53, czyli systemd-resolved, a prawdziwe serwery DNS pokazuje resolvectl status. Na RHEL plik zwykle uzupełnia NetworkManager. Sprawdzam przez getent hosts, bo idzie tą samą drogą co programy, a dig pyta DNS bezpośrednio, z pominięciem /etc/hosts.

grep '^hosts' /etc/nsswitch.conf
grep -v '^#' /etc/resolv.conf
getent hosts localhost
hosts:          files dns
nameserver 8.8.8.8
nameserver 8.8.4.4
options timeout:2 attempts:3
127.0.0.1       localhost

[Ubuntu] resolvectl status pokazuje, z jakich serwerów DNS korzysta systemd-resolved. [RHEL] nmcli dev show | grep DNS.

Czego rekruter naprawdę szuka: nsswitch.conf i różnicy między getent a dig. Jeśli getent zwraca adres, a dig nie, to ktoś wpisał nazwę do /etc/hosts. I zapomniał. Trzy lata temu.

Zła odpowiedź: „Pyta Google'a.” Czasem tak, ale tylko jeśli ktoś wpisał 8.8.8.8 do konfiguracji. Linux sam z siebie nie ma ulubionej wyszukiwarki.

Pytanie 2: „Jak działa DNS? Jakie znasz rekordy?”

Odpowiedź na głos: DNS to rozproszona książka telefoniczna internetu. Mój komputer pyta serwer rekurencyjny, na przykład u dostawcy internetu. Ten, jeśli nie ma odpowiedzi w pamięci podręcznej, pyta serwery główne, czyli root: „kto obsługuje .pl?”. Potem pyta serwery .pl: „kto obsługuje firma.pl?”. Na końcu pyta serwer autorytatywny tej domeny o konkretną nazwę i zapamiętuje odpowiedź na czas TTL. Rekordy: A to adres IPv4, AAAA to IPv6, CNAME to przezwisko wskazujące inną nazwę, MX to serwer poczty, PTR to odwrotne zapytanie, z IP na nazwę, NS wskazuje serwery domeny, TXT to notatki, na przykład SPF dla poczty.

dig firma.pl A
dig +short firma.pl MX
dig -x 1.1.1.1
dig +trace firma.pl

Na ekranie patrz na trzy rzeczy: status: (NOERROR to dobrze, NXDOMAIN to „nie ma takiej nazwy”, SERVFAIL to „serwer się zepsuł”), sekcję ANSWER i linijkę SERVER, czyli kogo zapytałeś. +trace pokazuje całą drogę od serwerów głównych w dół.

Czego rekruter naprawdę szuka: pamięci podręcznej i TTL. Klasyczne dopytanie: „zmieniłem rekord, a połowa ludzi dalej trafia na stary serwer, czemu?”. Bo stary rekord żyje w cache'ach do końca swojego TTL.

Zła odpowiedź: „DNS to serwer, który ma wszystkie strony internetu.” Gdyby taki istniał, dysk by mu się skończył szybciej niż i-węzły z lekcji 106.

Pytanie 3: „Czym się różni TCP od UDP?”

Odpowiedź na głos: TCP to list polecony: najpierw nawiązanie połączenia, trójstronny uścisk dłoni, potem każdy kawałek danych jest potwierdzany, zgubione są wysyłane ponownie i wszystko dochodzi w dobrej kolejności. Bezpiecznie, ale z narzutem. UDP to pocztówka wrzucona do skrzynki: bez połączenia, bez potwierdzeń, bez gwarancji kolejności. Szybko i lekko. TCP wybierasz, gdy liczy się każdy bajt: WWW, SSH, bazy danych, poczta. UDP, gdy liczy się czas, a zguba pojedynczego pakietu nie boli: DNS, rozmowy głosowe, gry, synchronizacja czasu NTP, a dziś też HTTP/3.

ss -tln
ss -uln
grep -wE '^(ssh|domain|http|https|ntp)' /etc/services
ssh		22/tcp				# SSH Remote Login Protocol
domain		53/tcp				# Domain Name Server
domain		53/udp
http		80/tcp		www		# WorldWideWeb HTTP
ntp		123/udp				# Network Time Protocol
https		443/tcp				# http protocol over TLS/SSL
https		443/udp				# HTTP/3

Czego rekruter naprawdę szuka: „połączenie i potwierdzenia” kontra „bez połączenia” i przykładów usług po każdej stronie.

Zła odpowiedź: „UDP jest gorsze.” Nie gorsze, tylko inne. Pocztówka nie jest gorsza od listu poleconego, jeśli chcesz tylko napisać „pozdrowienia z Zakopanego”.

Pytanie 4: „Wymień porty, które powinieneś znać”

To pytanie trwa piętnaście sekund i nie warto na nim polec.

Odpowiedź na głos: 22 SSH, 25 SMTP do poczty, 53 DNS, 80 HTTP, 443 HTTPS, 123 NTP po UDP, 3306 MySQL i MariaDB, 5432 PostgreSQL. Jak nie pamiętam, sprawdzam w /etc/services.

Czego rekruter naprawdę szuka: że nie zawahasz się przy 22, 53, 80 i 443. Reszta to bonus.

Zła odpowiedź: „Port 8080 to port do internetu.” Tylko jeśli internet jest w trybie testowym.

Pytanie 5: „Jak sprawdzisz, który proces słucha na danym porcie?”

Odpowiedź na głos: ss -tlnp: t to TCP, l tylko nasłuchujące, n numery zamiast nazw, p proces, czyli nazwa i PID. Jako root, bo bez tego nie zobaczę procesów innych użytkowników. Alternatywnie lsof -i :port. Patrzę też na adres przed portem: 127.0.0.1 znaczy, że usługa słucha tylko lokalnie i z zewnątrz się do niej nie dostanę, a 0.0.0.0 albo * znaczy wszystkie adresy. Typowa zagadka „aplikacja działa na serwerze, a z zewnątrz nie” bardzo często kończy się właśnie na 127.0.0.1.

Postawiłem dwa testowe serwery WWW, jeden tylko lokalnie, drugi na wszystkich adresach:

python3 -m http.server 18080 --bind 127.0.0.1 &
python3 -m http.server 18081 --bind 0.0.0.0 &
sudo ss -tlnp | grep -E ':1808[01] '
sudo lsof -nP -iTCP:18080 -sTCP:LISTEN
LISTEN 0      5            0.0.0.0:18081      0.0.0.0:*    users:(("python3",pid=13968,fd=3))
LISTEN 0      5          127.0.0.1:18080      0.0.0.0:*    users:(("python3",pid=13967,fd=3))
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
python3 13967 root    3u  IPv4 425130      0t0  TCP 127.0.0.1:18080 (LISTEN)

A przy okazji złapałem klasyczny błąd. Chciałem najpierw użyć portu 8080, a ten był już zajęty przez nginx:

OSError: [Errno 98] Address already in use

„Address already in use” to ten sam problem w każdym programie: okienko w urzędzie jest zajęte, ktoś już tam siedzi. ss -tlnp mówi kto.

Czego rekruter naprawdę szuka: ss -tlnp (albo starsze netstat -tlnp), sudo i różnicy 127.0.0.1 kontra 0.0.0.0.

Zła odpowiedź: „Robię ps aux i zgaduję.” ps pokaże, że działa python3. Na którym porcie i czy w ogóle na porcie, już nie powie.

Pytanie 6: „Serwer A nie może połączyć się z serwerem B. Jak szukasz przyczyny?”

Odpowiedź na głos: Idę od dołu, warstwa po warstwie. Raz: czy A ma adres i trasę: ip a, ip route. Dwa: czy nazwa B się rozwiązuje: getent hosts. Trzy: czy B w ogóle odpowiada: ping, pamiętając, że ping bywa blokowany. Cztery: czy port jest osiągalny: nc -zv B port. „Connection refused” znaczy, że dotarłem, ale nikt nie słucha. „Timed out” znaczy, że coś po drodze, zwykle zapora, pakiety po cichu wyrzuca. Pięć: po stronie B ss -tlnp, czy usługa słucha i na jakim adresie, oraz zapora. Sześć: logi usługi.

ip a
ip route
getent hosts serwer-b
ping -c 3 serwer-b
nc -zv serwer-b 443
curl -v https://serwer-b/

Prawdziwy wynik z mojej maszyny, kiedy nikt nie słucha:

nc: connect to 127.0.0.1 port 19999 (tcp) failed: Connection refused

Czego rekruter naprawdę szuka: metody od dołu do góry i różnicy „refused” kontra „timeout”. To jedno zdanie potrafi zaoszczędzić godzinę szukania.

Zła odpowiedź: „Restartuję oba serwery.” A potem trzy. Bo przecież może to wina trzeciego.

Pytanie 7: „Jak działa zapora na Linuksie? ufw czy firewalld?”

Odpowiedź na głos: Filtrowanie pakietów robi samo jądro, przez netfilter, dziś zwykle sterowany przez nftables, wcześniej przez iptables. To ochroniarz z listą okienek, do których wolno podejść. ufw na Ubuntu i firewalld na RHEL to tylko wygodne nakładki, które piszą te reguły za mnie. W firewalld są strefy i dwie konfiguracje: bieżąca i stała. Zmiana bez --permanent znika przy przeładowaniu, a sama --permanent nie działa, dopóki nie zrobię --reload. Zasada: domyślnie blokuj wejście, otwieraj tylko to, czego potrzebujesz, i nigdy nie włączaj zapory przez SSH, zanim nie otworzysz portu SSH.

sudo ufw allow 22/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Czego rekruter naprawdę szuka: że zapora to jądro, a ufw/firewalld to tylko pilot do niej, plus --permanent i --reload. Bonus: ufw limit ssh/tcp, które według dokumentacji blokuje adres próbujący otworzyć 6 lub więcej połączeń w ciągu 30 sekund.

Zła odpowiedź: „Mamy zaporę na routerze, więc na serwerze nie trzeba.” A jak atakujący jest już w środku sieci? Ochroniarz przy bramie osiedla nie zamyka drzwi do Twojego mieszkania.

Pytanie 8: „Dlaczego logowanie kluczem SSH jest bezpieczniejsze niż hasłem?”

Odpowiedź na głos: Przy kluczu nigdy nie wysyłam sekretu na serwer. Mam parę: klucz prywatny zostaje u mnie, publiczny kładę na serwerze w authorized_keys. Serwer daje mi zadanie, które da się rozwiązać tylko kluczem prywatnym, ja je podpisuję, serwer sprawdza podpis kluczem publicznym. To kłódka na serwerze i kluczyk w kieszeni. Hasło można zgadnąć, podejrzeć, wyłudzić albo użyć tego samego, które wyciekło z innego serwisu. Klucz to kilkaset losowych bitów, których nie da się zgadnąć słownikiem. Dlatego po skonfigurowaniu kluczy wyłączam logowanie hasłem, a klucz prywatny chronię hasłem i agentem SSH.

ssh-keygen -t ed25519 -C "eryk@laptop"
ssh-copy-id uzytkownik@serwer

Czego rekruter naprawdę szuka: „klucz prywatny nigdy nie opuszcza mojego komputera” i to, że po wdrożeniu kluczy wyłączasz hasła. Bez tego drugiego klucze to tylko wygoda, nie bezpieczeństwo.

Zła odpowiedź: „Bo klucz jest zaszyfrowany.” Coś tam jest zaszyfrowane, ale nie o to chodzi. To jak powiedzieć, że sejf jest bezpieczny, bo jest z metalu.

Pytanie 9: „Klucz jest wgrany, a SSH dalej pyta o hasło albo odmawia. Co sprawdzasz?”

Odpowiedź na głos: Po kolei. Raz: czy klient w ogóle proponuje ten klucz: ssh -v i linijka „Offering public key”. Dwa: czy loguję się na właściwego użytkownika i czy klucz trafił do jego ~/.ssh/authorized_keys, w jednej linijce, bez połamania. Trzy: uprawnienia, bo sshd ma opcję StrictModes, domyślnie włączoną: folder domowy i .ssh nie mogą być zapisywalne przez innych, .ssh daję 700, authorized_keys 600, a klucz prywatny u mnie też 600, bo inaczej klient go zignoruje. Cztery: log serwera, [Ubuntu] /var/log/auth.log, [RHEL] /var/log/secure albo journalctl -u ssh czy -u sshd. Pięć: na RHEL konteksty SELinux, jeśli kopiowałem pliki ręcznie, naprawia to restorecon -Rv ~/.ssh. Sześć: konfiguracja serwera, czy PubkeyAuthentication jest włączone, sprawdzam przez sshd -T.

Postawiłem testowy sshd na porcie 2222, tylko na 127.0.0.1, z kluczem w authorized_keys. Najpierw działa:

zalogowany jako root

Teraz chmod 777 na folderze .ssh i ta sama komenda:

ssh -v -i klucz -p 2222 root@127.0.0.1 true
debug1: Authentications that can continue: publickey
debug1: Offering public key: klucz ED25519 SHA256:+Q8Iuh0UvYmX7r0hd5H5uPaG9ePUQVloZ4fnLqmm25c explicit
debug1: Authentications that can continue: publickey
root@127.0.0.1: Permission denied (publickey).

Klient zaproponował klucz, serwer go nie przyjął. Dlaczego, mówi tylko log serwera:

Authentication refused: bad ownership or modes for directory /root/.ssh

chmod 700 i znowu „zalogowany jako root”. A teraz druga strona, klucz prywatny z prawami 644:

@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
Permissions 0644 for 'klucz' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.

Klient sam wyrzucił własny klucz do kosza i, jeśli serwer na to pozwala, przechodzi do pytania o hasło. Stąd „dalej pyta o hasło”.

Czego rekruter naprawdę szuka: ssh -v po stronie klienta, log po stronie serwera i uprawnienia 700/600. Bonus za StrictModes i restorecon na RHEL.

Zła odpowiedź: „Ustawiam chmod 777 na .ssh, żeby na pewno miał dostęp.” I właśnie to jest powód, dla którego nie ma.

Pytanie 10: „Jakie ustawienia SSH zmieniasz na nowym serwerze?”

Odpowiedź na głos: W sshd_config: PermitRootLogin no, a co najmniej prohibit-password, które jest domyślne. PasswordAuthentication no po tym, jak sprawdzę, że klucze działają. PubkeyAuthentication yes. AllowUsers albo AllowGroups, żeby wpuszczać tylko wybranych. Rozsądny MaxAuthTries. Wyłączam X11Forwarding, jeśli niepotrzebne. Do tego fail2ban albo limit połączeń na zaporze. Po każdej zmianie sshd -t, żeby sprawdzić składnię, restart usługi i test logowania w drugim oknie, zanim zamknę pierwsze.

sudo sshd -t
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication|maxauthtries|x11forwarding) '
maxauthtries 6
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no
x11forwarding no

sshd -T wypisuje konfigurację, która naprawdę obowiązuje, po złożeniu wszystkich plików i wartości domyślnych. without-password to stara nazwa dla prohibit-password.

Pamiętaj: zmiany w sshd_config rób na maszynie wirtualnej albo z drugą otwartą sesją SSH. Na Ubuntu zajrzyj też do /etc/ssh/sshd_config.d/, bo pliki stamtąd mogą nadpisać to, co ustawisz w głównym.

Czego rekruter naprawdę szuka: kolejności „najpierw działające klucze, potem wyłączenie haseł” i sshd -t przed restartem.

Zła odpowiedź: „Zmieniam port na 2222 i jestem bezpieczny.” Boty też umieją liczyć do 2222.

Pytanie 11: „Jak utwardzasz serwer Linux?”

Odpowiedź na głos: Zasada najmniejszych uprawnień w kilku warstwach. Raz: aktualizacje, najlepiej automatyczne dla poprawek bezpieczeństwa. Dwa: konta: brak logowania na roota, sudo dla nazwanych ludzi, usunięte konta byłych pracowników. Trzy: SSH tylko na klucze. Cztery: zapora, domyślnie zamknięte, otwarte tylko potrzebne porty. Pięć: wyłączone i odinstalowane usługi, których nie używam, bo czego nie ma, tego nie zhakują. Sześć: SELinux albo AppArmor włączony, nie wyłączony „bo przeszkadzał”. Siedem: logi i monitoring, fail2ban, centralne zbieranie logów. Osiem: kopie zapasowe, które były sprawdzone przez odtworzenie. Na koniec skan narzędziem typu Lynis albo według benchmarku CIS.

sudo ss -tlnp
systemctl list-unit-files --state=enabled
sudo find / -xdev -perm -4000 -type f 2>/dev/null

Czego rekruter naprawdę szuka: że myślisz warstwami i potrafisz wymienić konkretne komendy, a nie hasła z folderu reklamowego. I że wspomnisz o kopiach zapasowych, bo utwardzanie nie chroni przed rm -rf zrobionym przez Ciebie samego.

Zła odpowiedź: „Instaluję antywirusa.” Na serwerze Linux to jak zakładanie kasku do pływania. Coś tam chroni, ale nie przed tym, co zwykle się dzieje.

Pytanie 12: „Czym się różni SELinux od AppArmor?”

Odpowiedź na głos: Oba to dodatkowa warstwa kontroli dostępu w jądrze, ponad zwykłymi uprawnieniami. Nawet jeśli proces działa jako root, może zrobić tylko to, na co pozwala mu polityka. SELinux, standard w rodzinie Red Hata, nakleja etykiety, czyli konteksty, na każdy plik, proces i port, i reguły mówią, który typ procesu może dotknąć którego typu pliku. Bardzo dokładny, ale trudniejszy. AppArmor, standard na Ubuntu, opisuje programy profilami opartymi na ścieżkach: ten program może czytać te ścieżki i pisać tamte. Prostszy do zrozumienia. Oba mają tryb egzekwowania i tryb, w którym tylko logują: SELinux permissive, AppArmor complain. Gdy coś nie działa, nie wyłączam ich, tylko czytam log odmów i poprawiam etykietę lub profil.

getenforce
ls -Z /var/www/html
sudo ausearch -m avc -ts recent
sudo restorecon -Rv /var/www/html
sudo aa-status
sudo aa-complain /etc/apparmor.d/usr.sbin.nazwa
sudo journalctl -k | grep -i apparmor

Czego rekruter naprawdę szuka: „etykiety” kontra „ścieżki” i tego, że rozwiązaniem nie jest setenforce 0 na zawsze. Klasyczny przykład: plik strony skopiowany przez mv z folderu domowego zachowuje złą etykietę i Apache dostaje odmowę mimo uprawnień 644. Lekarstwo to restorecon.

Zła odpowiedź: „Pierwsze, co robię na nowym serwerze, to wyłączam SELinuxa.” Rekruter z Red Hata właśnie zapisał sobie Twoje nazwisko. Na czerwono.

Pytanie 13: „Co to jest ulimit? Czym się różni limit miękki od twardego?”

Odpowiedź na głos: ulimit pokazuje i ustawia limity zasobów dla procesu i jego dzieci: ile plików może mieć otwartych naraz, ile procesów uruchomić, jak duży plik zapisać. Limit miękki to ten, który obowiązuje teraz. Twardy to sufit: zwykły użytkownik może podnieść miękki najwyżej do twardego, a twardy może tylko obniżyć. Podnieść twardy może tylko proces z odpowiednim uprawnieniem, w praktyce root. Najczęstszy problem w pracy to „Too many open files” w serwerze WWW albo bazie. Na stałe ustawiam to w /etc/security/limits.conf dla logowań, a dla usług systemd w pliku jednostki przez LimitNOFILE=, bo usługi systemd nie czytają limits.conf.

ulimit -Sn
ulimit -Hn
ulimit -Sn 1024; ulimit -Sn
ulimit -Sn 30000
20000
20000
1024
bash: line 1: ulimit: open files: cannot modify limit: Invalid argument

Próba podniesienia miękkiego ponad twardy kończy się błędem. A tak wygląda zderzenie z limitem, Python z limitem 10 plików próbuje otworzyć 20:

OSError: [Errno 24] Too many open files: '/etc/hostname'
cat /proc/<PID>/limits

Tym sprawdzisz limity działającego procesu, a nie swojej powłoki. To ważne, bo usługa ma swoje, często inne.

Czego rekruter naprawdę szuka: różnicy miękki/twardy, /proc/PID/limits i tego, że usług systemd nie zmienisz przez limits.conf.

Zła odpowiedź: „Ustawiam ulimit -n 100000 w terminalu i działa.” Działa. W tym terminalu. Usługa nawet się nie dowiedziała.

Jak to działa pod spodem

Kiedy pakiet przychodzi do serwera, przechodzi przez kolejne bramki, i każde dzisiejsze pytanie to jedna z nich.

  • Zapora w jądrze (netfilter) patrzy na adres, port i protokół. Jeśli reguła mówi „odrzuć”, pakiet ginie po cichu albo z odpowiedzią „odmowa”. Stąd „timeout” kontra „refused”.
  • Gniazdo. Jeśli przeszedł, jądro szuka procesu, który słucha na tym porcie i adresie. Nie ma nikogo? Dla TCP jądro odsyła „refused”.
  • Program, na przykład sshd, dostaje połączenie i stosuje własne zasady: konfigurację, klucze, uprawnienia plików.
  • SELinux albo AppArmor sprawdzają każdą próbę dotknięcia pliku czy portu przez ten program, niezależnie od uprawnień.
  • Limity pilnują, żeby jeden program nie zjadł wszystkich deskryptorów plików czy procesów.

A DNS dzieje się wcześniej, jeszcze zanim pierwszy pakiet wyruszy w drogę: najpierw /etc/hosts, potem serwer z resolv.conf, a dalej cała hierarchia od serwerów głównych w dół.

Ściąga flag

KomendaFlagaCo robi
getenthosts nazwarozwiązuje nazwę tak jak programy (z /etc/hosts)
dig+short / -x / +tracekrótko / odwrotnie z IP / cała droga
resolvectlstatus[Ubuntu] serwery DNS systemd-resolved
ss-tlnp / -ulnpTCP / UDP nasłuchujące z procesami
lsof-i :PORTkto trzyma port
nc-zv host porttest, czy port odpowiada
ipa / routeadresy / trasy
ufwallow / limit / status verbose[Ubuntu] zapora
firewall-cmd--permanent / --reload / --list-all[RHEL] zapora
ssh-v / -i plikgadatliwie / wybrany klucz
ssh-keygen-t ed25519nowa para kluczy
sshd-t / -Ttest składni / pełna działająca konfiguracja
getenforce—[RHEL] tryb SELinux
restorecon-Rv[RHEL] przywraca etykiety SELinux
ausearch-m avc -ts recent[RHEL] ostatnie odmowy SELinux
aa-status—[Ubuntu] profile AppArmor
ulimit-Sn / -Hn / -amiękki / twardy limit plików / wszystko

⚠ Typowy błąd

Pytanie o SSH, kandydat mówi wszystko dobrze, a potem dodaje: „i na koniec restartuję sshd i się wylogowuję”.

I tu rekruter się uśmiecha, bo wie, co będzie dalej. Jeśli w konfiguracji jest błąd, sshd nie wstanie, a Ty właśnie zamknąłeś jedyne drzwi. Na serwerze w chmurze bez konsoli to bilet w jedną stronę do zgłoszenia „proszę o odtworzenie maszyny”.

Poprawna kolejność, która zawsze robi dobre wrażenie:

sudo sshd -t
sudo systemctl reload ssh

Potem nowe okno, ssh na serwer i dopiero gdy to działa, zamykasz stare. [Ubuntu] usługa nazywa się ssh, [RHEL] sshd. Pomylenie nazw daje tylko komunikat „Unit not found”, więc przynajmniej to akurat jest błąd, który nic nie psuje.

✎ Zadanie

  1. Odpowiedz na głos na pytania 5, 9 i 12. Każde maksymalnie 60 sekund.
  2. Na swojej maszynie wirtualnej sprawdź, jak system rozwiązuje nazwy: linijka hosts w /etc/nsswitch.conf, serwery DNS ([Ubuntu] resolvectl status, [RHEL] nmcli dev show) i porównaj getent hosts localhost z dig localhost.
  3. Zepsuj i napraw SSH: daj chmod 777 na ~/.ssh użytkownika, spróbuj zalogować się kluczem z ssh -v, znajdź w logu serwera linijkę z przyczyną i przywróć 700. Rób to na VM, z drugą otwartą sesją.
Pokaż rozwiązanie

Pytania 5, 9 i 12 na głos: jeśli przy pytaniu o SSH powiedziałeś „ssh -v u mnie, log na serwerze, uprawnienia 700 i 600”, masz rdzeń odpowiedzi.

Punkt drugi, rozwiązywanie nazw:

grep '^hosts' /etc/nsswitch.conf
resolvectl status        # [Ubuntu]
nmcli dev show | grep DNS   # [RHEL]
getent hosts localhost
dig +short localhost
hosts:          files dns
127.0.0.1       localhost

getent odpowiedział od razu, bo wziął nazwę z /etc/hosts. A dig nie zwrócił nic, bo pyta serwer DNS bezpośrednio, z pominięciem pliku hosts, a ten serwer o localhost nie musi mieć pojęcia. Na Ubuntu dig może jednak odpowiedzieć: lokalny systemd-resolved sam z siebie zna nazwę localhost i odpowiada na nią bez pytania internetu. To jest dokładnie ta różnica, o którą lubią pytać rekruterzy.

Punkt trzeci, zepsute SSH. Po chmod 777 ~/.ssh w logu serwera ([Ubuntu] /var/log/auth.log, [RHEL] /var/log/secure) pojawia się linijka w stylu:

Authentication refused: bad ownership or modes for directory /home/eryk/.ssh

chmod 700 ~/.ssh i logowanie wraca. Jeśli się nie udało i nie miałeś drugiej sesji, to właśnie poznałeś, po co jest konsola maszyny wirtualnej.

Co dalej

Ostatnia lekcja modułu to scenariusze: „serwer jest wolny”, „dysk pełny”, „nie mogę się zalogować”, „z crona nie działa”, „co się dzieje, gdy wpisujesz komendę” i bomba fork, której na pewno nie uruchomimy. Plus jak w ogóle odpowiadać na głos, kiedy nie znasz odpowiedzi.