Zabezpieczenie SSH: logowanie kluczem, blokada logowania roota, wyłączenie haseł

Opublikowano 14 min czytania

ed25519 pod Linuksem, macOS i Windows, pułapka cloud-init w /etc/ssh/sshd_config.d, aktywacja przez socket w Ubuntu 24.04, dowód przez sudo sshd -T oraz droga ratunkowa przez konsolę.

Świeżo postawiony serwer trafia na listy skanerów już kilka minut po tym, jak po raz pierwszy odezwie się z sieci. To, co potem do niego puka, wygląda prawie zawsze tak samo: automatyczne przebieranie nazw użytkowników i haseł na porcie 22. Kto wyłączy logowanie hasłem i dopuści wyłącznie klucze, zabiera całej tej klasie ataków grunt spod nóg. Nie osłabia jej, tylko usuwa w całości.

Podstawowe kroki zna większość administratorów. W typowych poradnikach brakuje tego, co dzieje się potem: dlaczego PasswordAuthentication no na obrazach chmurowych regularnie nie odnosi żadnego skutku, dlaczego systemctl reload ssh na serwerze z aktywnym ssh.socket nie robi już tego, czego oczekujesz, oraz jak udowodnić, zamiast mieć nadzieję, że zmiana naprawdę zadziałała. Właśnie o to chodzi w tym poradniku. Wszystkie informacje dotyczą Debiana 13, Debiana 12, Ubuntu 24.04 i Ubuntu 22.04.

Zasada, która ratuje wszystko: dwie sesje

Zanim zmienisz cokolwiek w konfiguracji SSH, otwórz drugie okno terminala i zaloguj się w nim na serwer po raz drugi. Ta druga sesja pozostaje otwarta tak długo, aż z powodzeniem przetestujesz nową konfigurację trzecim, całkowicie nowym połączeniem.

Powód jest czysto techniczny: restart usługi SSH nie kończy istniejących sesji. Trwające połączenia obsługują odgałęzione wcześniej procesy potomne, które przeżywają restart procesu nadrzędnego. Zepsutą konfigurację zauważysz więc dopiero przy kolejnej próbie połączenia, a wtedy stara sesja jest twoją jedyną drogą powrotną. Kto ją zamyka, żeby „zalogować się jeszcze raz, tym razem czysto”, żałuje tego z pewną regularnością.

Sprawdź poza tym, z jaką wersją OpenSSH masz do czynienia, bo od niej zależy kilka szczegółów:

ssh -V
SystemOpenSSHDomyślny listener
Debian 13 (trixie)10.0p2ssh.service
Debian 12 (bookworm)9.2p1ssh.service
Ubuntu 24.04 LTS9.6p1ssh.socket
Ubuntu 22.04 LTS8.9p1ssh.service

Jeśli usługi serwera w ogóle nie ma, na przykład w minimalnym obrazie systemu, doinstaluj ją:

sudo apt update
sudo apt install -y openssh-server

Generowanie klucza: ed25519 pod Linuksem, macOS i Windows

Wybierz ed25519. Klucz jest krótki, szybko się go weryfikuje, ma duży zapas bezpieczeństwa, a obsługuje go każda omawiana tu wersja OpenSSH. RSA potrzebujesz już tylko do starych systemów, które nie przyjmują niczego innego, i to z długością co najmniej 4096 bitów.

Linux i macOS

ssh-keygen -t ed25519 -a 100 -C "hani@notebook" -f ~/.ssh/id_ed25519

-a 100 podnosi liczbę rund KDF dla hasła klucza (passphrase) i znacząco podraża ataki offline na sam plik klucza. -C ustawia komentarz, który trafia później do authorized_keys i podpowiada ci, który klucz pochodzi z którego urządzenia. Nadaj hasło klucza. Klucz bez hasła to zwykły plik, który zabierze ze sobą każdy, kto choć na chwilę usiądzie przy twoim komputerze.

Żebyś nie musiał wpisywać hasła klucza przy każdym połączeniu, buforowaniem zajmie się agent:

eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519

Pod macOS zamiast tego umieszczasz hasło klucza w pęku kluczy. Dawny przełącznik -K nazywa się od macOS 12 --apple-use-keychain:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

Żeby ustawienie przetrwało restart, do ~/.ssh/config trafia:

Host *
    UseKeychain yes
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519

Windows

