Защита SSH: вход по ключу, запрет root-логина, отключение пароля

Опубликовано 15 мин. чтения

ed25519 в Linux, macOS и Windows, ловушка cloud-init в /etc/ssh/sshd_config.d, активация через сокет в Ubuntu 24.04, доказательство через sudo sshd -T и аварийный путь через консоль.

Свежеустановленный сервер попадает в списки сканеров уже через несколько минут после того, как впервые становится доступен из сети. Прилетает при этом почти всегда одно и то же: автоматический перебор имён пользователей и паролей на порту 22. Тот, кто отключает вход по паролю и оставляет только ключи, лишает весь этот класс атак почвы под ногами. Не ослабляет, а убирает полностью.

Базовые шаги известны почти всем. В обычных руководствах не хватает того, что идёт дальше: почему PasswordAuthentication no на облачных образах регулярно остаётся без эффекта, почему systemctl reload ssh на сервере с активным ssh.socket делает совсем не то, что вы ожидаете, и как доказать, а не понадеяться, что изменение вступило в силу. Именно об этом пойдёт речь. Все сведения относятся к Debian 13, Debian 12, Ubuntu 24.04 и Ubuntu 22.04.

Правило, которое спасает всё: два сеанса

Прежде чем менять хоть что-то в конфигурации SSH, откройте второе окно терминала и войдите в нём на сервер ещё раз. Этот второй сеанс остаётся открытым до тех пор, пока вы успешно не проверите новую конфигурацию третьим, полностью новым соединением.

Причина техническая: перезапуск службы SSH не разрывает уже установленные сеансы. Активные соединения обслуживаются дочерними процессами, порождёнными раньше, и переживают перезапуск родительского процесса. Сломанную конфигурацию вы заметите только при следующем подключении, и тогда старый сеанс окажется вашим единственным путём назад. Тот, кто закрывает его, чтобы «зайти заново начисто», жалеет об этом с завидной регулярностью.

Проверьте заодно версию OpenSSH, потому что от неё зависит сразу несколько деталей:

ssh -V
СистемаOpenSSHСлушатель по умолчанию
Debian 13 (trixie)10.0p2ssh.service
Debian 12 (bookworm)9.2p1ssh.service
Ubuntu 24.04 LTS9.6p1ssh.socket
Ubuntu 22.04 LTS8.9p1ssh.service

Если серверной службы нет вообще, например в минимальном образе, доустановите её:

sudo apt update
sudo apt install -y openssh-server

Создание ключа: ed25519 в Linux, macOS и Windows

Берите ed25519. Короткий, быстро проверяется, большой запас прочности, и его понимает каждая рассмотренная здесь версия OpenSSH. RSA нужен только для старых систем, которые не принимают ничего другого, и тогда с длиной не менее 4096 бит.

Linux и macOS

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

-a 100 повышает число раундов KDF для парольной фразы и заметно удорожает офлайновые атаки на файл ключа. -C задаёт комментарий, который потом окажется в authorized_keys и подскажет вам, какой ключ с какого устройства. Обязательно задайте парольную фразу. Ключ без парольной фразы — это просто файл, который унесёт любой, кто хоть на минуту окажется за вашим компьютером.

Чтобы не вводить парольную фразу при каждом подключении, кэшированием займётся агент:

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

В macOS парольная фраза вместо этого кладётся в связку ключей. Прежняя опция -K начиная с macOS 12 называется --apple-use-keychain:

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

Чтобы это пережило перезагрузку, в ~/.ssh/config должно быть:

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

Windows

Windows 10 начиная с версии 1809, Windows 11 и Windows Server начиная с 2019 поставляются с клиентом OpenSSH. В PowerShell:

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

Ключ окажется в C:\Users\ВАШЕ_ИМЯ\.ssh\. Если клиента нет, доустановите его как компонент Windows:

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

Агент в Windows представляет собой системную службу, и в состоянии поставки она отключена:

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

Перенос открытого ключа на сервер

На сервер попадает исключительно файл с расширением .pub. Файл без расширения — это закрытый ключ, и он никогда не покидает ваш компьютер.

