Коротко. Вход по SSH-ключу заменяет пароль парой файлов: приватный ключ остаётся у вас, публичный кладётся в ~/.ssh/authorized_keys на сервере. Сгенерируйте ключ командой ssh-keygen -t ed25519, установите его через ssh-copy-id, выставите права 700 на каталог и 600 на файл, проверьте вход в новой сессии и только потом отключайте PasswordAuthentication.
Как работает SSH-подключение по ключу
Пароль — это общий секрет: он есть и у вас, и на сервере, и его можно подобрать перебором. SSH-ключ устроен иначе. Вы генерируете пару файлов:
id_ed25519— приватный ключ. Никогда не покидает вашу машину. Это и есть ваш доступ.id_ed25519.pub— публичный SSH-ключ. Его можно спокойно копировать, отправлять в чат, класть на десяток серверов.
При подключении сервер отправляет клиенту случайный вызов. Клиент подписывает его приватным ключом, сервер проверяет подпись публичным ключом из authorized_keys. Приватный ключ по сети не передаётся вообще — ни в каком виде. Поэтому перехват трафика, скомпрометированный сервер-посредник или утечка базы паролей хостера не дают злоумышленнику вашего доступа.
Практический эффект: боты, которые круглосуточно перебирают root:123456 на порту 22, перестают быть угрозой в принципе. Не «становятся менее вероятными» — они физически не могут подобрать 256-битный ключ.
Приватный ключ — это не «пароль от сервера», это сам доступ. Файл id_ed25519 нельзя пересылать в мессенджере, класть в общий диск, коммитить в репозиторий и оставлять в Docker-образе. Если он утёк — ключ отзывается целиком, а не «меняется пароль».