Windows 10 od wersji 1809, Windows 11 oraz Windows Server od 2019 mają klienta OpenSSH na pokładzie. W PowerShellu:

ssh -V
ssh-keygen -t ed25519 -a 100 -C "hani@windows"

Klucz trafia do C:\Users\TwojaNazwa\.ssh\. Jeśli klienta brakuje, doinstaluj go jako funkcję systemu Windows:

Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

Agent jest pod Windows usługą systemową i fabrycznie pozostaje wyłączony:

Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519

Przeniesienie klucza publicznego na serwer

Na serwer trafia wyłącznie plik z rozszerzeniem .pub. Plik bez rozszerzenia to klucz prywatny i nigdy nie opuszcza twojego komputera.

Wygodna droga pod Linuksem

ssh-copy-id -i ~/.ssh/id_ed25519.pub root@203.0.113.10

ssh-copy-id zakłada katalog ~/.ssh, ustawia poprawne uprawnienia, dopisuje klucz do authorized_keys i sprawdza przy okazji, czy nie ma go tam już wcześniej. Pod macOS narzędzie nie jest dołączone do każdej wersji systemu. Sprawdź to krótko poleceniem command -v ssh-copy-id, a w razie potrzeby przejdź na drogę ręczną.

Droga pod Windows, bez ssh-copy-id

Klient OpenSSH od Microsoftu nie zawiera ssh-copy-id. Oryginał jest skryptem powłoki i nigdy nie został przeniesiony na Windows. Ten sam efekt osiągniesz potokiem:

type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh root@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Czai się tu szczegół, który potrafi kosztować wiele godzin: PowerShell, przekazując dane do zewnętrznego programu, dokleja końce wierszy w formacie Windows. W authorized_keys ląduje wtedy niewidoczny znak powrotu karetki na końcu wiersza. Jeśli opatrzyłeś klucz komentarzem przez -C, znak ten trafia do pola komentarza i niczemu nie przeszkadza. Bez komentarza dokleja się do bloku Base64, a logowanie kończy się niepowodzeniem bez żadnego sensownego komunikatu. Dlatego: zawsze ustawiaj komentarz, a w razie wątpliwości posprzątaj raz na serwerze.

sed -i 's/\r$//' ~/.ssh/authorized_keys

Droga ręczna, która działa wszędzie

mkdir -p ~/.ssh && chmod 700 ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys

Następnie dopisz zawartość pliku .pub jako jeden jedyny wiersz. Jeśli plik leży już na serwerze, bo na przykład wgrałeś go wcześniej, zrobisz to od razu:

cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys

Sprawdź potem, co faktycznie znalazło się w pliku:

ssh-keygen -lf ~/.ssh/authorized_keys

Wynik zawiera odcisk palca i komentarz każdego poprawnego wpisu. Czego tu nie widać, zostało złamane na kilka wierszy albo uszkodzone. Porównaj odcisk palca z odciskiem swojego lokalnego klucza:

ssh-keygen -l -f ~/.ssh/id_ed25519.pub

Jeśli oba są zgodne, transport przebiegł prawidłowo. Teraz, zanim cokolwiek wyłączysz, przetestuj, czy logowanie kluczem w ogóle działa. Dalej idziesz dopiero wtedy, gdy nowe połączenie przechodzi bez pytania o hasło.

Pułapka: /etc/ssh/sshd_config.d i cloud-init

To jest miejsce, w którym większość poradników się kończy, i najczęstszy powód zdania „przecież to wyłączyłem, a ono dalej działa”.

Od Debiana 11 i Ubuntu 22.04 na samej górze pliku /etc/ssh/sshd_config znajduje się wiersz, który wczytuje cały katalog. Sprawdź sam, na której pozycji:

grep -n Include /etc/ssh/sshd_config

Na wszystkich czterech omawianych tu systemach wiersz Include znajduje się na początku, a nie na końcu. I tu dochodzi osobliwość OpenSSH, której nie zna prawie żaden inny język konfiguracji: wygrywa pierwsza znaleziona wartość, a nie ostatnia. To, co leży w pliku w katalogu /etc/ssh/sshd_config.d/, jest więc czytane przed plikiem głównym i bije każdy późniejszy wiersz w sshd_config.