Удобный способ в Linux

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

ssh-copy-id создаёт ~/.ssh, выставляет правильные права, дописывает ключ в authorized_keys и заодно проверяет, нет ли его там уже. В macOS эта утилита входит в комплект не в каждой версии. Быстро убедитесь командой command -v ssh-copy-id, а иначе переходите к ручному способу.

Способ для Windows без ssh-copy-id

Клиент OpenSSH от Microsoft не содержит ssh-copy-id. Оригинал представляет собой shell-скрипт, и его никогда не портировали. Тот же результат достигается через конвейер:

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"

Здесь прячется деталь, которая стоит многих часов: при передаче данных внешней программе PowerShell дописывает переводы строк в формате Windows. В authorized_keys в конце строки оказывается невидимый символ возврата каретки. Пока ключ снабжён комментарием через -C, этот символ попадает в поле комментария и ничему не мешает. Без комментария он приклеивается к блоку Base64, и вход завершается отказом без внятного сообщения. Отсюда правило: всегда задавайте комментарий, а при сомнениях один раз почистите файл на сервере.

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

Ручной способ, который работает везде

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

Затем допишите содержимое файла .pub одной-единственной строкой. Если файл уже лежит на сервере, например потому что вы его загрузили, это делается напрямую:

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

После этого проверьте, что на самом деле оказалось в файле:

ssh-keygen -lf ~/.ssh/authorized_keys

Вывод перечисляет отпечаток и комментарий каждой корректной записи. Всё, что здесь не появилось, разорвано переносом строки или повреждено. Сравните отпечаток с отпечатком вашего локального ключа:

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

Если оба совпадают, перенос прошёл чисто. Теперь, прежде чем что-либо отключать, проверьте, работает ли вход по ключу вообще. Дальше двигаемся только тогда, когда новое соединение проходит без запроса пароля.

Ловушка: /etc/ssh/sshd_config.d и cloud-init

Именно здесь заканчивается большинство руководств, и это самая частая причина фразы «я же отключил, а оно всё равно работает».

Начиная с Debian 11 и Ubuntu 22.04 в самом начале /etc/ssh/sshd_config стоит строка, которая подключает целый каталог. Посмотрите сами, на какой она позиции:

grep -n Include /etc/ssh/sshd_config

Во всех четырёх рассматриваемых системах строка Include стоит в начале, а не в конце. И вот та особенность OpenSSH, которой почти нет ни в одном другом конфигурационном языке: побеждает первое найденное значение, а не последнее. То, что записано в файле внутри /etc/ssh/sshd_config.d/, читается раньше главного файла и перебивает любую более позднюю строку в sshd_config.

Cloud-init использует именно этот каталог. При первом запуске он пишет /etc/ssh/sshd_config.d/50-cloud-init.conf, и в зависимости от способа развёртывания там оказывается PasswordAuthentication yes. После этого вы можете сколько угодно раз вписывать PasswordAuthentication no в sshd_config: вход по паролю останется открытым. Сначала получите общую картину:

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

Отсюда следует главная рекомендация: не трогайте sshd_config вообще. Вместо этого создайте собственный файл, имя которого по алфавиту стоит перед всем, что складывает туда автоматика. Файлы читаются в отсортированном порядке, 01- идёт раньше 50-, и раз побеждает первое значение, cloud-init может потом переписывать свой файл сколько угодно, не отменяя вашу защиту. В этом отличие от широко распространённого совета создать 99-hardening.conf: такой файл проигрывает cloud-init, причём молча. Впрочем, та же механика может обернуться и против вас: файл с меньшим номером, например 00-cloud.conf, выиграет у вашего 01-, потому что OpenSSH берёт значение, прочитанное первым. Поэтому заглянуть в каталог стоит и после настройки.

Пишем правила защиты, с таймером отката в качестве страховки

Сначала постройте страховочную сеть. Следующая команда через десять минут автоматически удалит ваш новый файл и перезапустит SSH, если вы до тех пор её не отмените:

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'

Теперь сама конфигурация:

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