Какой SSH-ключ выбрать: ed25519 или RSA
Короткий ответ: ed25519. Он появился в OpenSSH 6.5 и с тех пор стал разумным значением по умолчанию. Причины:
- Стойкость. Ed25519 даёт примерно 128 бит безопасности при длине ключа 256 бит. Чтобы получить сопоставимый уровень на RSA, нужен ключ от 3072 бит.
- Размер. Публичный ed25519-ключ — одна короткая строка, влезающая в терминал. Публичный RSA 4096 — простыня на несколько строк, которую неудобно копировать вручную.
- Скорость. Подпись и проверка заметно быстрее, чем у RSA 4096. На массовых деплоях и CI это ощутимо.
- Меньше способов ошибиться. У ed25519 нет параметров, которые можно выбрать неправильно: нет выбора кривой, нет зависимости подписи от качества генератора случайных чисел на клиенте.
RSA имеет смысл держать во втором ключе только ради совместимости — например, если в контуре остались старые железки, сетевое оборудование или Git-хостинг, не понимающий Ed25519.
| Тип ключа | Стойкость | Размер публичного ключа | Поддержка | Вердикт |
|---|---|---|---|---|
| ed25519 | ~128 бит, современная кривая Curve25519 | Одна строка, ~80 символов | OpenSSH 6.5 и новее, GitHub, GitLab, все актуальные дистрибутивы | Выбор по умолчанию |
| ed25519-sk | То же плюс аппаратное подтверждение | Одна строка, чуть длиннее | Требует FIDO2-токена и свежего OpenSSH на обеих сторонах | Лучший вариант для админских ключей |
| RSA 4096 | Достаточная при длине от 3072 бит; 1024 и 2048 бит уже слабые | Несколько строк, сотни символов | Практически везде, включая легаси | Запасной вариант ради совместимости |
| ECDSA (nistp256/384/521) | Формально сопоставима с ed25519 | Короткий | Широкая | Работает, но выбирать заново незачем |
| DSA (ssh-dss) | Ограничен 1024 битами, считается устаревшим | Средний | Отключён по умолчанию начиная с OpenSSH 7.0, из свежих релизов вырезан | Не использовать |
Отдельная ловушка: алгоритм подписи ssh-rsa (на SHA-1) отключён по умолчанию в свежих версиях OpenSSH. Сам RSA-ключ при этом остаётся валидным — проблема именно в старом алгоритме подписи. Если после обновления клиента или сервера старый RSA-ключ внезапно перестал приниматься, это почти всегда он.
Генерация SSH-ключа: ssh-keygen шаг за шагом
Базовая команда для создания SSH-ключа:
ssh-keygen -t ed25519 -a 100 -C "alice@laptop-prod" -f ~/.ssh/id_ed25519_prod
Разбор флагов:
-t ed25519— тип ключа.-a 100— число раундов KDF при шифровании приватного ключа парольной фразой. Больше раундов — дороже перебор украденного файла. 100 — разумный компромисс, задержка при вводе фразы остаётся незаметной.-C "alice@laptop-prod"— комментарий. Он попадает в конец публичного ключа и вauthorized_keys. Через полгода именно по комментарию вы поймёте, чей это ключ и можно ли его удалить. Пишите туда человека и машину, а не «my key».-f ~/.ssh/id_ed25519_prod— путь. Отдельные имена под разные контуры удобнее, чем одинid_ed25519на всё.
Парольная фраза: ставить или нет
Ставить. Парольная фраза шифрует приватный ключ на диске. Без неё украденный ноутбук, забытая резервная копия домашнего каталога или доступ к синхронизированной папке дают злоумышленнику готовый вход на все ваши серверы.
Аргумент «неудобно вводить каждый раз» закрывается ssh-agent: фраза вводится один раз за сессию. Ключ без фразы допустим только для машинных сценариев — CI, деплой, бэкапы, — и такой ключ обязан быть отдельным и максимально ограниченным по правам.
Сменить или добавить фразу к существующему ключу можно без перевыпуска:
ssh-keygen -p -a 100 -f ~/.ssh/id_ed25519_prod
Посмотреть отпечаток ключа и убедиться, что вы работаете с тем самым файлом:
ssh-keygen -lf ~/.ssh/id_ed25519_prod.pub
# 256 SHA256:kx3Y... alice@laptop-prod (ED25519)
Как создать SSH-ключ в Windows
В Windows 10 и 11 есть встроенный OpenSSH-клиент, отдельный софт не нужен. В PowerShell работает ровно та же команда ssh-keygen -t ed25519 -C "...", ключи ложатся в C:\Users\Имя\.ssh. Агент запускается как служба Windows:
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
PuTTY — отдельная история. Он не понимает формат OpenSSH и использует собственный .ppk. Ключ генерируется в PuTTYgen, агент называется Pageant. Если ключ уже создан в OpenSSH-формате, его можно импортировать в PuTTYgen и сохранить как .ppk; обратная конвертация тоже доступна через экспорт в OpenSSH-формат. На сервер в любом случае кладётся публичная часть в однострочном виде ssh-ed25519 AAAA... комментарий — не содержимое окна PuTTYgen целиком со служебными заголовками.
Как добавить SSH-ключ на сервер
Простой путь — ssh-copy-id. Он сам создаст каталог, допишет ключ и выставит права:
ssh-copy-id -i ~/.ssh/id_ed25519_prod.pub -p 22 deploy@203.0.113.10
Указывайте .pub явно. Без -i утилита возьмёт ключи из агента, и на сервер может уехать не тот ключ, который вы имели в виду.
Если ssh-copy-id недоступен (типично для macOS без Homebrew и для Windows), тот же результат даёт одна команда:
cat ~/.ssh/id_ed25519_prod.pub | ssh deploy@203.0.113.10 \
"umask 077; mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
umask 077 здесь важен: он гарантирует, что созданные каталог и файл получат корректные права сразу, а не после отдельного chmod. Обратите внимание на >> — одинарный > затрёт уже существующие ключи других сотрудников.
Ручная установка и проверка
Если доступ на сервер только через веб-консоль хостера, зайдите под нужным пользователем и добавьте ключ руками:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys # вставить одну строку целиком
chmod 600 ~/.ssh/authorized_keys
chown -R "$USER:$USER" ~/.ssh
Публичный ключ — это ровно одна строка. Перенос строки в середине ломает её: сервер увидит два мусорных ключа вместо одного рабочего. После вставки проверьте, что файл читается корректно:
ssh-keygen -lf ~/.ssh/authorized_keys
Команда выведет по строке на каждый валидный ключ. Если ключей меньше, чем вы добавили, — где-то съехал перенос.
Ключ добавляется тому пользователю, под которым вы будете входить. Файл/root/.ssh/authorized_keysи/home/deploy/.ssh/authorized_keys— разные вещи. Классическая ошибка: положить ключ подsudoв домашний каталог root, а потом удивляться, почемуssh deploy@hostпросит пароль.
Права доступа chmod: главная причина Permission denied (publickey)
SSH по умолчанию работает в режиме StrictModes yes: если права на каталог или файлы слишком широкие, сервер молча игнорирует ключ и делает вид, что его нет. В клиентском логе вы увидите только Permission denied (publickey), без объяснений. Это причина примерно половины всех «ключ не работает».
| Путь | Права | Владелец | Что ломается при ошибке |
|---|---|---|---|
/home/user | 750 или 700 (не групп-/мир-записываемый) | user | Сервер отвергает весь каталог .ssh |
~/.ssh | 700 | user | Ключи не читаются, вход по паролю или отказ |
~/.ssh/authorized_keys | 600 | user | Ключ игнорируется |
~/.ssh/id_ed25519 (клиент) | 600 | user | UNPROTECTED PRIVATE KEY FILE, клиент отказывается использовать ключ |
~/.ssh/config (клиент) | 600 | user | Bad owner or permissions |
Разовая починка на сервере:
chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R "$(id -un):$(id -gn)" ~/.ssh
restorecon -Rv ~/.ssh 2>/dev/null || true
Последняя строка нужна на системах с SELinux (RHEL, Rocky, AlmaLinux, Fedora): если каталог .ssh создан через копирование из /tmp или распакован из архива, у него будет неверный контекст безопасности, и sshd не сможет прочитать файл при формально правильных правах. На Debian и Ubuntu команда просто отсутствует и безопасно пропускается.
Файл ~/.ssh/config и ssh-agent
Как только серверов становится больше трёх, длинные команды с -i, -p и полным именем пользователя перестают быть выносимыми. Решение — клиентский конфиг с Host-алиасами.
# ~/.ssh/config
Host prod-web
HostName 203.0.113.10
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
Host db-internal
HostName 10.0.5.20
User admin
IdentityFile ~/.ssh/id_ed25519_prod
ProxyJump prod-web
Host *
ServerAliveInterval 30
ServerAliveCountMax 3
HashKnownHosts yes
После этого ssh prod-web делает всё то же самое, что длинная команда. А ssh db-internal автоматически ходит через бастион, без ручного проброса.
Ключевой параметр здесь — IdentitiesOnly yes. Без него клиент предлагает серверу все ключи из агента подряд. При шести ключах в агенте и лимите MaxAuthTries 6 на сервере попытки закончатся до того, как дело дойдёт до нужного ключа, и вы получите Too many authentication failures.
ssh-agent: ввести парольную фразу один раз
# Linux: запустить агент, если он не поднят сессией
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519_prod # спросит фразу один раз
ssh-add -l # какие ключи сейчас в агенте
ssh-add -D # выгрузить все ключи
Полезная привычка — ограничивать время жизни ключа в агенте: ssh-add -t 4h ~/.ssh/id_ed25519_prod. По истечении срока ключ выгружается сам, и забытая незалоченная сессия перестаёт быть бессрочным доступом.
На macOS агент интегрирован с Keychain: ssh-add --apple-use-keychain ~/.ssh/id_ed25519_prod сохранит фразу в связке ключей, а строка UseKeychain yes в ~/.ssh/config заставит клиент забирать её оттуда автоматически.
Не включайтеForwardAgent yesв блокеHost *. Проброс агента даёт root'у на промежуточном сервере возможность использовать ваши ключи, пока сессия открыта. Если проброс действительно нужен — включайте его точечно для конкретного бастиона, а лучше замените наProxyJump, который такой проблемы не создаёт.