Cloud-init korzysta dokładnie z tego katalogu. Przy pierwszym uruchomieniu zapisuje /etc/ssh/sshd_config.d/50-cloud-init.conf, a w zależności od sposobu udostępnienia serwera widnieje tam PasswordAuthentication yes. Możesz potem wpisywać PasswordAuthentication no do sshd_config tyle razy, ile chcesz: logowanie hasłem i tak pozostanie otwarte. Najpierw zrób sobie rozeznanie:

ls -la /etc/ssh/sshd_config.d/
sudo grep -riE 'passwordauthentication|permitrootlogin|kbdinteractive' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

Stąd wynika właściwe zalecenie: nie ruszaj sshd_config w ogóle. Załóż zamiast tego własny plik, którego nazwa sortuje się alfabetycznie przed wszystkim, co odkłada tam automatyzacja. Pliki są wczytywane w kolejności posortowanej, 01- idzie przed 50-, a ponieważ wygrywa pierwsza wartość, cloud-init może potem zapisywać swój plik dowolnie wiele razy i tak nie podważy twojego utwardzenia. Na tym polega różnica wobec szeroko rozpowszechnionej rady, żeby zakładać 99-hardening.conf: taki plik przegrywa z cloud-init, i to całkiem po cichu. Ta sama mechanika potrafi się jednak obrócić przeciwko tobie: plik z mniejszym numerem, na przykład 00-cloud.conf, wygrywa z twoim 01-, bo OpenSSH bierze wartość przeczytaną jako pierwszą. Zajrzenie do katalogu opłaca się dlatego również po utwardzeniu.

Zapis utwardzenia, z bezpiecznikiem czasowym jako drogą wycofania

Najpierw rozepnij siatkę bezpieczeństwa. Poniższe polecenie usuwa twój nowy plik automatycznie po dziesięciu minutach i restartuje SSH, o ile do tego czasu go nie przerwiesz:

sudo systemd-run --on-active=10min --unit=ssh-rollback /bin/sh -c 'rm -f /etc/ssh/sshd_config.d/01-hardening.conf; systemctl try-restart ssh.socket ssh.service'

Teraz właściwa konfiguracja:

sudo tee /etc/ssh/sshd_config.d/01-hardening.conf >/dev/null <<'EOF'
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
PermitEmptyPasswords no
MaxAuthTries 3
EOF
sudo chmod 644 /etc/ssh/sshd_config.d/01-hardening.conf

Dwa wiersze zasługują na wyjaśnienie. KbdInteractiveAuthentication no to nie ozdobnik: jeśli zostanie na yes, PAM może dalej podsuwać pytanie o hasło okrężną drogą interaktywnego wprowadzania z klawiatury, choć PasswordAuthentication jest ustawione na no. Dokładnie dlatego istnieją serwery, które mimo wyłączonego logowania hasłem nadal pytają o hasło.

I jeszcze PermitRootLogin prohibit-password zamiast no: dzięki temu dostęp jako root kluczem zostaje zachowany, a hasła dla roota są wykluczone. Jeśli masz już założonego własnego użytkownika z sudo i sprawdziłeś, że logowanie kluczem na to konto naprawdę działa, ustaw zamiast tego PermitRootLogin no. Wcześniej nie.

Czego nie warto przenosić do własnej konfiguracji, choć zalecają to starsze poradniki: ChallengeResponseAuthentication. Od OpenSSH 8.7 opcja jest już tylko przestarzałym aliasem dla KbdInteractiveAuthentication i w aktualnych wersjach wywołuje w logu komunikat o wycofaniu. Pomiń ją.

Kontrola składni przed włączeniem, bez wyjątku:

sudo sshd -t

Brak jakiegokolwiek wyniku oznacza: plik jest poprawny składniowo. Nie znaczy to, że merytorycznie robi to, czego chcesz. Od tego jest za chwilę sudo sshd -T.

Jeśli test zgłasza zamiast tego Missing privilege separation directory: /run/sshd, to znaczy, że sshd od startu systemu nie uruchomił się ani razu, bo ten katalog powstaje dopiero razem z ssh.service. Dotyczy to przede wszystkim Ubuntu 24.04 z aktywacją przez socket i da się usunąć poleceniem sudo mkdir -p /run/sshd albo sudo systemctl start ssh.service, a na serwerze, na którym właśnie jesteś zalogowany przez SSH, i tak nie wystąpi.

Włączenie: ssh.service, ssh.socket i różnice między dystrybucjami