Две строки заслуживают пояснения. KbdInteractiveAuthentication no здесь не украшение: если оставить yes, PAM по-прежнему сможет предложить запрос пароля обходным путём через интерактивный ввод с клавиатуры, хотя PasswordAuthentication и стоит в no. Именно поэтому встречаются серверы, которые при отключённом входе по паролю всё равно спрашивают пароль.

И PermitRootLogin prohibit-password вместо no: так root-доступ по ключу сохраняется, а пароли для root исключены. Если вы завели отдельного пользователя с sudo и достоверно проверили вход по ключу для него, поставьте вместо этого PermitRootLogin no. Но не раньше.

Чего брать не стоит, хотя это и встречается в старых руководствах: ChallengeResponseAuthentication. Начиная с OpenSSH 8.7 эта опция всего лишь устаревший псевдоним для KbdInteractiveAuthentication, и в актуальных версиях она порождает в журнале сообщение об устаревании. Просто не используйте её.

Проверка синтаксиса перед активацией, без исключений:

sudo sshd -t

Пустой вывод означает: файл синтаксически в порядке. Он не означает, что по содержанию файл делает то, что вы хотите. За это отвечает sudo sshd -T чуть ниже.

Если проверка вместо этого сообщает Missing privilege separation directory: /run/sshd, значит sshd с момента запуска системы не стартовал ни разу, ведь этот каталог создаётся только вместе с ssh.service. Касается это прежде всего Ubuntu 24.04 с активацией через сокет и устраняется командой sudo mkdir -p /run/sshd или sudo systemctl start ssh.service; на сервере, где вы прямо сейчас работаете по SSH, такого всё равно не бывает.

Активация: ssh.service, ssh.socket и различия между дистрибутивами

Здесь четыре системы расходятся, и старая привычка systemctl reload ssh оказывается неверным ответом везде, где порт держит ssh.socket.

Ubuntu делает ставку на активацию через сокет начиная с 22.10, а Ubuntu 24.04 поставляет её по умолчанию: там ssh.socket включён, а ssh.service отключён. Debian с завода поступает ровно наоборот. Debian 13 и Debian 12 поставляются с включённым ssh.service и отключённым ssh.socket, вопреки распространённому мнению, будто Debian 13 при чистой установке переходит на сокет. При активации через сокет на порту 22 слушает сам systemd и запускает свежий sshd только при входящем соединении. У этого есть приятный побочный эффект: изменения в sshd_config и так вступают в силу при следующем подключении, потому что каждый процесс соединения перечитывает конфигурацию заново. Перечитывание конфигурации нужно только для настроек, которые касаются самого слушателя, то есть Port и ListenAddress.

Поэтому не гадайте, а спросите систему, какой юнит несёт службу именно у вас:

systemctl is-enabled ssh.socket ssh.service
Системаssh.socketssh.serviceОсобенность
Ubuntu 24.04 LTSenableddisabledактивация через сокет по умолчанию, ListenStream на 0.0.0.0:22 и [::]:22
Debian 13 (trixie)disabledenabledAccept=no
Debian 12 (bookworm)disabledenabledAccept=no
Ubuntu 22.04 LTS (а также Debian 11)disabledenabledAccept=yes, то есть отдельный процесс на каждое соединение через ssh@.service

И наконец команда, которая верна на всех четырёх системах:

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

try-restart перезапускает юнит только тогда, когда тот действительно активен, а второй оставляет нетронутым. Гадать не приходится. Обе части требуют root: sshd лежит в /usr/sbin, а в Debian этот каталог не входит в PATH обычного пользователя, да и try-restart в любом случае обращается к systemd. Без sudo вызов завершается, в зависимости от системы, сообщением sshd: command not found или sshd: no hostkeys available, потому что ключи хоста в /etc/ssh доступны для чтения только root.

