Коротко. Выполните ssh-keygen -t ed25519 -C "you@host-year" и задайте для ключа парольную фразу; Ed25519 используется по умолчанию начиная с OpenSSH 9.5. Если у вас есть ключ безопасности FIDO2, выбирайте -t ed25519-sk (или -t ecdsa-sk, если аутентификатор не поддерживает Ed25519): тогда закрытый ключ вообще не хранится на диске. RSA используйте только для систем, которые не умеют ничего другого, и тогда длиной не меньше 3072 бит: с 14 октября 2026 года GitHub отклоняет новые ключи RSA меньшей длины. Загружайте ключи в агент с ограниченным сроком жизни (ssh-add -t 8h), не перенаправляйте агент на хосты, которые вы не контролируете (используйте ProxyJump), и привязывайте ключи к хостам назначения через ssh-add -h. Для парка серверов замените разрозненные записи в authorized_keys короткоживущими SSH-сертификатами. Постквантовая защита сеанса уже включена: OpenSSH 10 по умолчанию согласует mlkem768x25519-sha256 и предупреждает, если сервер этого не умеет. Новый гибридный тип подписи ssh-mldsa44-ed25519 (OpenSSH 10.6) стоит испытать, но делать его своим единственным ключом пока рано.
Команды выполнялись 20 октября 2026 года в debian:forky-20261005-slim с пакетами Debian openssh-client и openssh-server версии 1:10.6p1-1. Все ключи сгенерированы специально для этой статьи.
Какой тип ключа выбрать
-t | Что это | Когда использовать |
|---|---|---|
ed25519 (по умолчанию) | Ed25519, открытый ключ длиной 32 байта, детерминированные подписи | Почти всегда |
ed25519-sk, ecdsa-sk | Ключ FIDO2: секрет остаётся в аутентификаторе, для каждой подписи нужно касание | У вас есть ключ безопасности, а на сервере работает OpenSSH 8.2 или новее |
ecdsa | ECDSA на P-256, P-384 или P-521 | Политика требует кривых NIST |
rsa | RSA, по умолчанию 3072 бита начиная с OpenSSH 8.0 | Старые серверы, специализированные устройства, некоторые HSM |
mldsa44-ed25519 | Гибридная подпись ML-DSA-44 + Ed25519, появилась в 10.6 | Испытание постквантовой аутентификации |
DSA больше нет: его удалили в OpenSSH 10.0 (апрель 2025 года), «завершив процесс отказа от него, начатый в 2015 году». С ключами RSA всё в порядке: в OpenSSH 8.8 (сентябрь 2021 года) отключили алгоритм подписи ssh-rsa, который хеширует с помощью SHA-1. Тот же ключ RSA подписывает и алгоритмами rsa-sha2-256 или rsa-sha2-512, поэтому ошибку сервера про ssh-rsa обычно устраняют обновлением клиента, а не заменой ключа. GitHub рассуждает так же: 13 января 2027 года он уберёт поддержку подписей ssh-rsa после пробных отключений 4 ноября и 9 декабря 2026 года, а с 14 октября 2026 года принимает новые ключи RSA только длиной от 3072 бит.
Размеры ключей в том виде, в каком их записывает ssh-keygen 10.6 (строка в .pub включает комментарий):
ssh-keygen -q -t rsa -N '' -f ~/rsa_default
ssh-keygen -q -t ecdsa -N '' -f ~/ecdsa_default
ssh-keygen -q -t mldsa44-ed25519 -N '' -f ~/.ssh/id_mldsa44_ed25519
for f in ~/rsa_default ~/ecdsa_default ~/.ssh/id_ed25519 ~/.ssh/id_mldsa44_ed25519; do
ssh-keygen -l -f "$f.pub"
printf ' public key line: %s bytes, private key file: %s bytes\n' "$(wc -c < "$f.pub")" "$(wc -c < "$f")"
done
3072 SHA256:NGv36ZHCwauN4GGrdKSDZPnKTn6nFuLSVRS01/eSXOs alice@59151e7fff6c (RSA)
public key line: 572 bytes, private key file: 2602 bytes
256 SHA256:4ODqYFVTGSwjx7pfis5ZtZ02PKaSd15alII7RIHPhE8 alice@59151e7fff6c (ECDSA)
public key line: 180 bytes, private key file: 513 bytes
256 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c alice@laptop-2026 (ED25519)
public key line: 99 bytes, private key file: 464 bytes
512 SHA256:oqHqur1575V136Khl2P7Gaa6rM+2dPT51B6YfjLSxB4 alice@59151e7fff6c (MLDSA44-ED25519)
public key line: 1868 bytes, private key file: 3982 bytes
Строка открытого гибридного ключа в 19 раз длиннее, чем у Ed25519. Это важно для файлов authorized_keys, сертификатов и всего, что хранит ключи в столбце базы данных, рассчитанном на Ed25519. Классическую пару подробно разбирает сравнение Ed25519 или RSA.
Генерация ключа
ssh-keygen -t ed25519 -C "alice@laptop-2026" -N 'correct horse battery staple' -f ~/.ssh/id_ed25519
Created directory '/home/alice/.ssh'.
Generating public/private ed25519 key pair.
Your identification has been saved in /home/alice/.ssh/id_ed25519
Your public key has been saved in /home/alice/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c alice@laptop-2026
The key's randomart image is:
+--[ED25519 256]--+
| ..o*..... |
| o . .=... =. |
|. o .. +. + o. |
| . . . o =. . ...|
| o + S .. ..|
| . . o + B |
| . * = * o . |
| o E = o.+ |
| o oo. . |
+----[SHA256]-----+
-N передаёт парольную фразу в командной строке только потому, что стендом управляет скрипт; в интерактивной работе опустите этот параметр: ssh-keygen запросит фразу сам, и она не попадёт в историю командной оболочки. Комментарий — произвольный текст, который хранится в открытом ключе: имя, машина и год позволяют провести аудит authorized_keys и спустя годы. Серверы записывают в журнал именно отпечаток (SHA256:…), поэтому сохраняйте его, где бы вы ни регистрировали ключ.
Парольная фраза и раунды KDF
Без парольной фразы файл закрытого ключа и есть ключ: его выдаст украденная резервная копия, образ диска ноутбука или неправильно настроенная папка синхронизации. С парольной фразой файл зашифрован ключом, который выводится с помощью bcrypt_pbkdf. Формат закрытого ключа OpenSSH записывает шифр, KDF и число раундов в заголовок, который любой может прочитать без парольной фразы:
"""Print the cipher, KDF and bcrypt rounds from the header of an OpenSSH private key file (openssh-key-v1)."""
import base64
import struct
import sys
text = open(sys.argv[1]).read()
body = "".join(line for line in text.splitlines() if not line.startswith("-----"))
blob = base64.b64decode(body)
magic = b"openssh-key-v1\0"
assert blob.startswith(magic), "not an OpenSSH private key"
pos = len(magic)
def string():
global pos
(length,) = struct.unpack(">I", blob[pos : pos + 4])
value = blob[pos + 4 : pos + 4 + length]
pos += 4 + length
return value
cipher = string().decode()
kdf = string().decode()
options = string()
line = f"cipher={cipher} kdf={kdf}"
if kdf == "bcrypt":
(salt_len,) = struct.unpack(">I", options[:4])
(rounds,) = struct.unpack(">I", options[4 + salt_len : 8 + salt_len])
line += f" salt={salt_len} bytes rounds={rounds}"
print(line)
python3 kdfinfo.py ~/.ssh/id_ed25519
for r in 32 100 500; do
ssh-keygen -q -p -a $r -P 'correct horse battery staple' -N 'correct horse battery staple' -f ~/.ssh/id_ed25519 >/dev/null
s=$(date +%s.%N); ssh-keygen -y -P 'correct horse battery staple' -f ~/.ssh/id_ed25519 >/dev/null; e=$(date +%s.%N)
printf 'rounds=%s unlock=%.2fs\n' $r $(echo "$e - $s" | bc)
done
cipher=aes256-ctr kdf=bcrypt salt=16 bytes rounds=32
rounds=32 unlock=0.16s
rounds=100 unlock=0.52s
rounds=500 unlock=2.59s
До OpenSSH 9.4 по умолчанию было 16 раундов, с 9.4 — 24, а с 10.6 — 32. Стоимость растёт линейно с числом раундов, как подчёркивают примечания к выпуску 10.6 («не как в bcrypt(3), где рост экспоненциальный»), поэтому цифры выше легко пересчитать на любое другое число раундов. Выберите наибольшее значение, при котором задержка разблокировки для вас приемлема: злоумышленник, скопировавший файл, платит ту же цену за каждую попытку подбора. ssh-keygen -p -a 100 -f ~/.ssh/id_ed25519 заново шифрует существующий ключ; если загружать его в агент один раз за сеанс, ждать придётся тоже только один раз. С версии 10.6 OpenSSH отклоняет ключи, у которых больше миллиона раундов, поэтому вредоносный файл ключа уже не может подвесить сервис, который его разбирает.
Парольная фраза защищает файл при хранении. Она ничего не даёт, если ключ уже расшифрован в агенте на скомпрометированной машине; для этого случая и нужны аппаратные ключи.
Аппаратные ключи: -sk
Ключи ed25519-sk и ecdsa-sk (OpenSSH 8.2 и новее) хранят секрет в аутентификаторе FIDO2, например в YubiKey или в платформенном аутентификаторе. Файл ~/.ssh/id_ed25519_sk в этом случае содержит только дескриптор ключа, по которому аутентификатор восстанавливает ключ; без устройства файл бесполезен. Каждая подпись требует присутствия пользователя (касания), а с -O verify-required — ещё и PIN-кода. Важные параметры с цитатами из ssh-keygen(1):
-O residentсохраняет дескриптор ключа на аутентификаторе, поэтомуssh-keygen -Kилиssh-add -Kмогут загрузить его на другом компьютере. Руководство предупреждает, что это «повышает вероятность того, что злоумышленник сможет воспользоваться украденным аутентификатором», поэтому сочетайте этот параметр с PIN-кодом.-O verify-requiredтребует проверки пользователя (сейчас это PIN-код) для каждой подписи; сервер может потребовать её опциейverify-requiredвauthorized_keys.-O application=ssh:workразделяет несколько резидентных ключей на одном устройстве; строка должна начинаться сssh:.-O no-touch-requiredотключает касание, а sshd отклоняет такие подписи, еслиauthorized_keysявно их не разрешает.-O write-attestation=pathсохраняет аттестацию аутентификатора, с помощью которой организация может при регистрации ключа доказать, что он создан на одобренном оборудовании.
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:work -C "alice@yubikey-2026"
Эту команду мы не запускали: в контейнере нет аутентификатора, и без него в выводе была бы только ошибка. Некоторые старые аутентификаторы поддерживают только P-256; для них используйте ecdsa-sk. Платформенные ключи тоже работают, но у ключей, которые хранит защищённое оборудование ноутбука, свои компромиссы; они разобраны в статье об аппаратных ключах.
Постквантовая защита: обмен ключами сейчас, подписи потом
У двух частей SSH разные сроки. Сеансовые ключи получаются в результате обмена ключами; если его взломают позже, записанный трафик можно будет расшифровать (собери сейчас, расшифруй потом). Подписи хоста и пользователя важны только в момент подключения: будущий квантовый компьютер не сможет вернуться в прошлое и подделать вход, который уже состоялся. Поэтому в OpenSSH первым сменили обмен ключами:
sntrup761x25519-sha512включён по умолчанию с OpenSSH 9.0 (2022);mlkem768x25519-sha256(ML-KEM-768 в сочетании с X25519) используется по умолчанию с OpenSSH 10.0 (апрель 2025 года) и теперь описан в RFC 10042 (информационный RFC, 2026);- с 10.1 клиент выводит предупреждение, если сеанс в итоге использует классический обмен ключами, а с 10.6 такой сеанс записывает в журнал и сервер.
debug1: kex: algorithm: mlkem768x25519-sha256
debug1: kex: host key algorithm: ssh-ed25519
debug1: Server host key: ssh-ed25519 SHA256:Qw4tNHnCo8UwS2qNXROrEm8RP+d+ccaVbwAzv8lLX+I
debug1: Offering public key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c agent
debug1: Server accepts key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c agent
Authenticated to localhost ([::1]:22) using "publickey".
При подключении к серверу с настройкой KexAlgorithms curve25519-sha256,ecdh-sha2-nistp256 клиент с настройками по умолчанию вывел:
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
Мы заметили две детали. Клиент молчит, если вы сами ограничили KexAlgorithms классическим методом (предупреждение адресовано серверам), а сервер записывает в журнал каждый такой сеанс, например connection from ::1 port 38814 is not using a post-quantum key exchange algorithm: "curve25519-sha256" [preauth]. WarnWeakCrypto no-pq-kex в блоке Match host отключает предупреждение клиента для хоста, который вы не можете исправить, как советует openssh.com/pq.html. GitHub объявил о поддержке mlkem768x25519-sha256 на github.com с 14 октября 2026 года; 20 октября наш клиент 10.6 согласовал этот алгоритм с git@github.com.
Подписи — второй шаг. В OpenSSH 10.4 появилась экспериментальная составная подпись из ML-DSA-44 и Ed25519; в 10.6 её включили под именем ssh-mldsa44-ed25519, и этот тип описан в черновике рабочей группы SSHM draft-ietf-sshm-composite-sigs. Ключи, созданные экспериментальным кодом 10.4, нужно сгенерировать заново. В пакете Debian версии 10.6 команда ssh-keygen -A создала ssh_host_mldsa44_ed25519_key рядом с ключами хоста RSA, ECDSA и Ed25519, а сам тип стоит в конце списка HostKeyAlgorithms по умолчанию, поэтому клиенты по-прежнему выбирают Ed25519. Если запросить этот тип явно, вся цепочка работает:
debug1: kex: host key algorithm: ssh-mldsa44-ed25519
debug1: Server host key: ssh-mldsa44-ed25519 SHA256:UVOUu+csMH8i8Zd/oWzokAGfzRYFetOncVzB4vYeMgQ
debug1: Server accepts key: /home/alice/.ssh/id_mldsa44_ed25519 MLDSA44-ED25519 SHA256:oqHqur1575V136Khl2P7Gaa6rM+2dPT51B6YfjLSxB4 explicit
Authenticated to localhost ([::1]:22) using "publickey".
Обе половины подписывают одно и то же сообщение с разделением доменов, и «если не пройдёт любая из двух операций проверки, проверка составной подписи ДОЛЖНА завершиться неудачей», поэтому сегодня гибрид не слабее Ed25519. Платить за это приходится размером (по данным черновика, подпись в 2 484 байта при каждой аутентификации) и совместимостью: серверы, Git-хостинги, бастионы (промежуточные хосты) и библиотеки, которые не знают этого типа, ключ не примут. Сгенерируйте такой ключ, проверьте его на своих хостах и оставьте рядом с ним зарегистрированный ключ Ed25519.
Агент и перенаправление агента
ssh-agent хранит расшифрованные ключи и подписывает по запросу; программы обращаются к нему через сокет из SSH_AUTH_SOCK. С OpenSSH 10.1 этот сокет находится в ~/.ssh/agent, а не в /tmp. Добавляйте ключи с ограниченным сроком жизни, чтобы забытый сеанс не держал их разблокированными бесконечно:
eval "$(ssh-agent -s)" >/dev/null
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
ssh-add -t 8h ~/.ssh/id_ed25519
ssh-add -l
SSH_AUTH_SOCK=/home/alice/.ssh/agent/s.eaeHp0xOjD.local.660.o5M944i4eW
Identity added: /home/alice/.ssh/id_ed25519 (alice@laptop-2026)
Lifetime set to 08:00:00
256 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c alice@laptop-2026 (ED25519)
AddKeysToAgent в ~/.ssh/config делает то же самое при первом использовании ключа и принимает срок жизни (AddKeysToAgent 8h) или confirm: тогда агент, как и с ssh-add -c, запрашивает подтверждение перед каждой подписью.
Перенаправление агента (ssh -A, ForwardAgent yes) открывает доступ к этому сокету на удалённом хосте. ssh_config(5) прямо называет риск: «злоумышленник не может получить ключевой материал из агента, однако может выполнять операции с ключами». Любой, у кого есть root на удалённом хосте, может входить от вашего имени везде, где работают ваши ключи, пока вы не отключитесь. Лучше выработать две привычки:
- Подключайтесь через промежуточный хост, а не перенаправляйте агент.
ssh -J bastion target(илиProxyJump bastionв конфигурации) устанавливает второе соединение с вашей машины через перенаправленный TCP-поток; бастион никогда не получает доступа к вашему агенту. - Ограничивайте ключи.
ssh-add -h(OpenSSH 8.9 и новее) привязывает ключ к хостам назначения, которые опознаются по их ключам хоста изknown_hosts, а если вы всё же перенаправляете агент, то и к цепочкам переходов (bastion>target).
ssh-add -h prod-host ~/agent_only_key
ssh -o BatchMode=yes prod-host 'echo signed in on prod-host'
ssh -v -o BatchMode=yes build-host true 2>&1 | grep -iE 'agent|Offering|Permission denied|Authenticated|identity'
Identity added: /home/alice/agent_only_key (alice@laptop)
signed in on prod-host
debug1: get_agent_identities: bound agent to hostkey
debug1: get_agent_identities: ssh_fetch_identitylist: agent contains no identities
debug1: Offering public key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:MA2nEhBnMlCkX3pWWgPtff90dRZF9D9k8i2svCUK1II
alice@127.0.0.2: Permission denied (publickey,password).
Агент привязал сеанс к ключу хоста build-host и не выдал для него ни одного ключа: ключ существует, но не для этого хоста назначения. Руководство честно говорит о пределах такой защиты: ограничение «не мешает злоумышленнику с доступом к удалённому SSH_AUTH_SOCK перенаправить его дальше и использовать на другом хосте (но только для разрешённого хоста назначения)».
Сертификаты вместо authorized_keys
Копировать открытые ключи в authorized_keys на каждый сервер — подход, который не масштабируется, а отзыв ключа при нём превращается в поиск по всем серверам. SSH-сертификат — это открытый ключ, подписанный ключом центра сертификации (CA) вместе с идентификатором, принципалами, серийным номером и сроком действия. Серверы один раз настраивают доверие к CA (TrustedUserCAKeys) и проверяют список отзыва (RevokedKeys):
ssh-keygen -s /etc/ssh/user_ca -I "alice@example.com 2026-10-20" -n alice -V +8h -z 1042 /home/alice/.ssh/id_cert_user.pub
ssh-keygen -L -f /home/alice/.ssh/id_cert_user-cert.pub
su - alice -c 'ssh -o IdentitiesOnly=yes -o IdentityAgent=none -i ~/.ssh/id_cert_user localhost "echo logged in with certificate"'
echo "serial: 1042" > /tmp/revoke.txt
ssh-keygen -k -u -s /etc/ssh/user_ca.pub -f /etc/ssh/revoked.krl /tmp/revoke.txt
ssh-keygen -Q -f /etc/ssh/revoked.krl /home/alice/.ssh/id_cert_user-cert.pub
su - alice -c 'ssh -o IdentitiesOnly=yes -o IdentityAgent=none -o BatchMode=yes -i ~/.ssh/id_cert_user localhost true'
Signed user key /home/alice/.ssh/id_cert_user-cert.pub: id "alice@example.com 2026-10-20" serial 1042 for alice valid from 2026-10-20T14:33:00 to 2026-10-20T22:34:33
/home/alice/.ssh/id_cert_user-cert.pub:
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:eVstR5lK6VBztjl2AYyWcv8C6xwNahEEVSrQpe7Y/LE
Signing CA: ED25519 SHA256:+URM6AMiGUu+gv/gxoQgpOhHP4CUN1fznXWDlVxorbY (using ssh-ed25519)
Key ID: "alice@example.com 2026-10-20"
Serial: 1042
Valid: from 2026-10-20T14:33:00 to 2026-10-20T22:34:33
Principals:
alice
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
logged in with certificate
Revoking from /tmp/revoke.txt
/home/alice/.ssh/id_cert_user-cert.pub (alice@ci-runner): REVOKED
alice@localhost: Permission denied (publickey).
sshd записал в журнал key ED25519-CERT SHA256:eVst… revoked by file /etc/ssh/revoked.krl. Обратите внимание на расширения по умолчанию: сертификат разрешает перенаправление агента и портов, если не подписать его с -O clear и не добавить обратно только нужное (-O permit-pty). Храните ключ CA офлайн или в HSM и выпускайте сертификаты на часы, а не на годы: при коротком сроке действия большинство отзывов просто не понадобится. Сертификаты хоста (-h при подписи, @cert-authority в known_hosts) точно так же убирают вопрос о доверии ключу хоста при первом подключении.
Ротация ключей
- Ключи пользователей. Сгенерируйте новый ключ, добавьте его открытый ключ рядом со старым везде (серверы, Git-хостинги, секреты CI), подключитесь с
-i new_key -o IdentitiesOnly=yes, чтобы убедиться, что он работает, и только потом удалите старую строку. Какая строка старая, подскажет комментарий с датой. - Ключи хоста.
UpdateHostKeysвключён по умолчанию с OpenSSH 8.5 (если вы не переопределилиUserKnownHostsFileи не включилиVerifyHostKeyDNS): после аутентификации сервер сообщает все свои ключи хоста, а клиент записывает новые, поэтому сервер может опубликовать замену до того, как выведет старый ключ из работы. Клиенты, которые подключаются редко, пропускают это окно; сертификаты хоста снимают проблему. - Алгоритмы. Тот же механизм позволяет провести миграцию: добавьте ключ хоста Ed25519 (а позже и гибридный), дайте клиентам его запомнить, затем удалите ключ RSA.
- Инвентаризация.
ssh-keygen -l -f authorized_keysвыводит по одному отпечатку на строку; сравните их со строкамиAccepted publickey … SHA256:в журнале sshd, чтобы найти ключи, которыми никто не пользуется.
Типичные ошибки
- Файл ключа доступен на чтение всем.
sshотказывается его использовать и выводит WARNING: UNPROTECTED PRIVATE KEY FILE!. Выполнитеchmod 600для ключа и700для каталога.ssh. - Неверная парольная фраза или преобразованный ключ. incorrect passphrase supplied to decrypt private key и error in libcrypto выглядят похоже; вторая ошибка обычно означает, что файл повреждён или записан в формате, который эта сборка не умеет читать. Библиотекам парольную фразу нужно передавать явно, как показывает ошибка this private key is passphrase protected в Go.
- Слишком много ключей в агенте. После
MaxAuthTries(6) неудачных попыток sshd прекращает аутентификацию и разрывает соединение с ошибкойToo many authentication failures, а каждый ключ, который пробует клиент, может отнять одну попытку (серверы OpenSSH 10.6 допускают ещё 6 запросов вида «примете ли вы этот ключ»,PubkeyOptions max-pk-ok). ЗадайтеIdentityFileиIdentitiesOnly yesдля каждого хоста. Если ни один из предложенных ключей не зарегистрирован, вы получите Permission denied (publickey). - Агент отказывает. agent refused operation означает, что сервер принял ключ, но агент не стал подписывать: ключа FIDO не коснулись вовремя, агент не может показать запрос подтверждения, токен заблокирован или файл ключа на диске больше не совпадает с тем, что хранится в агенте.
- Устаревшие узлы. no matching host key type found. Their offer: ssh-rsa означает, что сервер умеет подписывать только с SHA-1. Обновите его;
HostKeyAlgorithms +ssh-rsaдля отдельного хоста — временное исключение и никогда не глобальная настройка. - Игнорирование сменившегося ключа хоста. REMOTE HOST IDENTIFICATION HAS CHANGED означает либо плановую ротацию, которую можно подтвердить по независимому каналу, либо ровно ту атаку, ради которой эта проверка существует. Удаляйте старую строку командой
ssh-keygen -R hostтолько после того, как выясните, что именно произошло.
Чек-лист
ssh-keygen -t ed25519(или-t ed25519-skс ключом безопасности), парольная фраза, комментарий с владельцем, машиной и годом.- Увеличьте число раундов KDF до приемлемой для вас задержки (
-a 100здесь стоит около полусекунды). - Один ключ на устройство; никогда не копируйте закрытые ключи между машинами.
- Агент со сроками жизни ключей;
IdentitiesOnly yesдля каждого хоста;ProxyJumpвместоForwardAgent;ssh-add -h, если без перенаправления не обойтись. - Парк серверов: сертификаты пользователей и хостов с коротким сроком действия, ключ CA офлайн, KRL на каждом сервере.
- Ищите в выводе
ssh -vстрокуkex: algorithm: mlkem768x25519-sha256; считайте постквантовое предупреждение заявкой на обновление сервера. - Проверьте ключи
mldsa44-ed25519на своих серверах; держите классический ключ зарегистрированным, пока гибрид не начнут принимать все узлы.
Источники
- Примечания к выпускам OpenSSH: 8.5 (UpdateHostKeys по умолчанию), 8.8 (RSA/SHA-1 отключён), 8.9 (ограничения по хостам назначения), 9.0 (sntrup761x25519), 9.4 и 10.6 (раунды KDF), 9.5 (Ed25519 по умолчанию), 10.0 (ML-KEM по умолчанию, DSA удалён), 10.1 (предупреждение, сокеты агента), 10.4 и 10.6 (ML-DSA-44 + Ed25519)
- OpenSSH: постквантовая криптография
- Страницы руководства: ssh-keygen(1), ssh-add(1), ssh_config(5)
- RFC 10042: гибридный обмен ключами PQ/T с ML-KEM в SSH
- draft-ietf-sshm-composite-sigs: постквантовые составные подписи в SSH
- Журнал изменений GitHub: улучшения безопасности SSH (22 сентября 2026 года)