Перейти к содержанию
Обсудить задачу →
Статьи и инструкции

Как удалить SSH-ключ одного хоста из known_hosts безопасно

Безопасное обновление known_hosts: проверка причины и fingerprint, ssh-keygen -F и -R, aliases, bastion, hashed entries и строгий reconnect.
Как удалить SSH-ключ одного хоста из known_hosts безопасно — техническая иллюстрация

Предупреждение REMOTE HOST IDENTIFICATION HAS CHANGED защищает от подмены сервера. Удалять строку только ради исчезновения сообщения нельзя: сначала подтверждают, почему изменился host key, и сверяют новый fingerprint по независимому каналу.

Безопасное обновление known_hosts: поиск записи, проверка fingerprint, удаление и строгий повторный вход
Порядок проверки и настройки. Команды, ограничения и диагностика приведены в статье.

1. Прочитайте предупреждение целиком

Зафиксируйте hostname, IP, порт, тип ключа, старый fingerprint и номер файла. Изменение бывает штатным после переустановки, ротации ключей или переключения кластера, но также указывает на DNS-подмену, неверный NAT или атаку посредника.

Offending ED25519 key in /home/user/.ssh/known_hosts:17
Host key for server.example.net has changed

Не вводите пароль и не продолжайте с отключённой проверкой.

Host key и ключ пользователя — разные объекты

Host key подтверждает, к какому серверу подключился клиент. Пользовательский ключ из id_ed25519 и серверного authorized_keys подтверждает право пользователя войти. Удаление known_hosts не чинит Permission denied (publickey) и не меняет пароль. При предупреждении о подмене аутентификация пользователя ещё не должна начинаться: сначала решают вопрос доверия к серверу.

OpenSSH хранит для имени узла алгоритм и публичный host key. SHA-256 fingerprint — короткое представление этого ключа для сверки. Сервер может одновременно предлагать ED25519, ECDSA и RSA. Штатная замена одного алгоритма не означает автоматическое разрешение менять все остальные. Прочитайте фактическую строку предупреждения, а затем проверьте запись выбранного алгоритма. Хешированное имя вида |1|... скрывает hostname в файле, но не является ключом пользователя и не мешает ssh-keygen -F найти запись.

2. Проверьте DNS, маршрут и назначение адреса

Убедитесь, что имя ведёт на ожидаемый адрес и вы подключаетесь к правильной среде. Для хоста за балансировщиком уточните, должны ли узлы иметь общий сертификат/ключ или отдельные имена.

getent ahosts server.example.net
ip route get 192.0.2.20
ssh -G server.example.net | grep -E '^(hostname|port|user|proxyjump) '

Адреса 192.0.2.0/24 в примерах зарезервированы для документации.

3. Получите новый fingerprint по доверенному каналу

Администратор сервера может прочитать публичный host key локально через консоль или доверенный канал. Сравнивайте SHA-256 fingerprint, тип и все имена/IP, которыми пользуются клиенты.

sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub -E sha256
sudo ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub -E sha256

Сообщение в том же подозрительном SSH-сеансе не является независимым подтверждением.

4. Создайте резервную копию known_hosts

Файл может содержать записи других систем и комментарии. Скопируйте его с правами владельца и отметкой времени.

install -m 600 ~/.ssh/known_hosts ~/.ssh/known_hosts.before-$(date +%Y%m%d-%H%M%S)
wc -l ~/.ssh/known_hosts

Если задан пользовательский UserKnownHostsFile, работайте именно с ним.

5. Найдите все записи хоста

ssh-keygen -F умеет искать как открытые, так и хешированные имена. Проверьте hostname, IP и форму с нестандартным портом.

ssh-keygen -F server.example.net
ssh-keygen -F 192.0.2.20
ssh-keygen -F '[server.example.net]:2222'

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

6. Удалите только подтверждённую запись

Используйте ssh-keygen -R, а не ручное удаление «строки 17»: после других изменений номер может устареть. Для нестандартного порта нужны квадратные скобки.

ssh-keygen -R server.example.net
ssh-keygen -R 192.0.2.20
ssh-keygen -R '[server.example.net]:2222'