Столь же сознательно не применяется и отдельный systemctl restart ssh.socket, хотя эта команда и встречается во многих руководствах. На Debian 13 и Debian 12 она обрывается сообщением Job failed. See journalctl -xe for details., а в журнале к этому добавляется ssh.socket: Socket service ssh.service already active, refusing. Новая конфигурация при этом не становится активной, контрольная проверка по-прежнему выдаёт Permission denied (publickey,password). На Ubuntu 22.04 и Debian 11 команда проходит без сообщения об ошибке, но останавливает ssh.service и переводит хост на активацию через сокет. Поскольку ssh.socket там остаётся отключённым, при следующей перезагрузке снова поднимается ssh.service, то есть режим работы незаметно переключается туда и обратно. try-restart сразу для обоих юнитов избавляет и от того, и от другого.

Сознательно не применяется и reload: на системе, где порт держит ssh.socket, команда systemctl reload ssh отвечает так:

fatal: Cannot bind any address.

После этого служба находится в состоянии ошибки и уже отпустила свой порт. У перезапуска такой проблемы нет, и существующие сеансы он точно так же не разрывает.

Если вы хотите перенести порт, начинается следующее различие между дистрибутивами. Ubuntu 24.04 создаёт конфигурацию сокета через генератор systemd из sshd_config, там после смены порта достаточно:

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

Если же в Debian вы сознательно перешли на ssh.socket, порт задаётся в самом юните, и правка в sshd_config остаётся без эффекта. Он прописывается файлом-дополнением через sudo systemctl edit ssh.socket: пустая строка ListenStream= для сброса и вторая строка с новым значением. Результат потом проверяйте по реальности, а не по конфигурации:

sudo ss -tlnp

Доказательство: sshd -T и контрольная проверка

Команда, отработавшая без ошибок, ещё не доказательство. Доказательство — это итоговая развёрнутая конфигурация, в которой уже учтены все подключённые файлы:

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

Ожидается вывод примерно такого вида:

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

Если там несмотря на ваш файл значится passwordauthentication yes, то в каталоге лежит файл, который по алфавиту стоит раньше вашего. Возвращайтесь к разделу о порядке чтения.

Для проверки одного параметра хватит той же команды с более узким фильтром:

sudo sshd -T | grep -i passwordauthentication

За sudo впереди говорят две причины. Во-первых, sshd -T читает ключи хоста, а они в /etc/ssh доступны для чтения только root. Во-вторых, сам sshd лежит в /usr/sbin, и в Debian этот каталог не входит в PATH обычного пользователя, поэтому вызов без sudo там заканчивается сообщением sshd: command not found. Кто хочет обойтись без sudo, пишет полный путь /usr/sbin/sshd.

Начиная с OpenSSH 9.3, то есть на Ubuntu 24.04 (9.6p1) и Debian 13 (10.0p2), дополнительно есть sshd -G. Эта опция разбирает ту же конфигурацию, но не требует ни читаемых ключей хоста, ни существующего /run/sshd, что делает её пригодной для проверок в автоматизации и в контейнерах. На Debian 12 (9.2p1), Ubuntu 22.04 (8.9p1) и Debian 11 (8.4p1) этой опции ещё нет, там sshd отвергает её как неизвестную. Поэтому для руководства, которое действует везде, правильным выбором остаётся sudo sshd -T.

Теперь контрольная проверка, причём из нового терминала, пока старый сеанс остаётся открытым. Принудительно затребуйте вход по паролю и отключите ключи для этой попытки:

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

Правильно, если вас отвергают сразу и без единого запроса пароля:

root@203.0.113.10: Permission denied (publickey).

Решает содержимое скобок. Если там стоит (publickey,password) или появляется запрос пароля, вход по паролю всё ещё открыт. Только когда контрольная проверка чисто проваливается, а обычное соединение по ключу по-прежнему проходит, закрывайте старый сеанс и останавливайте таймер отката:

sudo systemctl stop ssh-rollback.timer

Сообщения об ошибках дословно

Permission denied (publickey). Сервер принимает только ключи, а ваш не подходит. Запустите ssh -v и посмотрите, какой файл вообще был предложен. Самые частые причины: неверное имя пользователя, ключ в authorized_keys не того пользователя, либо вы закрыли root, но по-прежнему входите как root.

