RE:NODE

VDS12 мин чтения

SSH-ключи и защита sshd_config: агент, jump host

Создайте ключ ed25519, безопасно установите его, используйте агент и jump host и усильте sshd_config так, чтобы не заблокировать себе доступ к серверу.

0 прочтений

Обычный случай закрывают три команды. ssh-keygen -t ed25519 -a 100 создаёт ключ. ssh-copy-id deploy@203.0.113.10 устанавливает его. PasswordAuthentication no в конфигурации сервера закрывает за вами дверь. Сделайте эти три вещи, и вашу машину больше нельзя взломать перебором паролей, а это убирает самую большую категорию атак на неё, потому что закрытый ключ никто не угадывает.

Всё остальное - о том, как сделать ключи удобными в жизни, а не сильнее: один ключ, который разблокируется раз в день, вместо парольной фразы, набранной сорок раз; файл конфигурации, превращающий длинную команду в короткое имя; jump host, который приводит к машинам без публичного адреса; и горстка настроек sshd_config, которые стоит менять, в отличие от сорока, которые переходят из статьи в статью без объяснений.

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

Какой тип ключа выбрать и почему ed25519#

ТипИспользоватьПримечания
ed25519Да, по умолчаниюМаленький, быстрый, нет параметров, в которых можно ошибиться
ed25519-skДля аппаратного токенаНужен ключ FIDO2 и OpenSSH 8.2 или новее
rsaТолько для совместимостиИспользуйте -b 4096. С точки зрения криптографии по-прежнему годится
ecdsaНет причин выбиратьРаботает, но ничто не рекомендует его перед ed25519
dsaНикогдаУстарел и удалён из современного OpenSSH

Открытый ключ ed25519 - одна короткая строка, закрытый - около 400 байт, а выбирать размер ключа не нужно, потому что его задаёт кривая. Флаг -b для ed25519 молча игнорируется, что иногда сбивает с толку тех, кто следует руководству по RSA.

Одна деталь про версии объясняет запутанный сбой. OpenSSH 8.8 по умолчанию отключил алгоритм подписи ssh-rsa, использующий SHA-1. Это алгоритм подписи, а не тип ключа: ключ RSA по-прежнему работает с современным сервером, пока обе стороны могут договориться о rsa-sha2-256 или rsa-sha2-512. На практике это значит, что очень старый клиент или очень старая настройка RSA может внезапно быть отвергнута обновлённым сервером, а ошибка винит ключ, а не алгоритм. Если вы с этим столкнулись, создайте ключ ed25519, а не включайте SHA-1 обратно.

Как правильно создать и установить ключ#

Создавайте ключ на той машине, за которой сидите, никогда не на сервере. Закрытый ключ, который побывал на сервере, - это закрытый ключ, который лежал на чужом компьютере.

bash
$ ssh-keygen -t ed25519 -a 100 -C "davit@laptop-2026"Generating public/private ed25519 key pair.Enter file in which to save the key (/home/davit/.ssh/id_ed25519):Enter passphrase (empty for no passphrase):

Три части этой строки стоит понять, а не копировать.

  • -a 100 задаёт число раундов, используемых для получения ключа, которым шифруется файл вашего закрытого ключа. Это делает перебор украденного файла ключа медленнее, а вам стоит доли секунды при разблокировке.
  • -C - комментарий. Впишите в него человека и машину. Через три года authorized_keys на общем сервере - это список комментариев и ничего больше, и вопрос «какой из этих пяти ключей принадлежит человеку, который ушёл» вам зададут обязательно.
  • Задайте парольную фразу. Ключ без неё - это файл с паролем, который может скопировать и использовать вечно любой, у кого есть доступ на чтение к вашему ноутбуку. Агент, о котором в следующем разделе, означает, что вводить её придётся раз в день.

Устанавливайте его через ssh-copy-id, который правильно выставляет права и дописывает файл:

bash
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@203.0.113.10

Если вход по паролю уже отключён и нужно добавить второй ключ, допишите его через сеанс, который у вас ещё открыт. Права важнее, чем ожидают, потому что sshd игнорирует файл authorized_keys, который считает небезопасным, и не даёт клиенту ни малейшего намёка почему:

ПутьРежимВладелец
/home/deploy755 или строже, без записи для группыdeploy
/home/deploy/.ssh700deploy
/home/deploy/.ssh/authorized_keys600deploy
~/.ssh/id_ed25519 (ваша машина)600вы

Когда ключ отвергается, а причину вы не видите, спросите сначала клиент, потом сервер:

bash
$ ssh -v deploy@203.0.113.10          # which keys were offered, what was accepted$ journalctl -u ssh -n 50             # on the server, with LogLevel VERBOSE

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

Агент, чтобы вводить парольную фразу один раз#

ssh-agent хранит расшифрованные закрытые ключи в памяти и подписывает запросы по требованию, поэтому парольная фраза вводится один раз за сеанс, а не при каждом подключении.

bash
$ eval "$(ssh-agent -s)"$ ssh-add ~/.ssh/id_ed25519$ ssh-add -l256 SHA256:5xM8n... davit@laptop-2026 (ED25519)

Большинство настольных систем запускают агент за вас. В macOS ssh-add --apple-use-keychain сохраняет парольную фразу в связке ключей. В Windows OpenSSH Authentication Agent - это служба, которую нужно настроить на автоматический запуск, прежде чем заработает ssh-add. Менеджеры паролей и gpg-agent тоже могут выступать агентами, что удобно и не меняет ни одного из правил ниже.

Две настройки в ~/.ssh/config делают это автоматическим:

~/.ssh/config
Host *    AddKeysToAgent yes    IdentitiesOnly yes    ServerAliveInterval 30    ServerAliveCountMax 3

IdentitiesOnly yes - самая важная, и она исправляет реальный сбой. Без неё клиент по очереди предлагает каждый ключ из агента, и каждое предложение засчитывается как попытка аутентификации. Если загружено пять ключей, а на сервере MaxAuthTries 3, вас отключают с сообщением «Too many authentication failures», хотя правильный ключ у вас есть. IdentitiesOnly говорит клиенту предлагать только тот ключ, который указан для этого хоста.

Проброс агента требует предупреждения. ssh -A открывает сокет вашего агента на удалённой машине, и любой с root-правами там может использовать его для аутентификации от вашего имени везде, что открывают ваши ключи. Не используйте его по привычке и никогда не направляйте на машину, которой вы не администрируете. Правильный инструмент, чтобы добраться до второй машины, - ProxyJump: он оставляет аутентификацию на вашем ноутбуке.

Файл конфигурации, который экономит больше всего времени#

~/.ssh/config превращает длинную команду в слово, и именно там живут хорошие привычки.

~/.ssh/config
Host vds    HostName 203.0.113.10    User deploy    IdentityFile ~/.ssh/id_ed25519Host db    HostName 10.0.0.20    User deploy    ProxyJump vdsHost *    AddKeysToAgent yes    IdentitiesOnly yes    ControlMaster auto    ControlPath ~/.ssh/cm-%r@%h:%p    ControlPersist 10m

Теперь работает ssh vds, а также scp file vds:/tmp/ и rsync -a ./site/ vds:/var/www/, потому что все они читают один и тот же файл.

ProxyJump - то, что стоит перенять уже сегодня. ssh db открывает соединение через vds к машине с частным адресом, при этом аутентификация на каждом шаге происходит на вашем ноутбуке, а ни ключ, ни сокет агента не попадают на промежуточный хост. Он заменяет все причины, по которым раньше использовали проброс агента.

ControlMaster переиспользует одно TCP-соединение для последующих сеансов на тот же хост, так что второй и третий ssh vds мгновенны и не проходят аутентификацию заново. Это большое улучшение удобства на медленном канале, и клиент Windows OpenSSH его не поддерживает. ControlPersist 10m держит общее соединение живым десять минут после закрытия последнего сеанса.

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

Усиление защиты sshd#