Отключение входа по паролю, root-логина и настройка порта SSH
Ключи сами по себе ничего не закрывают: пока PasswordAuthentication yes, боты продолжают перебирать пароли. Смысл появляется после отключения парольного входа.
Правим /etc/ssh/sshd_config:
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
PermitEmptyPasswords no
MaxAuthTries 3
LoginGraceTime 20
AllowUsers deploy admin
Что здесь важно:
KbdInteractiveAuthentication noобязателен вместе сPasswordAuthentication no. Иначе на системах с PAM пароль остаётся доступным через клавиатурно-интерактивный метод, и вы получите ложное ощущение закрытого доступа.PermitRootLogin prohibit-passwordоставляет root доступным по ключу (это нужно для части автоматизации), но запрещает пароль. Если root вообще не нужен — ставьтеno.AllowUsers— белый список. Даже при утечке ключа сервисного пользователя вход под чужим именем не пройдёт.
Ловушка с Include и sshd_config.d
В современных дистрибутивах в начале sshd_config стоит строка Include /etc/ssh/sshd_config.d/*.conf. Для sshd действует правило «первое совпадение выигрывает»: значение, заданное во включённом файле, перебивает всё, что вы напишете ниже в основном конфиге. Облачные образы часто кладут туда файл, включающий парольный вход обратно.
Поэтому проверяйте не файл, а эффективную конфигурацию:
sudo sshd -T | grep -Ei 'passwordauth|permitroot|pubkeyauth|kbdinteractive|^port'
sudo grep -r -n 'PasswordAuthentication' /etc/ssh/
sshd -T выводит итоговые значения с учётом всех включений. Если там passwordauthentication yes, а в вашем файле no — ищите перебивающий файл в sshd_config.d.
Проверка sshd -t и правило второй сессии
Опечатка в sshd_config означает, что демон не поднимется после перезапуска. Если это единственный способ попасть на машину, вы её потеряли до похода в веб-консоль хостера.
# 1. Синтаксическая проверка ДО перезапуска
sudo sshd -t
# 2. Только если проверка молчит — применяем
sudo systemctl reload ssh || sudo systemctl reload sshd
# 3. В ДРУГОМ окне терминала — проверяем вход
ssh -o PreferredAuthentications=publickey deploy@203.0.113.10
Держите текущую SSH-сессию открытой, пока новая не подключилась успешно. reload не рвёт уже установленные соединения — это ваш путь отката. Закрыли обе сессии, не проверив вход, — и единственным вариантом остаётся консоль хостера или загрузка в rescue-режиме.
Смена порта SSH: что она даёт и чего не даёт
Перенос SSH с 22 на нестандартный порт — популярный совет, и он полезен ровно в одном: массовые сканеры, которые долбятся строго в 22, перестают засорять auth.log. Читать логи становится реально.
Чего смена порта не даёт: защиты. Полное сканирование диапазона портов находит SSH за секунды, а баннер выдаёт версию демона независимо от номера порта. Если парольный вход остался включён, нестандартный порт не спасёт — целевой скан всё равно найдёт сервис. Порт — это гигиена логов, а не мера безопасности. Подробный разбор того, какие порты стоит держать открытыми, — в материале о безопасности открытых портов.
# /etc/ssh/sshd_config
Port 2222
Дальше нужны три вещи, о которых регулярно забывают:
- Открыть порт в файрволе до перезапуска:
sudo ufw allow 2222/tcpилиsudo firewall-cmd --permanent --add-port=2222/tcp && sudo firewall-cmd --reload. - На SELinux-системах разрешить порт для SSH:
sudo semanage port -a -t ssh_port_t -p tcp 2222. Без этого демон не сможет занять порт. - Проверить, не запущен ли SSH через systemd-сокет. В свежих Ubuntu демон активируется через
ssh.socket, и директиваPortизsshd_configпри этом игнорируется — порт задаётся в override сокета.
# Если активен ssh.socket — порт меняется здесь
systemctl is-enabled ssh.socket
sudo systemctl edit ssh.socket
# в открывшийся файл:
# [Socket]
# ListenStream=
# ListenStream=2222
Пустая строка ListenStream= обязательна: она сбрасывает унаследованное значение 22, иначе демон будет слушать оба порта.
SSH-ключи для Git: GitHub, GitLab и деплой-ключи
Git по SSH использует ту же механику. Отдельный ключ под Git-хостинг — хорошая практика: его компрометация не даёт доступа к продовым серверам.
ssh-keygen -t ed25519 -C "alice@github" -f ~/.ssh/id_ed25519_github
cat ~/.ssh/id_ed25519_github.pub # содержимое вставить в настройках аккаунта
# проверка (ответ "successfully authenticated" — норма, шелла там нет)
ssh -T git@github.com
ssh -T git@gitlab.com
Привязка ключа к хосту через конфиг избавляет от догадок, какой ключ уйдёт:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
Host gitlab-work
HostName gitlab.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Второй блок решает частую задачу «два аккаунта на одном хостинге»: репозиторий клонируется как git clone git@gitlab-work:group/project.git, и клиент подставит нужный ключ.
Почему деплой-ключ должен быть отдельным
Личный ключ разработчика, положенный на сервер ради git pull, даёт этому серверу доступ ко всем репозиториям, к которым имеет доступ человек. Взлом одного веб-сервера превращается в компрометацию всей кодовой базы организации.
Правильный подход: под каждый сервер и репозиторий — свой ключ, только на чтение, без парольной фразы (иначе автоматизация не сработает), с явным сроком ревизии. На стороне сервера доступ такого ключа ограничивается прямо в authorized_keys:
# одна строка в ~/.ssh/authorized_keys
restrict,from="203.0.113.0/24",command="/usr/local/bin/deploy.sh" ssh-ed25519 AAAAC3Nza... ci@runner
restrictвыключает сразу всё лишнее: проброс портов, агента, X11, выделение псевдотерминала.from=привязывает ключ к сети источника.command=жёстко задаёт единственную выполняемую команду: что бы клиент ни попросил, запустится только она.

Как удалить SSH-ключ и отозвать доступ
Отзыв ключа — это не «поменять пароль». Ключ работает, пока его строка физически лежит в authorized_keys хотя бы на одном сервере.
Порядок действий при увольнении сотрудника или подозрении на утечку:
# 1. Найти все места, где встречается отпечаток/комментарий ключа
sudo grep -rn "alice@laptop" /home/*/.ssh/authorized_keys /root/.ssh/authorized_keys 2>/dev/null
# 2. Удалить строку (backup обязателен)
sudo cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak
sudo sed -i '/alice@laptop/d' /home/deploy/.ssh/authorized_keys
# 3. Убедиться, что список ключей стал ожидаемым
ssh-keygen -lf /home/deploy/.ssh/authorized_keys
Проверьте все точки, а не только очевидную:
- домашние каталоги всех пользователей, включая сервисных и root;
- устаревший
~/.ssh/authorized_keys2, если он остался с древних времён; - нестандартный путь:
sudo sshd -T | grep authorizedkeysfile— некоторые конфигурации выносят ключи в/etc/ssh/authorized_keys/%u; - ключи в CI, в панелях хостера, в Git-хостинге (аккаунтные ключи и deploy keys), в образах и в шаблонах виртуалок;
- активные сессии: удаление строки не рвёт уже открытое соединение — завершите их через
whoиpkill -u username sshd.
На парке машин удобнее централизованный запрет. Директива RevokedKeys в sshd_config указывает на файл со списком отозванных публичных ключей — они не примутся, даже если строка случайно осталась в чьём-то authorized_keys.
Отдельно про «ssh удалить ключ» в другом смысле — запись сервера в known_hosts на клиенте:
ssh-keygen -R 203.0.113.10
ssh-keygen -R "[203.0.113.10]:2222" # для нестандартного порта
Разбор ошибок: ssh -vvv и типовые сообщения
Универсальный первый шаг — подробный лог клиента:
ssh -vvv -o PreferredAuthentications=publickey -i ~/.ssh/id_ed25519_prod deploy@203.0.113.10
Ищите в выводе строки Offering public key: и то, что идёт следом. Если сервер отвечает Authentications that can continue: publickey и цикл повторяется — ключ отправлен, но не принят: проблема на сервере. Если клиент вообще не предлагает нужный файл — проблема локальная: не тот путь, не те права, ключ не в агенте.
Серверная сторона (нужен доступ через второй канал):
sudo journalctl -u ssh -f
sudo tail -f /var/log/auth.log # Debian/Ubuntu
sudo tail -f /var/log/secure # RHEL-семейство
Permission denied (publickey)
Самая частая ошибка и самая неинформативная. Проверяйте по порядку:
- Права:
~не групп-записываемый,~/.ssh— 700,authorized_keys— 600, владелец совпадает с пользователем входа. - Тот ли пользователь: ключ лежит у
deploy, а вы заходите какroot. - Тот ли ключ: сравните
ssh-keygen -lf ключ.pubиssh-keygen -lf authorized_keys— отпечатки должны совпасть. - Целостность строки: перенос в середине ключа при копипасте.
- Пользователь не отсечён списком
AllowUsers/AllowGroups, аккаунт не заблокирован (passwd -S deploy, значениеL— заблокирован). - SELinux-контекст каталога
.ssh.
Bad owner or permissions on /home/user/.ssh/config
Клиент отказывается читать конфиг, доступный на запись группе или другим пользователям. Лечится одной командой: chmod 600 ~/.ssh/config. Та же логика применяется к самому каталогу.
WARNING: UNPROTECTED PRIVATE KEY FILE!
Приватный ключ читаем кем-то кроме владельца. chmod 600 ~/.ssh/id_ed25519. Частый источник — копирование ключа с флешки или из Windows-раздела, где права не сохраняются.
Host key verification failed
Отпечаток сервера не совпал с сохранённым в known_hosts. Законные причины: переустановка ОС, миграция на новую машину, восстановление из бэкапа, смена IP. Незаконная: MITM. Сначала убедитесь через второй канал (консоль хостера, документация), что новый отпечаток настоящий, и только потом удаляйте старую запись через ssh-keygen -R. Отключать проверку через StrictHostKeyChecking no — способ узаконить подмену сервера.
Too many authentication failures
Агент предложил больше ключей, чем разрешает MaxAuthTries. Решение — IdentitiesOnly yes в конфиге и точечный IdentityFile.
no matching host key type found / ssh-rsa
Старый RSA-ключ с подписью на SHA-1 против свежего OpenSSH. Правильное решение — перевыпустить ключ в ed25519. Временный обход — явно разрешить алгоритм в конфиге клиента для конкретного хоста через PubkeyAcceptedAlgorithms +ssh-rsa, но это откладывание проблемы, а не её решение.
Connection refused / Connection timed out
Это уже не аутентификация. refused — порт закрыт или демон не запущен: systemctl status ssh. timed out — трафик режет файрвол или группа безопасности у облачного провайдера. Проверяется снаружи, а не с самой машины.

