OpenSSH 7.8 отключил DSA по умолчанию: как мы перегенерировали ключи у трёх клиентов за выходные
OpenSSH 7.8 убрал поддержку DSA-ключей и устаревших алгоритмов по умолчанию. После обновления у трёх клиентов встала автоматическая репликация - пришлось массово переходить на Ed25519.
OpenSSH 7.8 (август 2018) удалил поддержку DSA-ключей хоста и ужесточил умолчания по безопасности, отключив устаревшие алгоритмы шифрования и обмена ключами
В конце августа вышел OpenSSH 7.8. Релиз не производил впечатления события - очередная версия, changelog прочитали по диагонали, поставили на тестовых стендах, всё работает. Через неделю начали прилетать алерты с продакшн-окружений трёх разных клиентов: автоматическая репликация не проходит, rsync-задачи в cron падают, Ansible не может подключиться к ряду хостов.
Все три инцидента оказались об одном и том же.
Что изменил OpenSSH 7.8
Главное изменение - DSA (ssh-dss) ключи хоста отключены по умолчанию на стороне клиента. PubkeyAcceptedKeyTypes больше не включает ssh-dss. Формально это было deprecated ещё с OpenSSH 7.0 три года назад, тогда же вышло предупреждение что DSA будет убран. Но «deprecated» на практике означает «пока работает» - и никто особо не торопился.
Кроме DSA, 7.8 убрал из умолчаний несколько устаревших алгоритмов:
- diffie-hellman-group14-sha1 - обмен ключами DH с SHA-1
- ecdsa-sha2-nistp256 и nistp521 - не убраны, но ужесточены требования к параметрам
- arcfour, blowfish, cast128 - из шифров (они исчезли ещё раньше, но 7.8 подчистил остатки)
Всё это в changelog было написано честно. Мы просто не проверили достаточно тщательно перед раскаткой.
Что сломалось и у кого
У всех трёх клиентов ситуация была одна и та же структурно, с вариациями в деталях.
Серверы с автоматической репликацией данных работали под управлением debian-based дистрибутивов. Обновление OpenSSH прилетело через unattended-upgrades - автоматически, ночью, без ручного вмешательства. После перезагрузки или просто после рестарта sshd старые DSA host key остались в /etc/ssh/, но клиентская сторона (в том числе другие обновлённые серверы) отказывалась их принимать.
Первый клиент - репликация PostgreSQL между двумя датацентрами через rsync. Скрипт ходил по ключу DSA типа ssh-dss 1024 бит. Стоп.
Второй - Ansible-пайплайн, который ночью собирал конфигурации с парка из нескольких десятков хостов. Часть хостов обновилась, часть - нет. Те что обновились перестали принимать подключения от ansible-runner, у которого в known_hosts был прописан старый DSA fingerprint.
Третий - резервное копирование через rsync + SSH, скрипт запускался с отдельного backup-сервера. DSA-ключ для авторизации backup-пользователя был выписан ещё в 2013 году. Работал всё это время, никто не трогал. После обновления - не работает.
Как чинили
Первый шаг - диагностика. На падающем соединении включаем verbose:
ssh -vvv user@host 2>&1 | grep -E "(key type|offer|debug|refused)"
Видим что-то вроде:
debug1: Skipping ssh-dss key ... not in PubkeyAcceptedKeyTypes
Причина понятна. Дальше - регенерация.
Решение стандартное: переходим на Ed25519. Это эллиптическая криптография, короткие ключи (256 бит вместо 1024+ у DSA), быстрая генерация, поддерживается в OpenSSH начиная с версии 6.5 (2014 год). То есть ни на одном из серверов клиентов совместимости не было: все достаточно свежие.
Генерация нового ключа для сервисного аккаунта:
ssh-keygen -t ed25519 -C "replication@hostname" -f /etc/ssh/replication_ed25519 -N ""
Для host key - удалить старые DSA, убедиться что Ed25519 host key уже есть (OpenSSH генерирует его автоматически при установке начиная с 6.7) или сгенерировать:
ls /etc/ssh/ssh_host_*
# если ssh_host_ed25519_key нет - генерируем:
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
Дальше: распространить публичную часть нового ключа в authorized_keys на целевых серверах, обновить known_hosts на клиентской стороне, проверить что соединение устанавливается, убрать старые DSA-ключи из authorized_keys.
По факту это Ansible-задача на полчаса написания и час прогона по всем затронутым хостам.
Что нашли попутно
Пока чинили - прошлись по остальным ключам. Нашли несколько RSA 1024-битных ключей, выписанных в 2010-2012 годах. Они пока принимаются (OpenSSH предупреждает, но не блокирует), но это очевидный следующий кандидат на замену.
RSA ключи меньше 2048 бит - отдельная история: OpenSSH уже несколько версий подряд выводит предупреждения при использовании RSA-1024. Параллельно с Ed25519-миграцией заменили и их.
Также обнаружили что у одного клиента ~/.ssh/known_hosts на backup-сервере не обновлялся с 2015 года и содержал fingerprint хостов, которые уже дважды переехали. Работало «случайно» - потому что хосты сохранили те же IP, а old DSA fingerprint валидировался. После перегенерации host key пришлось перестраивать known_hosts с нуля для этого сервера.
Итог
Три инцидента за одни выходные - неплохой мотиватор разобраться с состоянием SSH-ключей в инфраструктуре. На всех серверах под managed-обслуживанием теперь есть инвентаризация типов ключей в Ansible-переменных и задача в регламентных работах - проверять что нет deprecated алгоритмов.
Правило которое мы для себя сформулировали: если upstream пишет «deprecated, уберём в следующих версиях» - это задача в бэклог прямо сейчас, не «разберёмся когда сломается». unattended-upgrades - штука удобная до тех пор, пока инфраструктура не аккумулировала технический долг по безопасности, который выстреливает одновременно везде.