Tutaj cztery systemy się rozchodzą, a stary nawyk systemctl reload ssh jest złą odpowiedzią wszędzie tam, gdzie port trzyma ssh.socket.

Ubuntu stawia od wersji 22.10 na aktywację przez socket, a Ubuntu 24.04 dostarcza ją domyślnie: tam ssh.socket jest włączony, a ssh.service wyłączony. Debian robi to fabrycznie dokładnie odwrotnie. Debian 13 i Debian 12 dostarczają włączony ssh.service i wyłączony ssh.socket, wbrew rozpowszechnionemu przekonaniu, że Debian 13 przy nowej instalacji przechodzi na socket. Przy aktywacji przez socket to sam systemd nasłuchuje na porcie 22 i dopiero przy przychodzącym połączeniu uruchamia świeży proces sshd. Ma to przyjemny efekt uboczny: zmiany w sshd_config i tak zadziałają przy kolejnym połączeniu, bo każdy proces obsługujący połączenie wczytuje konfigurację od nowa. Przeładowania potrzebujesz tylko dla ustawień dotyczących samego listenera, czyli Port oraz ListenAddress.

Nie zgaduj więc, tylko zapytaj system, która jednostka obsługuje u ciebie tę usługę:

systemctl is-enabled ssh.socket ssh.service
Systemssh.socketssh.serviceCecha szczególna
Ubuntu 24.04 LTSenableddisabledAktywacja przez socket domyślnie, ListenStream na 0.0.0.0:22 oraz [::]:22
Debian 13 (trixie)disabledenabledAccept=no
Debian 12 (bookworm)disabledenabledAccept=no
Ubuntu 22.04 LTS (a także Debian 11)disabledenabledAccept=yes, czyli osobny proces na każde połączenie przez ssh@.service

A potem polecenie, które jest poprawne na wszystkich czterech systemach:

sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service

try-restart restartuje jednostkę tylko wtedy, gdy jest ona faktycznie aktywna, a drugą zostawia nietkniętą. Dzięki temu nie musisz zgadywać. Obie części wymagają uprawnień roota: sshd leży w /usr/sbin i na Debianie nie znajduje się w PATH zwykłego użytkownika, a try-restart i tak sięga do systemd. Bez sudo wywołanie kończy się, zależnie od systemu, komunikatem sshd: command not found albo sshd: no hostkeys available, bo klucze hosta w /etc/ssh są czytelne tylko dla roota.

Równie świadomie żadnego pojedynczego systemctl restart ssh.socket, nawet jeśli to polecenie pojawia się w wielu poradnikach. Na Debianie 13 i Debianie 12 przerywa ono działanie z komunikatem Job failed. See journalctl -xe for details., a w journalu dochodzi do tego ssh.socket: Socket service ssh.service already active, refusing. Nowa konfiguracja nie staje się przy tym aktywna, a test kontrolny nadal zgłasza Permission denied (publickey,password). Na Ubuntu 22.04 i Debianie 11 polecenie przechodzi wprawdzie bez komunikatu błędu, ale zatrzymuje przy tym ssh.service i przestawia hosta na aktywację przez socket. Ponieważ ssh.socket pozostaje tam wyłączony, przy kolejnym restarcie znów rusza ssh.service, tryb pracy przeskakuje więc niepostrzeżenie tam i z powrotem. try-restart na obie jednostki omija jedno i drugie.

Świadomie także żadnego reload: na systemie, na którym port trzyma ssh.socket, systemctl reload ssh kwituje to komunikatem

fatal: Cannot bind any address.

Usługa trafia potem w stan błędu i oddaje swój port. Restart nie ma tego problemu i tak samo nie kończy istniejących sesji.

Jeśli chcesz przenieść port, pojawia się kolejna różnica między dystrybucjami. Ubuntu 24.04 tworzy konfigurację socketu generatorem systemd na podstawie sshd_config, tam po zmianie portu wystarczy:

sudo systemctl daemon-reload
sudo systemctl try-restart ssh.socket ssh.service

Jeśli natomiast na Debianie świadomie przestawiłeś się na ssh.socket, port znajduje się w samej jednostce, a zmiana w sshd_config nie odnosi żadnego skutku. Zapisujesz go wtedy w pliku uzupełniającym przez sudo systemctl edit ssh.socket, z pustym ListenStream= do wyzerowania i drugim wierszem z nową wartością. Wynik sprawdź potem w rzeczywistości, a nie w konfiguracji:

sudo ss -tlnp

Dowód: sshd -T i test kontrolny

Polecenie, które przechodzi bez błędu, nie jest jeszcze dowodem. Dowodem jest rozwinięta konfiguracja całkowita, w której uwzględniono już wszystkie dołączone pliki:

sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|permitrootlogin|usepam|port|authorizedkeysfile)'

Oczekiwany wynik wygląda mniej więcej tak:

port 22
permitrootlogin prohibit-password
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
usepam yes
authorizedkeysfile .ssh/authorized_keys .ssh/authorized_keys2

Jeśli mimo twojego pliku widnieje tam passwordauthentication yes, w katalogu leży plik, który sortuje się alfabetycznie przed twoim. Wróć do rozdziału o kolejności.

Do zapytania o pojedynczą wartość wystarczy to samo polecenie z węższym filtrem:

sudo sshd -T | grep -i passwordauthentication

Za sudo na początku przemawiają dwa powody. Po pierwsze sshd -T czyta klucze hosta, które w /etc/ssh są czytelne tylko dla roota. Po drugie sam sshd leży w /usr/sbin, a ten katalog nie należy na Debianie do PATH zwykłego użytkownika, przez co wywołanie bez sudo kończy się tam komunikatem sshd: command not found. Kto chce ominąć drogę przez sudo, wpisuje pełną ścieżkę /usr/sbin/sshd.

Od OpenSSH 9.3, czyli na Ubuntu 24.04 (9.6p1) i Debianie 13 (10.0p2), dochodzi dodatkowo sshd -G. Opcja rozwija tę samą konfigurację, ale nie wymaga czytelnych kluczy hosta ani istniejącego /run/sshd, co czyni ją przydatną do sprawdzeń w automatyzacji i w kontenerach. Na Debianie 12 (9.2p1), Ubuntu 22.04 (8.9p1) i Debianie 11 (8.4p1) przełącznika jeszcze nie ma, sshd kwituje go tam jako nieznaną opcję. Dla poradnika, który ma obowiązywać wszędzie, właściwym wyborem pozostaje więc sudo sshd -T.

Teraz test kontrolny, i to z nowego terminala, podczas gdy stara sesja pozostaje otwarta. Wymuś logowanie hasłem i wyłącz klucze na czas tej próby:

ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password,keyboard-interactive root@203.0.113.10

Poprawnie jest wtedy, gdy zostaniesz odrzucony natychmiast i bez żadnego pytania o hasło:

root@203.0.113.10: Permission denied (publickey).

Decydująca jest zawartość nawiasu. Jeśli widnieje tam (publickey,password) albo pojawia się pytanie o hasło, logowanie hasłem jest nadal otwarte. Dopiero gdy test kontrolny zawiedzie dokładnie tak, jak powinien, a zwykłe połączenie kluczem nadal działa, zamykasz starą sesję i zatrzymujesz bezpiecznik czasowy:

sudo systemctl stop ssh-rollback.timer

Komunikaty błędów, dosłownie

Permission denied (publickey). Serwer przyjmuje tylko klucze, a twój nie pasuje. Wywołaj ssh -v i sprawdź, który plik w ogóle został zaproponowany. Najczęstsze przyczyny: zła nazwa użytkownika, klucz w authorized_keys niewłaściwego użytkownika, albo zablokowałeś roota, a dalej logujesz się jako root.

Authentication refused: bad ownership or modes for directory /home/hani/.ssh Ten wiersz nie pojawia się na twoim ekranie, tylko w logu serwera, widoczny przez sudo journalctl -u ssh -n 50 --no-pager. Nie filtruj przy tym po -t sshd: od OpenSSH 9.8, czyli na Debianie 13 już w stanie fabrycznym, sesje działają we własnym procesie sshd-session, a pod znacznikiem sshd zostają potem tylko komunikaty listenera i ani jedna operacja logowania. Kto chce filtrować po znacznikach, użyje sudo journalctl -t sshd -t sshd-session -n 50 --no-pager. OpenSSH odmawia przyjęcia klucza, gdy katalog domowy, .ssh albo authorized_keys są zapisywalne dla grupy lub dla pozostałych. Poprawka:

chmod go-w ~ && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys

WARNING: UNPROTECTED PRIVATE KEY FILE! względnie Load key "/home/hani/.ssh/id_ed25519": bad permissions. Ten sam problem po stronie klienta. Rozwiązuje go chmod 600 ~/.ssh/id_ed25519.