Команда создаёт .old, но собственная резервная копия всё равно полезна.

Рабочий сценарий Linux с отдельным файлом доверия

В следующем сценарии сервер доступен как server.example.net:2222, а SSH-профиль использует отдельный known_hosts. Имена и адреса примеры; DNS и fingerprint нужно заменить на подтверждённые значения. Создайте каталог владельца, но не перезаписывайте уже существующую конфигурацию .ssh/config.

Host app-prod
    HostName server.example.net
    Port 2222
    User admin
    UserKnownHostsFile ~/.ssh/known_hosts-production
    StrictHostKeyChecking yes
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_ed25519

Host — локальный алиас для команды ssh app-prod; HostName — сетевое назначение; UserKnownHostsFile — фактически проверяемый файл. Если нет HostKeyAlias, запись обычно ищется по сетевому имени и порту, то есть [server.example.net]:2222. С HostKeyAlias ключ поиска меняется. Посмотрите ssh -G app-prod до удаления записей, особенно если есть Include, Match и несколько файлов конфигурации.

kh="$HOME/.ssh/known_hosts-production"
ssh-keygen -F '[server.example.net]:2222' -f "$kh"
cp -p "$kh" "$kh.before-rotation"
ssh-keyscan -p 2222 -t ed25519 server.example.net > /tmp/app-prod.hostkey 2>/tmp/app-prod.keyscan.log
ssh-keygen -lf /tmp/app-prod.hostkey -E sha256

ssh-keyscan получает заявленный сетью ключ: доверия к нему ещё нет. Сравните его SHA256 с тем, что оператор прочитал через консоль гипервизора или другой утверждённый канал. Если получен пустой файл, проверьте DNS, порт и доступность SSH; не дописывайте пустую или HTML-страницу в known_hosts. Журнал keyscan содержит сетевые ошибки, а не приватный ключ.

После совпадения идентичности удалите только старую запись указанного endpoint и добавьте проверенный публичный ключ:

ssh-keygen -R '[server.example.net]:2222' -f "$kh"
cat /tmp/app-prod.hostkey >> "$kh"
chmod 600 "$kh"
ssh-keygen -F '[server.example.net]:2222' -f "$kh" -l -E sha256
ssh -o BatchMode=yes app-prod true

Последняя команда запускает безвредный true после строгой проверки host key и пользовательской аутентификации. Успех — код 0. Если ключ пользователя защищён passphrase и не загружен в агент, BatchMode может закончиться отказом, хотя проверка host key уже прошла. Разделяйте эти два слоя. Повторите вход обычным разрешённым способом, сохраняя StrictHostKeyChecking yes. Не меняйте этот параметр ради прохождения автоматического теста.

Эквивалент на Windows с встроенным OpenSSH

Пользовательский файл обычно находится в $env:USERPROFILE\.ssh\known_hosts. Не путайте его с глобальным файлом %ProgramData%\ssh\ssh_known_hosts. Работайте под той учётной записью, которая действительно подключается. На Windows PowerShell 5.1 обычный Out-File может записать UTF-16; для known_hosts нужен обычный текст, например ASCII для стандартных строк публичных ключей.

$KnownHosts = Join-Path $env:USERPROFILE '.ssh\known_hosts'
$Endpoint = '[server.example.net]:2222'
$Backup = $KnownHosts + '.before-' + (Get-Date -Format yyyyMMdd-HHmmss)
Copy-Item -LiteralPath $KnownHosts -Destination $Backup -ErrorAction Stop
ssh-keygen.exe -F $Endpoint -f $KnownHosts
$Candidate = Join-Path $env:TEMP 'app-prod-hostkey.txt'
ssh-keyscan.exe -p 2222 -t ed25519 server.example.net |
    Set-Content -LiteralPath $Candidate -Encoding ascii
ssh-keygen.exe -lf $Candidate -E sha256
# Следующие две строки — только после независимой сверки fingerprint.
ssh-keygen.exe -R $Endpoint -f $KnownHosts
Get-Content -LiteralPath $Candidate | Add-Content -LiteralPath $KnownHosts -Encoding ascii
ssh.exe -p 2222 -o StrictHostKeyChecking=yes admin@server.example.net

