На свежем VPS первые попытки войти под root появляются в логах через считанные часы после выдачи IP, иногда через минуты. Боты идут по диапазонам адресов подряд и гоняют словари, ничего не зная ни про тебя, ни про то, что на сервере стоит. Пока вход по паролю включён, всё держится на том, насколько этот пароль хорош и не засветился ли он в какой-нибудь чужой утечке. Ключ убирает зависимость от угадываемого секрета: перебор нормального современного ключа практически нереализуем, и фоновый долбёж превращается в строчки auth.log, которые можно не читать.
Ниже сначала рабочая последовательность из трёх команд, а потом разбор: где ключ лежит на сервере, почему он иногда не принимается при внешне правильной настройке, как выключить вход по паролю и не запереть себя снаружи.
Три команды, которые закрывают вопрос
Ключ генерируется на твоём компьютере, не на сервере. На этом спотыкаются чаще всего: человек заходит по паролю на VPS, набирает там ssh-keygen и дальше не понимает, что делать с полученными файлами. Приватная половина пары должна лежать у тебя и не покидать машину, с которой ты подключаешься.
Команда одинаковая в терминале Linux, в macOS и в PowerShell на Windows 10/11 (клиент OpenSSH входит в штатную поставку, но если ssh в консоли не находится, его доставляют через «Дополнительные компоненты» системы):
ssh-keygen -t ed25519 -C "alex@thinkpad" На вопрос о пути жми Enter, если не хочешь держать несколько ключей. Дальше спросят passphrase – введи её, к этому вернёмся отдельно. На выходе два файла: ~/.ssh/id_ed25519 (приватный, секрет) и ~/.ssh/id_ed25519.pub (публичный, его и отдают серверу или вставляют в панель провайдера, секретом он не является). В Windows это C:\Users\Имя\.ssh\. Комментарий после -C попадает в конец публичного ключа и потом сильно упрощает жизнь, когда на сервере накопится пять строк и надо понять, чьи они.
Дальше ключ надо положить на сервер:
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@203.0.113.10 Пароль у тебя спросят в последний раз. Утилита сама создаст каталог ~/.ssh, допишет строку в authorized_keys и выставит права. В штатной виндовой сборке OpenSSH ssh-copy-id обычно отсутствует, поэтому то же самое делается вручную одной строкой:
Get-Content -Raw $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" Проверка – обязательный шаг, а не формальность:
ssh -i ~/.ssh/id_ed25519 root@203.0.113.10 Если спросили passphrase и пустили внутрь, всё в порядке: это локальный пароль от файла ключа, сервер его не видит. Если снова спросили пароль от сервера, ключ не принят, и лезть в настройки sshd пока рано – сначала раздел про разбор ошибок.
У многих провайдеров публичный ключ можно вставить прямо в панели при создании виртуального сервера. Тогда cloud-init положит его в authorized_keys ещё до первой загрузки, и пароль не понадобится вовсе. Ловушка в том, что ключ достанется только root (или дефолтному пользователю образа, вроде ubuntu), а обычный пользователь, которого ты создашь позже по-человечески, приедет пустым. Это и есть классическое «вчера заходил, сегодня просит пароль»: зашёл-то ты уже под новым пользователем. Про то, как вообще заводить пользователей и раздавать им sudo, есть отдельный разбор, а базовая первичная настройка сервера описана в статье про настройку VPS для новичков.
Что на самом деле происходит при входе
Приватный ключ по сети не передаётся никогда. Сервер отправляет клиенту случайные данные, клиент подписывает их приватным ключом, сервер проверяет подпись публичным, который лежит у него в authorized_keys. По проводу уходит только подпись, и восстановить из неё ключ нельзя.
Разница с паролем вылезает в неприятном сценарии. Если сервер, куда ты ходишь, уже кем-то захвачен, набранный тобой пароль у этого кого-то есть. Ключ не будет: подпись одноразовая, она годится ровно для этой сессии и больше ни для чего.
Ed25519, RSA и ошибка, которая пугает людей
Выбор типа ключа выглядит сложнее, чем есть.
| Тип | Когда брать |
|---|---|
ed25519 | По умолчанию для всего: компактный, быстрый, поддерживается везде, где OpenSSH новее 2014 года. |
rsa 3072 или 4096 | Ради совместимости, и 3072 тут вполне нормальный размер: старые панели управления, сетевое железо, древние Java-клиенты, самописные деплой-скрипты на чужих библиотеках. |
ecdsa | Работает, но причин выбирать его вместо ed25519 практически нет. |
dsa | Мёртв. Из OpenSSH его выпилили совсем, если встретишь такой ключ в старом проекте – это повод его заменить, а не переносить. |
С версии 9.5 (осень 2023-го) ssh-keygen без аргументов делает ed25519, до этого делал RSA. Встроенный в Windows OpenSSH нередко отстаёт от свежего апстрима на несколько версий, так что там можно получить RSA, даже не заметив. Проще всегда писать -t ed25519 явно и не гадать, какая у тебя сборка.
Теперь про ошибку, из-за которой люди выбрасывают рабочие ключи. Выглядит она так:
sign_and_send_pubkey: no mutual signature supported Появляется при подключении старым RSA-ключом к обновлённому серверу. В OpenSSH 8.8 по умолчанию отключили схему подписи ssh-rsa, которая построена на SHA-1, и с тех пор дистрибутивы постепенно разносят это изменение по релизам. Ключ при этом целый, дело в алгоритме подписи: тот же самый RSA-ключ прекрасно работает с rsa-sha2-256 и rsa-sha2-512, если обе стороны их умеют. Правильное лечение – обновить клиент. Если же подключаться надо чем-то совсем древним, что заменить прямо сейчас нельзя, схему можно вернуть временно: на клиенте разовым ключом ssh -o PubkeyAcceptedAlgorithms=+ssh-rsa user@server, на сервере строкой PubkeyAcceptedAlgorithms +ssh-rsa в sshd_config. Именно временно: это откат к подписи на SHA-1, а не решение. Ключи, к слову, не «протухают» сами по себе – ни срока действия, ни счётчика использований у них нет, и эта их особенность позже вылезет ещё раз, уже с другой стороны.
Одна строка в authorized_keys и права, которые её ломают
На сервере публичный ключ живёт в файле ~/.ssh/authorized_keys того пользователя, под которым ты входишь. Одна строка – один ключ, перенос строки внутри строки всё ломает. Если копируешь ключ мышкой через панель провайдера или через веб-консоль, проверяй, не разорвало ли его на две части: сервер в этом случае честно скажет «ключ не подошёл», не уточняя, что в файле лежит мусор.
Дальше права, и вот здесь ломается больше всего входов. Демон sshd по умолчанию работает в режиме StrictModes yes: он отказывается читать ключи, если домашний каталог, каталог .ssh или сам файл доступны на запись кому-то кроме владельца. Логика понятная (иначе сосед по системе допишет туда свой ключ), но диагностика не очевидная: клиент видит обычный отказ, а настоящая причина остаётся в серверном логе строкой вроде Authentication refused: bad ownership or modes for directory /home/alex/.ssh.
Правильный набор такой:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R alex:alex /home/alex/.ssh Два самых частых способа это сломать. Первый: файл создавали через sudo, он остался принадлежать root, а пользователь его прочитать не может. Второй встречается на серверах с сайтами постоянно – кто-то поставил chmod 777 на домашний каталог, чтобы веб-сервер смог что-то туда записать, и вход по ключу отвалился в ту же секунду. Связь между «дал права на папку сайта» и «перестал пускать ключ» на глаз не видна вообще, а причина именно в этом. Если тема прав в Linux пока мутная, есть разбор chmod и chown простыми словами.
Passphrase и агент: как не вводить её сто раз в день
Passphrase шифрует файл приватного ключа на диске. Она не мешает краже файла, но не даёт воспользоваться копией сразу: сначала придётся подобрать саму passphrase, а это уже зависит от того, насколько она хороша. Сценарий, ради которого это придумано, довольно приземлённый: не «хакеры», а потерянный ноутбук, чужая флешка с бэкапом домашнего каталога, синхронизация папки в облако по ошибке. Ключ без пароля в такой ситуации – готовый доступ ко всем твоим серверам.
Чтобы не вводить её при каждом подключении, есть ssh-agent: он держит расшифрованный ключ в памяти до перезагрузки или до конца сессии. В Linux и macOS достаточно один раз добавить ключ:
ssh-add ~/.ssh/id_ed25519 В macOS удобнее сохранить passphrase в связке ключей: ssh-add --apple-use-keychain ~/.ssh/id_ed25519, и тогда после перезагрузки повторять не придётся. В Windows агент оформлен службой и по умолчанию выключен:
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519 Тут стоит понимать размен: виндовый агент кладёт ключ в реестр под защитой учётной записи, то есть после добавления passphrase у тебя спрашивать больше не будут вообще, и защита сводится к тому, насколько хорошо заперт сам аккаунт Windows.
Отдельная мелочь, которая экономит нервы при нескольких ключах. Когда в агенте лежит десяток ключей, клиент предлагает их серверу по очереди, а sshd по умолчанию разрывает соединение после шести неудачных попыток. Получается абсурдная ошибка Too many authentication failures при наличии верного ключа, который просто не успел дойти до очереди. Лечится явным указанием в ~/.ssh/config:
Host myvps
HostName 203.0.113.10
User alex
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
AddKeysToAgent yes IdentitiesOnly yes запрещает клиенту перебирать всё подряд и заставляет использовать указанный ключ. Дальше вход выглядит как ssh myvps, про остальные возможности этого файла было в статье про подключение к VPS по SSH.
Выключаем вход по паролю
Ключ полезен и сам по себе: пароль уводят фишингом, вытаскивают из чужой утечки, подсматривают в чужом терминале, а с ключом ничего этого не сделать. Но пока парольная аутентификация включена, канал для перебора остаётся открытым – сколько бы ключей ты ни добавил. Отключение пароля закрывает именно его.
Порядок действий, отступать от которого не надо. Держи открытой текущую сессию, в которой ты уже вошёл. Все проверки делай из второго окна терминала. Если что-то пойдёт не так, первая сессия останется живой и позволит откатить конфиг.
Правки идут в /etc/ssh/sshd_config:
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password Третья строка нужна не для красоты. Через PAM пароль умеет приезжать вторым путём, клавиатурно-интерактивным, и на части систем отключение одного только PasswordAuthentication оставляет дырку, в которую по-прежнему спрашивают пароль. На старых конфигах эта директива называлась ChallengeResponseAuthentication, так что если увидишь в файле её, правь то, что есть.
Теперь главное место, где новички теряют вечер. В современных сборках Debian, Ubuntu и AlmaLinux в начале sshd_config стоит строка Include /etc/ssh/sshd_config.d/*.conf, а в самом sshd действует непривычное правило: для большинства параметров выигрывает первое встреченное значение, а не последнее. То есть подключённый в начале файл перебивает всё, что ты напишешь ниже своими руками. На облачных образах там нередко оказывается 50-cloud-init.conf, и если в нём стоит PasswordAuthentication yes, парольный вход останется живым при идеально отредактированном основном конфиге. Проверяется это не глазами, а так:
sudo sshd -T | grep -iE 'passwordauth|pubkeyauth|permitroot' Ключ -T показывает итоговую конфигурацию, какой её видит демон, со всеми подключёнными файлами. Если видишь passwordauthentication yes после всех правок, смотри в /etc/ssh/sshd_config.d/. Чинить лучше не удалением чужого файла (его вернёт обновление пакета), а своим файлом с именем, которое сортируется раньше: /etc/ssh/sshd_config.d/00-hardening.conf с нужными строками.
Перед применением проверь синтаксис: sudo sshd -t молчит, если всё в порядке, и ругается с номером строки, если нет. Дальше лучше не restart, а sudo systemctl reload ssh (в RHEL-семействе служба называется sshd): демон перечитает конфиг, не трогая уже открытые сессии, включая твою. Если службы в системе просто нет запущенной, смотри следующий абзац – перезагружать там нечего.
Кстати, про перезапуск. Начиная с Ubuntu 22.10 демон по умолчанию поднимается через systemd-сокет: соединение принимает ssh.socket, и уже он запускает sshd на каждое подключение. Постоянно работающей службы при этом может не быть, а конфиг перечитывается сам собой при следующем входе, так что привычной паузы «ошибся, но restart ещё не делал» здесь нет. Там же прячется причина, по которой смена порта в sshd_config иногда не даёт эффекта: в выпусках примерно с 22.10 по 24.10, включая ходовую сейчас 24.04 LTS, порт задаётся в сокете, и править его надо через systemctl edit ssh.socket. В более свежих сборках (openssh 9.9, Ubuntu 25.04 и новее) добавили генератор, который вытаскивает порт и адрес из sshd_config самостоятельно. Что именно у тебя, показывают systemctl status ssh.socket и ss -tlnp | grep ssh. Про порт, ограничение по IP и прочую обвязку – в статье про защиту SSH.
Страховка на случай, если всё-таки заперся: у любого нормального провайдера в панели есть VNC-консоль или аварийный режим, они работают мимо SSH. Как этим пользоваться, разобрано в материале про восстановление доступа к VPS. Проверить, что консоль вообще открывается и ты помнишь пароль root, стоит до того, как правишь конфиг, а не после.
И нет, отключение пароля не отменяет Fail2ban и файрвол. Брутфорс станет бессмысленным, но боты продолжат стучаться, забивая логи и отъедая соединения, а кроме SSH на сервере обычно торчит ещё что-нибудь.
PuTTY, .ppk и прочая Windows-специфика
PuTTY хранит ключи в собственном формате .ppk, который OpenSSH не понимает, и наоборот. Конвертация делается в PuTTYgen через меню Conversions: импортировать существующий ключ и сохранить в нужную сторону.
Тут же живёт ошибка, которую делают почти все, кто генерирует ключ в PuTTYgen. Кнопка «Save public key» сохраняет файл в формате RFC 4716: несколько строк, заголовок, комментарии. Вставить его в authorized_keys нельзя, сервер такую запись не примет. Нужен текст из поля в верхней части окна, подписанного «Public key for pasting into OpenSSH authorized_keys file» – одна длинная строка, начинающаяся с ssh-ed25519 или ssh-rsa. Роль агента в мире PuTTY играет Pageant.
Ещё одна засада на Windows связана не с SSH, а с PowerShell: перенаправление вывода через > в старых версиях пишет файл в UTF-16, и такой «публичный ключ» на сервере выглядит нечитаемым бинарным мусором. Поэтому в примере выше вывод идёт в конвейер, а не в файл.
Ключ не принимается: куда смотреть
Речь здесь про случай, когда соединение до сервера доходит, а ключ не принимают. Если не доходит вообще – таймаут, Connection refused, обрыв до приглашения, красная простыня про сменившийся ключ хоста, – это другая ветка, разобранная по симптомам в статье про подключение к VPS по SSH.
Сообщения клиента по умолчанию бесполезны, зато подробный режим показывает почти всё:
ssh -vvv -i ~/.ssh/id_ed25519 alex@203.0.113.10 В потоке ищи три вещи. Offering public key означает, что клиент вообще предложил твой ключ (если такой строки нет – проблема на твоей стороне, не на серверной). Server accepts key означает, что сервер ключ узнал. А перечисление Authentications that can continue: publickey,password показывает, какие способы сервер готов принять: если в списке остался password, парольный вход ещё жив.
Серверную сторону смотри в sudo journalctl -u ssh -n 50 (в RHEL-семействе служба называется sshd) или в /var/log/auth.log, в AlmaLinux и Rocky – /var/log/secure. Именно там лежит настоящая причина отказа. Разбор логов вообще отдельная тема, ей посвящён материал про работу с логами сервера.
Короткий список того, что обнаруживается в девяти случаях из десяти:
- ключ попал не тому пользователю (лежит у root, а входишь под собой);
- права на
~/.sshили домашний каталог слишком широкие, либо каталог принадлежит root; - строка в
authorized_keysразорвана переносом при копировании; - на сервере в файле лежит не публичный ключ, а содержимое приватного (бывает чаще, чем кажется);
- клиент предлагает не тот ключ из агента, помогает
-iплюсIdentitiesOnly yes; - на стороне клиента права на файл ключа слишком свободные, тогда ssh прямо скажет
Permissions 0644 for 'id_ed25519' are too open; в Linux лечитсяchmod 600, в Windows – через свойства файла, убрать всех кроме своей учётки.
Ключи для скриптов: бэкапы, деплой, CI
Момент, когда правила меняются. Скрипту, который ночью по cron забирает дамп базы на другой сервер, passphrase ввести некому, поэтому его ключ делается без пароля. Это нормально, но такой ключ надо ограничить, иначе утечка файла с машины, где крутится задание, отдаёт полный доступ к серверу.
Ограничения ставятся прямо в authorized_keys, в начале строки:
restrict,from="203.0.113.5",command="/usr/local/bin/backup-receive" ssh-ed25519 AAAAC3Nz...5Hk backup@node1 Здесь restrict отключает всё лишнее: проброс портов, агента, X11, выделение терминала. Параметр from пускает этот ключ только с указанного адреса. А command жёстко задаёт, что будет выполнено при входе, независимо от того, что попросил клиент: даже если ключ утечёт, им можно будет запустить ровно один скрипт и ничего больше.
Есть и опция срока годности: expiry-time="20271231" в той же строке (дата в формате YYYYMMDD) заставит sshd перестать принимать ключ после этого дня. Встречается она в конфигах редко, хотя для временного доступа подрядчику подходит идеально – гораздо надёжнее, чем обещание себе «потом удалю».
И предостережение про проброс агента. Конструкция ssh -A удобна, когда с сервера надо сходить дальше по ключу, но она даёт root’у промежуточной машины возможность пользоваться твоим агентом, пока сессия открыта. На чужие или сомнительные серверы так ходить не стоит; безопаснее ProxyJump: ssh -J bastion.example.com target, где ключ остаётся у тебя, а промежуточный хост просто прокидывает трафик. Про то, как ключи используются при копировании файлов, есть разбор scp, sftp и rsync, а настройка регулярного вывоза бэкапов на другой сервер описана здесь.
Потеря ключа, ротация и лишние строки
Приватный ключ невосстановим. Не «сложно восстановить», а никак: если файл потерян, а второго доступа нет, остаётся консоль провайдера или аварийный режим. Отсюда простая привычка, которая стоит пяти минут: сгенерируй второй ключ с другого устройства (рабочий ноутбук и домашний компьютер, например) и положи оба в authorized_keys заранее. Резервной копии приватного ключа в облаке лучше избегать, второй независимый ключ решает ту же задачу без лишнего риска.
Поскольку срока жизни у ключей нет, файл authorized_keys на живом сервере имеет свойство обрастать. На серверах, которые кто-то администрирует несколько лет, регулярно обнаруживается четыре-пять строк, из которых опознаются одна-две: остальное – ноутбук, который сдали в ремонт в позапрошлом году, ключ подрядчика, разовый доступ для друга, который помогал с миграцией… Заглянуть туда и удалить лишнее стоит хотя бы раз в год, благо строка удаляется обычным текстовым редактором.
Ротация делается тем же порядком, что и первичная настройка: добавил новый ключ, проверил вход во втором окне, удалил старую строку. Вот здесь и вылезает граница возможностей обычных ключей: когда серверов десяток, а людей несколько, править файл на каждой машине становится мучением, и в этот момент смотрят в сторону SSH-сертификатов и центра сертификации. Для одного VPS это из пушки по воробьям, но знать, что такой инструмент есть, полезно.
Держать один ключ на все серверы или разводить по одному на проект – вопрос без единственно верного ответа. Один ключ проще в обслуживании и меньше шансов запутаться, отдельные ключи ограничивают радиус поражения при утечке. Разумный компромисс для небольшого хозяйства: один личный ключ на все свои серверы плюс отдельные ключи без пароля для автоматики, каждый со своими ограничениями в authorized_keys. Общая логика защиты сервера, частью которой это всё является, разложена в статье про базовые меры защиты VPS.
Источники
- ssh-keygen(1) – man-страница генератора ключей OpenSSH.
- sshd(8) – описание формата
authorized_keys, включая опцииrestrict,from,commandиexpiry-time. - sshd_config(5) – параметры серверного конфига.
- ssh_config(5) – параметры клиентского конфига, включая
IdentitiesOnlyиAddKeysToAgent. - OpenSSH 8.8 release notes – отключение подписей
ssh-rsaна SHA-1. - OpenSSH 9.5 release notes – переход
ssh-keygenна ed25519 по умолчанию. - OpenSSH release notes – история изменений проекта.
- Key-based authentication in OpenSSH for Windows – документация Microsoft по ключам и ssh-agent в Windows.
- SSHd now uses socket-based activation – объявление Ubuntu о переходе на сокет-активацию и о том, как это влияет на порт.
- PuTTY – официальный сайт клиента, PuTTYgen и Pageant.
- RFC 8709 – стандарт на использование Ed25519 в SSH.