Too many authentication failures Twój agent podsuwa po kolei wszystkie wczytane klucze i przekracza przy tym MaxAuthTries. Ogranicz połączenie do jednego jedynego klucza: ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 hani@203.0.113.10.

sign_and_send_pubkey: no mutual signature supported Stary klucz RSA z podpisem SHA-1 trafia na serwer, który już tego nie przyjmuje. Wygeneruj klucz ed25519, zamiast majstrować przy PubkeyAcceptedAlgorithms.

Bad owner or permissions on C:\Users\hani\.ssh\config Klient Windows sprawdza uprawnienia dostępu do swojego pliku konfiguracyjnego. We właściwościach pliku, w zakładce Zabezpieczenia, usuń dziedziczenie oraz wszystkie wpisy poza twoim kontem użytkownika i SYSTEM.

WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! Klucz hosta serwera jest inny niż poprzednim razem. Po ponownej instalacji systemu jest to spodziewane, w innym wypadku nie. Usuń celowo tylko ten jeden wpis poleceniem ssh-keygen -R 203.0.113.10, i tylko wtedy, gdy znasz powód.

Zatrzaśnięte drzwi: droga przez konsolę w panelu klienta

Jeśli obie sesje przepadły i żadne połączenie się już nie nawiązuje, nie jest to utrata danych, tylko objazd. Każdy serwer root KVM i każdy serwer dedykowany KernelHost ma w panelu klienta konsolę, która jest podpięta do wyjścia ekranowego systemu i działa niezależnie od stosu sieciowego serwera. Wejdziesz więc do środka także wtedy, gdy SSH w ogóle już nie nasłuchuje.

  1. Zaloguj się w panelu klienta, otwórz dotknięty serwer i uruchom konsolę.
  2. W monicie logowania zaloguj się jako root hasłem nadanym przy udostępnianiu serwera. Logowanie przez konsolę nie przechodzi przez SSH, więc PermitRootLogin go nie dotyczy.
  3. Cofnij zmianę: sudo rm /etc/ssh/sshd_config.d/01-hardening.conf
  4. Sprawdź i zrestartuj: sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service
  5. Jeśli SSH w ogóle nie działa, pomogą sudo systemctl status ssh.socket ssh.service oraz zajrzenie do sudo journalctl -u ssh -n 50 --no-pager. Kod wyjścia 3 przy status oznacza tylko, że jedna z dwóch jednostek jest nieaktywna, a na Debianie z wyłączonym ssh.socket to sytuacja normalna.

Dwa środki ostrożności oszczędzą ci tej drogi prawie zawsze. Zanotuj hasło roota, zanim wyłączysz logowanie hasłem, bo konsola go potrzebuje. I sprawdź aktywny firewall, zanim przeniesiesz port SSH. Zmiana portu bez pasującego otwarcia zatrzaskuje drzwi równie skutecznie jak zepsuta sshd_config, ale wygląda inaczej: zamiast Permission denied dostajesz Connection timed out.

W skrócie

  • Zostaw drugą sesję otwartą, dopóki nie potwierdzisz, że trzecie, nowe połączenie działa.
  • ed25519 z -a 100 i hasłem klucza, klucz publiczny przez ssh-copy-id, a pod Windows potokiem.
  • Przetestuj logowanie kluczem, zanim padnie logowanie hasłem.
  • Utwardzenie do /etc/ssh/sshd_config.d/01-hardening.conf, nie do sshd_config. Wygrywa pierwsza znaleziona wartość, stąd niski numer.
  • Nie zapomnij o KbdInteractiveAuthentication no, inaczej okrężna droga przez PAM zostaje otwarta.
  • Włączaj przez sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service, a nie przez reload ani pojedynczy restart ssh.socket.
  • Wcześniej ustal poleceniem systemctl is-enabled ssh.socket ssh.service, która jednostka jest w ogóle aktywna. Ubuntu 24.04 korzysta z socketu, a Debian 13 i Debian 12 z usługi.
  • Dowód przez sudo sshd -T oraz wymuszoną próbę logowania hasłem z nowej sesji.
  • Drogą awaryjną jest konsola w panelu klienta, miej na nią przygotowane hasło roota.