Как проверить настройку
После правок нужно посмотреть на сервер снаружи, а не изнутри. Локальные ss -tlnp и systemctl status показывают только то, что демон живой; они ничего не говорят о том, что видит интернет.
- Порт SSH виден снаружи? Просканируйте хост через сканер портов. Если 22 (или ваш нестандартный порт) отвечает всему миру, следующий вопрос — нужно ли это. Лучший вариант для SSH — ограничение по IP на уровне файрвола или облачной security group, а не открытый доступ. Как читать результаты скана, разобрано в статье о проверке открытых портов.
- Общая гигиена сервера. Прогоните домен через проверку безопасности: заголовки, TLS, признаки утечек конфигурации. SSH — только одна дверь; закрытая на ключ, она не спасает от открытой админки или отсутствующего HSTS. Системный чеклист по всему периметру — в материале о харденинге веб-сервера.
- Проверка эффективного конфига.
sudo sshd -T | grep -Ei 'passwordauth|permitroot|kbdinteractive'— единственный надёжный источник правды о том, что реально применилось. - Реальная попытка входа по паролю.
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password deploy@hostдолжна завершиться отказом без запроса пароля. Если пароль спрашивают — парольный вход остался включён.
Для админских ключей есть уровень выше: аппаратный токен (ssh-keygen -t ed25519-sk) требует физического касания при каждом подключении, поэтому украденный файл ключа сам по себе бесполезен. Логика та же, что у современных методов входа в веб-сервисы — см. разборы двухфакторной аутентификации и passkeys против классической 2FA.
Частые вопросы
Можно ли использовать один SSH-ключ на всех серверах?
Технически да, и для личного ключа администратора это нормальная практика: ключ один, а отзывается он централизованно. Проблема возникает, когда тот же ключ уезжает в CI, в контейнер и на чужую машину. Правило простое: один ключ на одну «личность» — вы, конкретный CI-раннер, конкретный сервис. Не один ключ на всех.
Что делать, если приватный ключ утёк?
Немедленно удалить соответствующий публичный ключ из authorized_keys на всех серверах и из всех аккаунтов Git-хостинга, завершить активные сессии этого пользователя, затем сгенерировать новую пару и раскатать её. Менять парольную фразу на утёкшем ключе бессмысленно: у злоумышленника уже есть файл, и он может подбирать фразу офлайн сколько угодно.
Нужен ли fail2ban, если пароли отключены?
Ценность падает, но не до нуля. Перебор ключей бесполезен, однако fail2ban отсекает шум в логах, снижает нагрузку от массовых сканеров и ловит попытки эксплуатации других сервисов. Как единственная мера защиты SSH он не нужен — отключённый пароль сильнее.
Почему ключ работает у одного пользователя и не работает у другого?
Почти всегда — права или владелец. Каталог .ssh, скопированный от другого пользователя через cp -r под sudo, сохраняет старого владельца, и sshd его отвергает. Проверьте ls -ld ~ ~/.ssh ~/.ssh/authorized_keys — владелец должен совпадать с именем пользователя, под которым вы входите.
Обязательно ли отключать вход root по SSH?
Как минимум отключите пароль для root (PermitRootLogin prohibit-password). Полный запрет плюс работа через обычного пользователя с sudo лучше: появляется персонализированный аудит — в логах видно, кто именно выполнил команду, а не безликий root.
Ключ перестал работать после переустановки сервера — почему?
Переустановка стирает authorized_keys вместе с домашним каталогом, а сервер получает новый host key. Первое лечится повторной установкой ключа, второе — ssh-keygen -R и подтверждением нового отпечатка через консоль хостера.
Чеклист
- Ключ сгенерирован как
ed25519, с осмысленным комментарием и парольной фразой. - Приватный ключ никуда не копировался: ни в чат, ни в репозиторий, ни в образ.
- Публичный ключ добавлен нужному пользователю, одной строкой, отпечатки совпадают.
- Права выставлены: домашний каталог не групп-записываемый,
~/.ssh— 700,authorized_keys— 600, владелец верный. - Клиентские
~/.ssh/configи приватные ключи — 600. - Вход по ключу проверен в отдельной сессии до изменения конфига сервера.
PasswordAuthentication noиKbdInteractiveAuthentication noподтверждены черезsshd -T, а не только записаны в файл.- Проверен каталог
sshd_config.dна перебивающие директивы. sudo sshd -tвыполнен до перезапуска, вторая сессия держалась открытой.- Порт (если менялся) открыт в файрволе, разрешён в SELinux, учтён
ssh.socket. - Для Git и деплоя выпущены отдельные ключи, деплой-ключ ограничен
restrictиcommand=. - Есть процедура отзыва: где искать ключи, кто её выполняет, как проверяется результат.
- Порт проверен снаружи через сканер портов, периметр — через проверку безопасности.