Эти настройки применяются на сервере. В Debian и Ubuntu учтите, что /etc/ssh/sshd_config начинается с Include /etc/ssh/sshd_config.d/*.conf, а sshd использует первое значение, которое получил для ключевого слова, поэтому файл в этом каталоге перекрывает основную конфигурацию ниже. Прежде чем что-либо писать, проверьте, что там уже есть, как описано в статье первый час на новом VDS.

ДирективаЗначениеЗачем
PasswordAuthenticationnoУбирает перебор как категорию
KbdInteractiveAuthenticationnoДругой способ, которым можно принять пароль
PermitRootLoginnoИли prohibit-password, если автоматизации нужен root
PubkeyAuthenticationyesЗначение по умолчанию, но стоит указать
AuthenticationMethodspublickeyДелает намерение явным и принудительным
AllowUsers или AllowGroupsВаши учётные записиБольше никто даже не сможет попытаться
MaxAuthTries3Меньше предложений за соединение
LoginGraceTime20Неаутентифицированные сокеты не задерживаются
X11ForwardingnoСерверу это ни к чему
PermitEmptyPasswordsnoЗначение по умолчанию, и в нём стоит быть уверенным
ClientAliveInterval300Убирает мёртвые сеансы, держащие блокировки
LogLevelVERBOSEЗаписывает отпечаток ключа, которым вошли

LogLevel VERBOSE недооценён. Он записывает, каким ключом прошла аутентификация, по отпечатку, при каждом входе. После инцидента эта единственная строка - разница между знанием, чьи учётные данные использовались, и догадками. О том, как её хранить, рассказывает статья журналы, которые стоит хранить, а о том, зачем она вам понадобится, - что делать, если ваш сервер взломали.

Блоки Match применяют настройки к подмножеству и действуют до конца файла или до следующего Match, поэтому размещайте их последними:

/etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication noPermitRootLogin noAllowGroups ssh-usersMatch Group sftp-only    ChrootDirectory /srv/sftp/%u    ForceCommand internal-sftp    AllowTcpForwarding no

Всегда проверяйте конфигурацию перед перезапуском и всегда из сеанса, который у вас уже есть:

bash
$ sshd -t && systemctl restart ssh

Чего делать не стоит: менять порт (сканер найдёт его за секунды, хотя журналы станут тише), отключать все шифры, о которых современный клиент и так договорится, или вставлять блок sysctl из сорока строк, который вы не можете объяснить. Что стоит добавить - fail2ban, если на машине что-то ещё принимает пароль, и firewall, разрешающий SSH только с адресов, которыми вы пользуетесь; синтаксис есть в руководстве по ufw.

Ограничение того, что может делать ключ#

Ключ не обязан быть универсальным входом. Опции в начале строки в authorized_keys действуют только на этот ключ, и именно так задание резервного копирования или deploy hook получает ровно тот доступ, который ему нужен.

~/.ssh/authorized_keys
restrict,from="203.0.113.0/24" ssh-ed25519 AAAAC3Nza... davit@laptop-2026restrict,command="/usr/local/bin/backup-receive" ssh-ed25519 AAAAC3Nza... backup@nas

Опции, которые стоит знать:

  • restrict (OpenSSH 7.2 и новее) сразу отключает всё необязательное: проброс портов, проброс агента, X11, tty и переменные окружения пользователя. Начинайте с него и добавляйте обратно только необходимое через pty или port-forwarding.
  • from="..." ограничивает ключ адресом или диапазоном источника. Для постоянного офиса или известного сервера это сильная и бесплатная мера.
  • command="..." принудительно запускает эту команду, что бы ни запросил клиент. Исходный запрос клиента доступен скрипту в SSH_ORIGINAL_COMMAND, и именно так строится ограниченный доступ по rsync или git.
  • expiry-time="20270101" (OpenSSH 8.2 и новее) прекращает действие ключа после даты. Полезно для подрядчика.

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

Ключи хоста, known_hosts и доверие к серверу#

Аутентификация взаимна. Сервер доказывает, кто он, ключом хоста, а ваш клиент запоминает его в ~/.ssh/known_hosts. Первое подключение - это доверие при первом использовании: вам показывают отпечаток и просят принять, и почти все нажимают yes, не глядя, отчего вся схема становится декоративной.

Сделайте это как следует один раз для каждой машины. На сервере, из консоли провайдера:

bash
$ ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub256 SHA256:5xM8nQ... root@web-01 (ED25519)

Сравните эту строку с тем, что клиент показывает при первом подключении. Это занимает десять секунд, и это единственный момент, когда проверка возможна.

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

bash
$ ssh-keygen -R 203.0.113.10

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

Двухфакторная аутентификация, сертификаты и когда они оправданы#

Существуют ещё два слоя, и большинству небольших систем они не нужны.

Двухфакторная аутентификация поверх ключа. AuthenticationMethods publickey,keyboard-interactive требует ключ, а затем второй фактор через PAM, как правило код TOTP. Для общей рабочей машины это действительно надёжнее. Но это ломает автоматические задания без присмотра, так что сочетайте его с блоком Match, исключающим учётную запись автоматизации, и убедитесь, что понимаете, что происходит при сбое модуля PAM, прежде чем на него полагаться.

SSH-сертификаты. Вместо копирования открытых ключей на каждый сервер центр сертификации подписывает краткоживущие пользовательские сертификаты, а серверы доверяют этому центру через TrustedUserCAKeys. Добавление и удаление людей становится решением о подписи, а не правкой на тридцати машинах, а сертификаты истекают сами.

bash
$ ssh-keygen -s ca_key -I davit -n deploy -V +8w ~/.ssh/id_ed25519.pub

Для одного человека с двумя серверами это лишние затраты без пользы. Начиная примерно с пяти человек или двадцати машин это становится очевидно правильным ответом, а точка пересечения - там, где правки authorized_keys начинают забываться.

Заблокировали себя: что действительно спасает#

В порядке того, как сильно вы пожалеете, что этого нет:

  1. Сеанс, который вы оставили открытым. Отмените изменение, перезапустите sshd, выдохните.
  2. `sshd -t` перед каждым перезапуском. Он ловит опечатки, а из них состоит большинство блокировок.
  3. Консоль провайдера или режим восстановления. У VDS с полным root-доступом он есть; он медленный, но работает. Выясните, где он, до того, как он понадобится, а не во время.
  4. Второй ключ со второй машины, установленный, пока всё работало. Телефон с клиентом SSH тоже подходит.
  5. Вторая учётная запись в `AllowUsers` с собственным ключом, используемая только для этого.

Управляемый игровой, прикладной или веб-сервер - совсем другая ситуация: там нет демона SSH, в котором можно заблокировать себя, доступ к файлам идёт по SFTP с учётными данными, выдаваемыми на каждый сервер, а консоль живёт в панели. Этот компромисс описан в статье VDS или игровая панель: VDS даёт вам все настройки из этой статьи и ответственность за каждую из них.

FAQ#

Нужна ли ключу парольная фраза, если этим ноутбуком пользуюсь только я?

Да. Парольная фраза защищает файл ключа, а не соединение, а копируют именно файл, когда ноутбук крадут или когда вредоносная программа ищет ~/.ssh. С агентом вы вводите её раз за сеанс, так что цена почти нулевая, а выгода в том, что украденный файл не сразу превращается в украденный сервер.

Можно ли использовать один ключ на нескольких серверах?

Да, это нормально и допустимо: закрытый ключ никогда не покидает вашу машину, а открытый по определению публичен. Используйте отдельный ключ на человека, а не на сервер. Отдельные ключи на каждый сервер становятся полезны, только когда вы хотите иметь возможность отозвать доступ к одной машине, не трогая остальные.

Что если я потеряю закрытый ключ?

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

Безопасно ли отправлять открытый ключ по почте или вставлять в чат?

Да. Открытый ключ предназначен для публикации; он лежит в authorized_keys на каждом сервере, которым вы пользуетесь. Никогда не должен покидать вашу машину файл без расширения .pub. Если закрытый ключ хоть раз был куда-то вставлен, считайте его сгоревшим и создайте новую пару.

Отключение входа по паролю заблокирует мои другие инструменты?

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

Нужен ли мне fail2ban при входе только по ключам?

Для SSH, строго говоря, нет. Неудачные попытки с ключом не могут увенчаться успехом, так что выгода в объёме журналов, а не в безопасности. Он по-прежнему оправдывает себя перед веб-входами, почтой и всем остальным, что принимает пароль, и оставлять его работающим почти ничего не стоит.


Комментарии

Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.

0/2000