Authentication refused: bad ownership or modes for directory /home/hani/.ssh Эта строка появляется не у вас на экране, а в журнале сервера, увидеть её можно через sudo journalctl -u ssh -n 50 --no-pager. Не фильтруйте при этом по -t sshd: начиная с OpenSSH 9.8, то есть в Debian 13 прямо из коробки, сеансы работают в отдельном процессе sshd-session, и под тегом sshd остаются только сообщения слушателя, но ни одной попытки входа. Кто хочет фильтровать по тегам, берёт sudo journalctl -t sshd -t sshd-session -n 50 --no-pager. OpenSSH отклоняет ключи, если домашний каталог, .ssh или authorized_keys доступны на запись группе или всем остальным. Исправление:

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

WARNING: UNPROTECTED PRIVATE KEY FILE! или же Load key "/home/hani/.ssh/id_ed25519": bad permissions. Та же проблема на стороне клиента. Решает её chmod 600 ~/.ssh/id_ed25519.

Too many authentication failures Ваш агент по очереди предлагает все загруженные ключи и превышает при этом MaxAuthTries. Ограничьте соединение одним-единственным ключом: ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 hani@203.0.113.10.

sign_and_send_pubkey: no mutual signature supported Старый RSA-ключ с подписью SHA-1 попадает на сервер, который её больше не принимает. Создайте ключ ed25519, вместо того чтобы крутить PubkeyAcceptedAlgorithms.

Bad owner or permissions on C:\Users\hani\.ssh\config Клиент под Windows проверяет права доступа к своему конфигурационному файлу. В свойствах файла на вкладке безопасности отключите наследование и удалите все записи, кроме вашей учётной записи и SYSTEM.

WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! Ключ хоста у сервера отличается от прошлого раза. После переустановки это ожидаемо, в остальных случаях нет. Удаляйте старую запись прицельно командой ssh-keygen -R 203.0.113.10, и только если вы знаете причину.

Доступ потерян: путь через консоль в личном кабинете

Если оба сеанса потеряны и ни одно соединение больше не проходит, это не потеря данных, а обходной путь. У каждого KVM root-сервера и каждого выделенного сервера в KernelHost есть в личном кабинете консоль, которая подключена к экранному выводу системы и не зависит от сетевого стека сервера. То есть вы попадёте внутрь даже тогда, когда SSH уже вообще не слушает.

  1. Войдите в личный кабинет, откройте нужный сервер и запустите консоль.
  2. В приглашении на вход авторизуйтесь как root с паролем, который был выдан при развёртывании. Вход через консоль идёт не по SSH, и PermitRootLogin его не затрагивает.
  3. Откатите изменение: sudo rm /etc/ssh/sshd_config.d/01-hardening.conf
  4. Проверьте и перезапустите: sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service
  5. Если SSH не работает совсем, помогут sudo systemctl status ssh.socket ssh.service и взгляд в sudo journalctl -u ssh -n 50 --no-pager. Код возврата 3 у status означает лишь, что один из двух юнитов неактивен; в Debian с отключённым ssh.socket это нормальная ситуация.

Две меры предосторожности почти всегда избавят вас от этого пути. Запишите пароль root, прежде чем отключать вход по паролю, потому что консоли он понадобится. И проверьте активный файрвол, прежде чем переносить порт SSH. Смена порта без соответствующего разрешающего правила отрезает доступ так же надёжно, как сломанный sshd_config, но выглядит иначе: вместо Permission denied вы получите Connection timed out.

Кратко

  • Держите второй сеанс открытым, пока третье, новое соединение не заработает доказуемо.
  • ed25519 с -a 100 и парольной фразой, открытый ключ через ssh-copy-id, в Windows через конвейер.
  • Проверьте вход по ключу, прежде чем отключать вход по паролю.
  • Правила защиты пишите в /etc/ssh/sshd_config.d/01-hardening.conf, а не в sshd_config. Побеждает первое найденное значение, отсюда и низкий номер.
  • Не забудьте KbdInteractiveAuthentication no, иначе обходной путь через PAM останется открытым.
  • Активируйте через sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service, ни через reload, ни через отдельный restart ssh.socket.
  • Заранее выясните командой systemctl is-enabled ssh.socket ssh.service, какой юнит вообще активен. Ubuntu 24.04 использует сокет, Debian 13 и Debian 12 используют службу.
  • Доказательство даёт sudo sshd -T вместе с принудительной попыткой входа по паролю из нового сеанса.
  • Аварийный путь: консоль в личном кабинете, для неё держите наготове пароль root.