Kolejne oczywiste warstwy opisują nasze artykuły o fail2ban oraz o firewallu UFW. Ważniejsze od obu jest to, co właśnie zrobiłeś.

Najczęstsze pytania

Dlaczego logowanie hasłem działa dalej mimo PasswordAuthentication no?
Prawie zawsze przez plik w katalogu /etc/ssh/sshd_config.d/, najczęściej 50-cloud-init.conf. Wiersz Include znajduje się na Debianie i Ubuntu na początku pliku sshd_config, a OpenSSH bierze pierwszą znalezioną wartość, nie ostatnią. To, co leży w tym katalogu, wygrywa więc z plikiem głównym. Sprawdź poleceniem sudo sshd -T, co faktycznie obowiązuje, i załóż własny plik jako 01-hardening.conf, żeby był czytany przed wszystkimi plikami tworzonymi automatycznie.
Czy po zmianie w sshd_config muszę zrestartować usługę?
Na Debianie 13, Debianie 12 i Ubuntu 22.04 tak, bo fabrycznie włączony jest tam ssh.service, a ssh.socket pozostaje wyłączony. Na Ubuntu 24.04 systemd nasłuchuje domyślnie przez ssh.socket i uruchamia na każde połączenie nowy proces sshd, który i tak wczytuje konfigurację od nowa. Samego listenera dotyczą tylko Port oraz ListenAddress. Która jednostka jest u ciebie aktywna, pokaże systemctl is-enabled ssh.socket ssh.service. Polecenie sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service jest poprawne na wszystkich czterech systemach, a pojedynczy systemctl restart ssh.socket już nie: na Debianie 13 i Debianie 12 kończy się niepowodzeniem i nowa konfiguracja nie staje się aktywna, a na Ubuntu 22.04 niepostrzeżenie przestawia hosta na aktywację przez socket.
Dlaczego lepiej unikać polecenia reload?
Na systemach z aktywnym ssh.socket, czyli domyślnie na Ubuntu 24.04, systemctl reload ssh przerywa działanie z komunikatem fatal: Cannot bind any address, usługa trafia w stan błędu i oddaje swój port. Restart nie ma tego problemu i tak samo nie kończy istniejących sesji, bo trwające połączenia obsługują własne procesy potomne.
Jak przenieść klucz z Windows na serwer, skoro nie ma tam ssh-copy-id?
Potokiem: type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh użytkownik@serwer "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys". Zadbaj o to, żeby klucz miał komentarz ustawiony przez -C, inaczej doklejony przez PowerShell znak powrotu karetki może uszkodzić blok Base64. Na serwerze sprząta to sed -i 's/\r$//' ~/.ssh/authorized_keys.
Jak udowodnić, że logowanie hasłem jest naprawdę zamknięte?
W dwóch krokach. Po pierwsze sudo sshd -T, które wypisuje rozwiniętą konfigurację całkowitą i musi pokazywać passwordauthentication no oraz kbdinteractiveauthentication no. Po drugie test kontrolny z nowej sesji: ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password,keyboard-interactive użytkownik@serwer musi zostać odrzucone natychmiast, komunikatem Permission denied (publickey). Jeśli w nawiasie widnieje publickey,password, dostęp jest nadal otwarty.
Co zrobić, jeśli zablokowałem sobie dostęp?
Zaloguj się w panelu klienta, otwórz serwer i uruchom konsolę. Jest podpięta do wyjścia ekranowego systemu i działa niezależnie od SSH. Zaloguj się tam jako root, usuń własny plik poleceniem sudo rm /etc/ssh/sshd_config.d/01-hardening.conf, sprawdź składnię przez sudo sshd -t i zrestartuj przez sudo systemctl try-restart ssh.socket ssh.service. Miej hasło roota pod ręką, zanim wyłączysz logowanie hasłem.
Co jest lepszym wyborem: PermitRootLogin no czy prohibit-password?
prohibit-password nadal wpuszcza roota kluczem i wyklucza wyłącznie hasła, dzięki czemu dostęp zostaje zachowany także przy błędnej konfiguracji. no jest ostrzejsze, ale zakłada, że istnieje drugi użytkownik z sudo, którego logowanie kluczem już w sprawdzony sposób działa. Przestaw się dopiero wtedy, gdy ten test wypadnie pomyślnie.

SSH Bezpieczeństwo serwera Linux Debian Ubuntu OpenSSH ed25519 systemd cloud-init Poradnik