Если политика использует конкретный UserKnownHostsFile, подставьте его. Команда -R не переписывает глобальную базу доверия, которую пользователь не имеет права изменять. Также не удаляйте весь каталог .ssh: он содержит приватные ключи, настройки и записи других серверов.

7. Получите ключ без автоматического доверия

ssh-keyscan удобен для просмотра, но сам по себе не подтверждает подлинность. Сравните его fingerprint с полученным по независимому каналу.

ssh-keyscan -t ed25519 server.example.net 2>/dev/null > /tmp/server.hostkey
ssh-keygen -lf /tmp/server.hostkey -E sha256

Только после совпадения добавляйте ключ или принимайте его при строгом подключении.

8. Повторите подключение со строгой проверкой

Не используйте StrictHostKeyChecking=no и /dev/null для production. Первый вход должен показать тот fingerprint, который уже подтверждён.

ssh -o StrictHostKeyChecking=ask -o VisualHostKey=yes server.example.net
ssh-keygen -F server.example.net -l

Для автоматизации заранее разворачивайте доверенный known_hosts из управляемого источника.

9. Учтите алиасы, bastion и кластеры

HostKeyAlias, ProxyJump, короткое имя, FQDN и IP создают разные ключи поиска. Просмотрите итоговую конфигурацию ssh -G. Для большого парка рассмотрите SSH CA или DNS SSHFP с DNSSEC, но задайте правила ротации и отзыва.

ssh -G app-prod | grep -E '^(hostname|hostkeyalias|proxyjump|userknownhostsfile) '

Почему bastion, IP и сертификаты требуют отдельной проверки

ProxyJump устанавливает сначала SSH-соединение с bastion, а затем SSH до целевого сервера через него. Ключи обоих узлов проверяются отдельно. Изменение конечного сервера не разрешает автоматически смену ключа bastion. Если предупреждение относится к прыжковому узлу, исправление записи app-prod его не устранит. ssh -vv показывает этапы, но перед передачей такого журнала проверьте персональные пути и внутренние адреса.

Hostname и IP могут иметь отдельные записи. При NAT один внешний IP и разные порты представляют разные endpoint. Удаление обычного IP не обязательно затронет [IP]:port. За DNS round-robin несколько независимых серверов с разными ключами могут поочерёдно выдавать предупреждения. Решение — утверждённые отдельные имена, host certificates или согласованная архитектура доверия, а не регулярное удаление known_hosts скриптом.

Строки @cert-authority задают доверие к SSH CA для группы имён, а @revoked запрещают ключ. Их нельзя воспринимать как рядовые старые ключи и стирать без разбора. При сертификате сверяют CA, principals, срок действия и правила отзыва. Запись CA позволяет вращать ключи хостов по установленной процедуре, но компрометация CA затрагивает весь охваченный парк. Для небольшой ручной операции не внедряйте SSH CA одновременно с исправлением одного узла.

10. Проверьте результат и сохраните доказательства

Успех — строгий вход с подтверждённым fingerprint, отсутствие неожиданного запроса после повторного подключения и сохранённая резервная копия. Если новый ключ не совпал, верните known_hosts и эскалируйте инцидент безопасности.

cmp -s ~/.ssh/known_hosts ~/.ssh/known_hosts.old || true
ssh-keygen -F server.example.net -l -E sha256

Приёмка и возврат

Сохраните факт переустановки или ротации, имя владельца изменения, подтверждённый fingerprint, используемый endpoint и путь файла. Повторный строгий вход должен пройти без вопроса о новом ключе. Затем проверьте и штатную автоматизацию: она может работать от другой учётной записи и использовать другой known_hosts. Если сверка не совпала, ничего не принимайте; восстановите собственную резервную копию нужного файла и расследуйте назначение адреса. Возврат старого known_hosts не вернёт старый ключ на сервере и может снова вызвать предупреждение — это ожидаемое безопасное поведение.

Источники: ssh-keygen(1), ssh(1), ssh_config(5).

Дополнительная документация: OpenSSH: ssh-keygen, OpenSSH: ssh_config.