Следующие очевидные слои защиты описаны в наших статьях о fail2ban и о файрволе UFW. Но важнее их обоих то, что вы только что сделали.

Частые вопросы

Почему вход по паролю остаётся активным несмотря на PasswordAuthentication no?
Почти всегда из-за файла в /etc/ssh/sshd_config.d/, чаще всего 50-cloud-init.conf. В Debian и Ubuntu строка Include стоит в начале sshd_config, а OpenSSH берёт первое найденное значение, а не последнее. Значит, то, что лежит в каталоге, побеждает главный файл. Проверьте командой sudo sshd -T, что действует на самом деле, и создайте собственный файл под именем 01-hardening.conf, чтобы он читался раньше всех автоматически созданных файлов.
Нужно ли перезапускать службу после изменения sshd_config?
На Debian 13, Debian 12 и Ubuntu 22.04 да, там с завода включён ssh.service, а ssh.socket отключён. На Ubuntu 24.04 systemd по умолчанию слушает через ssh.socket и запускает на каждое соединение новый sshd, который и так перечитывает конфигурацию. Самого слушателя касаются только Port и ListenAddress. Какой юнит активен у вас, покажет systemctl is-enabled ssh.socket ssh.service. Команда sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service верна на всех четырёх системах, а вот отдельный systemctl restart ssh.socket нет: на Debian 13 и Debian 12 он завершается ошибкой и новая конфигурация не становится активной, а на Ubuntu 22.04 он незаметно переводит хост на активацию через сокет.
Почему стоит избегать reload?
На системах с активным ssh.socket, то есть по умолчанию на Ubuntu 24.04, команда systemctl reload ssh обрывается сообщением fatal: Cannot bind any address, служба переходит в состояние ошибки и освобождает свой порт. У перезапуска такой проблемы нет, и существующие сеансы он тоже не разрывает, потому что активные соединения обслуживаются собственными дочерними процессами.
Как перенести ключ на сервер из Windows, если ssh-copy-id там нет?
Через конвейер: type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh пользователь@сервер "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys". Проследите, чтобы ключ был снабжён комментарием через -C, иначе дописанный PowerShell символ возврата каретки может повредить блок Base64. На сервере это исправляет sed -i 's/\r$//' ~/.ssh/authorized_keys.
Как доказать, что вход по паролю действительно закрыт?
В два шага. Во-первых, sudo sshd -T выводит итоговую развёрнутую конфигурацию, и в ней должно стоять passwordauthentication no, а также kbdinteractiveauthentication no. Во-вторых, контрольная проверка из нового сеанса: ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password,keyboard-interactive пользователь@сервер должна сразу отклоняться с сообщением Permission denied (publickey). Если в скобках стоит publickey,password, доступ ещё открыт.
Что делать, если я закрыл доступ самому себе?
Войдите в личный кабинет, откройте сервер и запустите консоль. Она подключена к экранному выводу системы и не зависит от SSH. Там авторизуйтесь как root, удалите свой файл командой sudo rm /etc/ssh/sshd_config.d/01-hardening.conf, проверьте через sudo sshd -t и перезапустите через sudo systemctl try-restart ssh.socket ssh.service. Пароль root держите наготове ещё до того, как отключите вход по паролю.
Что лучше выбрать: PermitRootLogin no или prohibit-password?
prohibit-password по-прежнему разрешает root вход по ключу и исключает только пароли, что сохраняет доступ при ошибках в конфигурации. Вариант no строже, но требует, чтобы существовал второй пользователь с sudo и чтобы вход по ключу для него уже доказуемо работал. Переключайтесь только после того, как этот тест пройден.

SSH Безопасность серверов Linux Debian Ubuntu OpenSSH ed25519 systemd cloud-init Руководство