Все, кто использует SSH знают, что при первом подключении к серверу, появляется сообщение с подтверждением отпечатка ключа. Дальше ключ сохраняется на стороне клиента, и больше это сообщение не показывается до момента пока сохраненный ключ не изменится. Но в чем практический смысл этой процедуры?
В реальной жизни почти никто не проверяет отпечаток SSH-ключа сервера не особенно задумываясь о возможности MiTM-атаках. С появлением DNS-записи SSHFP отпечаток ключа сервера можно хранить в DNS и проверять его подлинность с помощью DNSSEC. При этом не нужно даже подтверждать ключ при первом подключении. В статье разберем, как настроить запись SSHFP для своего SSH-сервера.
Сервер для тестов
Для начала нам потребуется сервер.В панели RuVDS реквизиты для SSH-доступа находятся сразу на карточке сервера. Сохраняем IP-адрес и пароль для подключения.
Вы также можете настроить файрвол прямо из контрольной панели, чтобы разрешить доступ по SSH только для своего IP.
Для настройки SSHFP на IP-адрес сервера должен быть направлен домен, именно для этого домена мы будем настраивать DNS-записи.
Как работают ключи в SSH
В примерах мы будем рассматривать только пакет OpenSSH, так как это самый популярный вариант.
При установке нового сервера генерируются случайные SSH-ключи, обычно это происходит сразу при установке пакета OpenSSH или при первой загрузке, если используются готовые образы систем.
Файлы с ключами находятся здесь:
$ ls -la /etc/ssh/ssh_host_*_key*
/etc/ssh/ssh_host_dsa_key
/etc/ssh/ssh_host_dsa_key.pub
/etc/ssh/ssh_host_ecdsa_key
/etc/ssh/ssh_host_ecdsa_key.pub
/etc/ssh/ssh_host_ed25519_key
/etc/ssh/ssh_host_ed25519_key.pub
/etc/ssh/ssh_host_rsa_key
/etc/ssh/ssh_host_rsa_key.pub
В этом списке есть сразу несколько ключей разного типа: dsa, rsa, ecdsa, ed25519. Это сделано для совместимости с разными SSH-клиентами. На этапе подключения клиент и сервер согласовывают, какой алгоритм ключа будет использоваться. Клиент может попросить сервер использовать другой алгоритм, если предложенный не поддерживается. Сервер посылает публичную часть своего ключа клиенту и пользователю предлагается проверить его отпечаток вручную.
Сервер посылает клиенту отпечаток публичного ключа в момент подключения
Если это первое подключение, клиенту будет показано сообщение с требованием проверить отпечаток ключа вручную:
The authenticity of host 'example.com (123.45.67.89)' can't be established.
ECDSA key fingerprint is SHA256:7Q4nIqjuo/lSXWFkt9RaJYVHrT6LUAc6KWrdQ4/DDeA.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
На этом этапе мы все обычно нажимаем Yes не задумываясь и отпечаток ключа сохраняется в файл
~/.ssh/known_hosts
. Теперь если ключ на сервере изменится, клиенту будет показано сообщение о возможной MiTM-атаке. Предполагается, что в самый первый раз клиент выполнил проверку ключа самостоятельно и убедился, в его подлинности. Такой подход довольно рискованный, ведь если атакующий успел перехватить момент первого подключения, то сможет подсунуть свой ключ и перехватить подключение. В случае c HTTPS у нас есть третья сторона, которая подтверждает ключ сервера с помощью сертификата. Если такой же подход как в SSH использовался в вебе, нас бы постоянно мучали сообщения о проверке ключа и MiTM-атаки были повсеместно, ведь никто не станет проверять ключи, а будет просто всегда соглашаться.
Храним отпечаток ключа в DNS. Что такое записи SSHFP
Как узнать, что предложенный сервером SSH-ключ действительно настоящий и это не MiTM-атака? Ведь для того чтобы зайти на сервер и проверить ключ нужно сперва ввести пароль. Но тогда атакующий сможет моментально взломать сервер и подменить все данные на нем, да так, что мы и не заметим подвоха. Поэтому нужен способ проверить подлинность ключа еще ДО реального подключения.
Долгое время, единственным способом проверить SSH-ключ сервера до подключения, было использовать другой канал, например, попросить, администратора сервера сообщить отпечаток ключа сервера. Это неудобно, поэтому проще игнорировать проблему и всегда соглашаться не задумываясь.
SSHFP позволяет проверить подлинность ключа сервера до подключения через DNS
SSHFP — тип записей DNS для хранения SSH-ключей. Отпечаток SSH-ключа помещается в DNS-сервер подобно TXT записи и подписывается ключом DNSSEC. То есть при первом подключении к серверу с использованием имени хоста, клиент сможет заранее узнать отпечаток ключа сервера через DNS, и если он совпадает с предложенным сервером, то подключаться без предупреждения.
Настройка DNSSEC
Для использования SSHFP потребуется доменное имя с настроенным DNSSEC. Есть множество публичных DNS-сервисов, которые предлагают панель управления DNS сразу с функцией DNSSEC. Самый популярный такой сервис — CloudFlare. Рассмотрим настройку на его примере. Для следующий действий домен должен быть делегирован NS-сервера Cloudflare.
?Шаг 1 — сгенерировать ключ
Заходим в панель DNS --> Enable DNSSEC
В этот момент будут сгенерированы ключи для подписания вашей доменной зоны. Вам будут показаны публичные ключи. Их нужно будет добавить на стороне регистратора домена.
?Шаг 2 — добавление публичных ключей у регистратора
Далее, нужно создать DS-записи, содержащие публичный ключ, у регистратора домена.
В зависимости от вашего регистратора, интерфейс добавления ключей DNSSEC может выглядеть по-разному. Важно только не перепутать значения, так как они могут называться по-разному и отличаться от названий показанных в CloudFlare.
На примере ниже показано как соотносятся значения, показанные в панели CloudFlare со значениями в панели управления доменом у регистратора Uniregistry.
?Шаг 3 — проверка добавленных DS-записей
После добавления DS-записей на стороне регистратора можно проверить корректность настроек. На стороне CloudFlare подписание DNS-записей будет активировано, только когда будет пройдена проверка корректности добавления DS-записей на стороне регистратора.
Ожидание добавления DS-записей
Через пару минут или часов, если записи были добавлены корректно, вы увидите такое сообщение. Это значит, что DNS-ответы теперь подписываются с помощью DNSSEC.
?Шаг 4 — проверка работы DNSSEC
Теперь можно проверить работу DNSSEC на нашем домене с помощью онлайн-сервисов вроде dnssec-analyzer.verisignlabs.com. Все галочки должны быть зелеными.
Результат проверки DNSSEC
Добавление записей SSHFP
Сгенерируем SSHFP-записи на сервере. В нашем примере мы администрируем сервер с адресом myserver.com. Для этого доменного имени мы ранее настроили DNSSEC.
На сервере выполняем команду:
# Сгенерировать записи SSHFP из существующих SSH-ключей
sudo ssh-keygen -r myserver.com
myserver.com IN SSHFP 1 1 057ecce168ace29d5a0099e3b63df2850e4c8e20
myserver.com IN SSHFP 1 2 52cd6099a304b9b8f57f2cd914e96a1b7477eb2f88c98c602
myserver.com IN SSHFP 2 1 42d677482e4450ee515d3aac94d96302a99bd4ec
myserver.com IN SSHFP 2 2 edda5fa445dc0da570c772a6df0d4012037e1a102840d29c4
...
Будут сгенерированы отпечатки для всех ключей из папки /etc/ssh/. Вы можете выборочно сгенерировать отпечатки для конкретных ключей, указав путь к файлу.
Теперь все эти записи нужно добавить в панели DNS, в нашем случае Cloudflare.
Добавление записи SSHFP в панели Cloudflare
Так, нужно добавить все ключи, полученные на предыдущем шаге. Теперь можно проверить, что ключи добавлены:
dig SSHFP myserver.com
В ответе вы должны увидеть все добавленные ключи. На подписание новых записей может потребоваться время, поэтому ключи в ответе могут появиться не сразу. Обычно это не занимает дольше 10-15 минут.
Подключаемся к серверу
Чтобы SSH-клиент проверял валидность ключей через DNS, нужно добавить включить это в настройках. Конфиг клиента находится в домашней папке пользователя. Добавляем туда одну строку.
# Редактируем конфиг
vi ~/.ssh/config
VerifyHostKeyDNS yes
Теперь можно подключаться к серверу. Для чистоты эксперимента можно удалить строку с отпечатком ключа из ~/.ssh/known_hosts. Для наглядности можно добавить опцию -v
# Подключаемся к серверу
ssh -v root@myserver.com
# DNS настроен неправильно, записей SSHFP нет
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
.....
DNS lookup error: data does not exist
# DNS настроен правильно, но системный резолвер
# не поддерживает валидацию DNSSEC
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
....
debug1: found 8 insecure fingerprints in DNS
debug1: matching host key fingerprint found in DNS
# DNS настроен правильно, системный резолвер поддерживает DNSSEC
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
....
debug1: found 8 secure fingerprints in DNS
debug1: matching host key fingerprint found in DNS
debug1: ssh_rsa_verify: signature correct
Если все настроено правильно, то при первом подключении к серверу не будет запрошена ручная проверка отпечатка ключа. Для этого также нужно, чтобы системный DNS-резолвер поддерживал валидацию DNSSEC.
Важно помнить, что SSHFP будет работать только при подключении к серверу по доменному имени и не будет работать при подключении по IP или другому домену, на котором нет SSHFP-записей.
zkutch
Чтобы обезопасить первое подключение можно использовать fwknop, хотя там есть и своя слабина.
YourChief
Каким образом? fwknop это портнокер по сути, он наоборот ограничивает клиентов пока они не пришлют аутентифицированный пакет.
Из альтернатив тут только использование сертификатов SSH (подчеркну: не ключей — сертификатов), распространяемых отдельно. Такое решение лучше масштабируется и не требует внедрения DNSSEC. Вот руководство, правда не очень свежее: www.digitalocean.com/community/tutorials/how-to-create-an-ssh-ca-to-validate-hosts-and-clients-with-ubuntu
zkutch
Да, и как раз именно этим можно обезопасить первое подключение: ссш открывается только тому клиенту, который присылает нужный пакет. Для остальных, в основном, даже не видно, что порт назначен для ссш. После того как я настроил fwknop боты фактически сканируют впустую.
YourChief
Смысл статьи в противоположном: защитить не сервер от левых клиентов, а клиентов от подложного сервера. Ситуация такая: клиент делает первое подключение, он ещё не знает, какой у сервера должен быть host key. Первый раз обычно мы просто его принимаем на веру, не зная, к кому подключаемся на самом деле. А вот с DNSSEC можно опубликовать подписанную запись, какой должен быть ключ.
zkutch
Рассмотрим схематичную связку — fwknop && ssh — если не прошел fwknop, то это ложный сервер и ssh не начинается.
YourChief
Во-первых, как Вы узнаете, прошёл он или нет? Там отправляется с клиента один UDP-пакет больше ничего. Можно узнать только если порт UDP закрыт.
Во-вторых, это никак не поможет защитить само TCP-соединение от перехвата. Атакующий может даже и не знать про какой-то там fwknop — он просто зарулит соединение SSH себе и всё. А SPA-пакет fwknop спокойно дойдёт до сервера, и сервер может его даже получит и что-то там откроет.
zkutch
У fwknop на сервере есть возможность отсылать на мейл сообщения на открытии и закрытии доступа.
zhovner Автор
YourChief прав, порт кнокинг не защтит вас от MiTM-атаки. Представьте, что атакующий сидит на стороне провайдера и использует DPI для обнаружения SSH хендшейка и автоматически подменяет ключ на свой. Ему не важно что вы делали до этого, ведь TCP-подключение с SSH все равно будет обнаружено и перехвачено.
zkutch
речь о fwknop, а не о простом кнокинге.