Как защитить SSH-доступ к серверу с помощью аппаратных ключей

14 февраля 2020 года вышла последняя (на момент написания статьи в мае 2020) версия пакета OpenSSH — 8.2. В этой версии была добавлена поддержка U2F/FIDO токенов (аппаратных ключей). Детальная информация доступна на официальном сайте OpenSSH.

Почему это важно?

Аппаратные ключи заметно повышают уровень безопасности любого решения. Вообще говоря, и до релиза OpenSSH 8.2 существовали методы использования аппаратных ключей совместно с SSH. Например, можно использовать сервер PrivacyIdea, хранить приватные SSH-ключи на аппаратном токене, используя PIV или же OpenPGP. Неплохая сводка таких решений доступна на сайте Yubico.

Но в большинстве этих решений нужно было использовать стороннее ПО, а иногда еще и специальным образом настраивать аппаратные ключи. Во многих случаях процесс становился слишком сложным для массового применения.

Новый подход выглядит многообещающе: он поддерживается стандартно самим пакетом OpenSSH и очень напоминает обычную схему работы с SSH-ключами. Никакого дополнительного ПО, сложных конфигураций, преднастройки аппаратных ключей и прочего. Вы берете аппаратный ключ, генерируете SSH-ключ специального типа, добавляете публичный ключ на ваш сервер, как и обычно — и готово! Давайте посмотрим, как это работает в реальной жизни.

Тест на практике

Прежде всего, нам нужен пакет OpenSSH версии 8.2 на клиентской и серверной машинах. И здесь кроется основная проблема с этим решением на момент написания этого материала (май 2020). Поскольку 8.2 — это новейшая версия, выпущенная в феврале 2020 года, во многих ОС пока еще используются предыдущие версии.

Хорошая новость в том, что эта ситуация естественным образом будет улучшаться с течением времени, и все больше ОС будут добавлять поддержку нужной нам версии. Однако мы очень хотели испытать этот функционал на практике и поэтому нашли сочетание клиентских и серверных ОС для нашего практического теста.

По состоянию на май 2020 только несколько популярных дистрибутивов Linux имеют версию OpenSSH 8.2 в своих репозиториях. Среди них Fedora 32, Ubuntu 20.04 LTS и Kali Linux.

Наше тестовое окружение

  • Клиент: MacOS Catalina 10.15.4
  • Сервер: Ubuntu 20.04 LTS (для теста мы использовали VPS от ATLEX. Последняя версия популярного дистрибутива уже доступна для установки на виртуальный сервер в панели управления). Fedora 32 и последняя версия Kali Linux тоже будут работать.
  • Аппаратные токены (ключи): Yubikey 4, Yubikey 5 NFC
Ноутбук

Настройки клиентской машины

Для начала проверим версию SSH, запустив ssh -V в терминале.

В нашем случае была установлена версия 8.1. Не проблема — 8.2 доступна для установки через Homebrew.

Итак, запускаем в своем терминале:

brew install openssh

После установки на всякий случай убедимся, что версия теперь та, которая нам нужна:

ssh -V

Вывод должен быть примерно такой:

OpenSSH_8.2p1, OpenSSL 1.1.1f 31 Mar 2020

Далее можно переходить к генерированию новой пары SSH-ключей с использованием аппаратного токена U2F.

По информации с официального сайта, для поддержки FIDO-токенов в OpenSSH появились два новых типа публичных ключей: «ecdsa-sk» и «ed25519-sk» (-sk означает Security Key). Стоит отметить, что первый тип должен поддерживаться любыми U2F-токенами, а второй — не обязательно. В случае с Yubikey (как у нас) аппаратный ключ должен иметь версию прошивки 5.2.3 или новее. В нашем случае только Yubikey 5 удовлетворяет этому требованию (Yubikey 4 — нет).

Если вы используете несколько ключей (мы рекомендуем делать именно так, поскольку ключ можно потерять или сломать), возможно, имеет смысл найти серийный номер ключа перед тем как перейти к следующему шагу. Этот номер (или скажем 4 последних цифры) удобно использовать при создании SSH-ключей чтобы различать, какой SSH-ключ связан с каким аппаратным токеном.

Теперь вставим Yubikey в ноутбук и запустим следующую команду в терминале:

ssh-keygen -t ecdsa-sk -C "yubi-8945"

(здесь -t указывает тип SSH-ключа который мы генерируем, а -C добавляет комментарий).

Как обычно, ssh-keygen запрашивает имя файла для сохранения пары SSH-ключей (вы можете задать свое или согласиться с именем по умолчанию) и предлагает задать пароль. Эта опция (passphrase) необязательна, но мы рекомендуем всегда защищать приватные SSH-ключей парольной фразой, даже в случае с дополнительной защитой (как сейчас).

Результатом работы ssh-keygen будет, как и всегда, два файла — один с приватным, другой с публичным SSH-ключами. Поведение, знакомое по работе с обычными SSH-ключами. Есть только одно отличие — сохраненный на диске приватный ключ не работает, пока соответствующий ему аппаратный Yubikey не подключен к машине и вы не нажали на нем золотую кнопку!

На этом конфигурация клиента завершена, и мы можем перейти к серверной стороне.

Конфигурация сервера

Как вы помните, в качестве сервера у нас виртуальный сервер от ATLEX с предустановленной Ubuntu 20.04 LTS. Мы предполагаем, что наш сервер доступен по SSH и некоторая его предварительная настройка уже произведена (например, добавлен пользователь с правами sudo, запрещен доступ рутом по SSH, запрещен доступ через SSH по паролям и разрешен только по публичным ключам). Хотя такая настройка и необязательно для этой статьи, мы рекомендуем произвести ее в целях безопасности.

Ок, теперь давайте зайдем на сервер по SSH и для начала проверим версию ОС и пакета SSH.

$ cat /etc/issue

Ubuntu 20.04 LTS \n \l

(все верно, у нас установлена Ubuntu 20.04 LTS)

~$ ssh -V

OpenSSH_8.2p1 Ubuntu-4, OpenSSL 1.1.1f 31 Mar 2020

(и у нас нужная версия SSH, ничего дополнительно устанавливать или апгрейдить не нужно).

Как и в случае с обычными SSH-ключами, нам нужно добавить публичный ключ на сервер. Это можно сделать разными способами, выберем самый простой. На клиентской машине нужно открыть файл <your-new-SSH-key-name>.pub, который мы сгенерировали в предыдущем разделе, в текстовом редакторе и скопировать его содержимое. В нашем случае оно выглядит примерно так:

sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBENJxD85GM/N4Kcp74j0LxpWesQNlqY7tXXMrp2FwQbcQzVAXArpMOykjxtNH61TBIvLgvbj5TjWF+gyRpa9Q+QAAAAEc3NoOg== yubi-8945

А теперь вернемся к серверу и добавим этот ключ в файл

/home/$USER/.ssh/authorized_keys

Сохраняем файл и возвращаемся вновь к клиентской машине.

Подключение к серверу

Настало время попробовать подключиться к серверу с нашим новым ключом.

В терминале на клиентской машине запускаем следующую команду:

ssh -i <your_private_key_filename> <your_server /VPS_name_or_address>

Вывод должен быть примерно такой:

$ ssh -i ecdsa-yubi-8945 <serverIP /address> Enter passphrase for key 'ecdsa-yubi-8945': Confirm user presence for key ECDSA-SK SHA256:/o4F/uM/hp7DiauZd9hsMCYuCP9/oxFOouivFa8aQEso

Как и обычно, SSH-клиент запросит парольную фразу для приватного ключа (если вы ее устанавливали). Кроме этого, он проверит, подключен ли соответствующий аппаратный ключ Yubikey к машине и затем потребует подтвердить присутствие пользователя. Для этого нужно нажать золотую кнопку на Yubikey. Если все было сделано корректно, после этого вы получите SSH-доступ к вашему серверу.

Возможно, вы обратили внимание на параметр -i. Он указывает, какой именно SSH-ключ использовать, что может быть полезно, если вы используете не один такой ключ на ваших машинах. Обратите внимание, что если вы используете ssh-agent и в него уже загружены какие-то из ваших ключей, они могут быть использованы, несмотря на то, что вы явно указываете другой ключ в командной строке с помощью параметра -i.

Возможно, на время эксперимента вы захотите удалить все ключи загруженные в ssh-agent. Это можно сделать командой ssh-add -D.

Проверить, какие ключи загружены, можно с помощью ssh-add -l

Другой вариант: использовать опцию «IdentitiesOnly true» при запуске SSH-клиента вместе с -i.

Все детали о командах и ключах которые описаны в этом разделе можно найти в man для ssh и ssh_config.

Для отладки (в том числе и проверки, какой именно ключ был использован) можно использовать стандартные методы: на клиенте это запуск ssh с параметром -v (что означает verbose; можно добавлять несколько v для получения более детального вывода - -vvv; на стороне сервера можно найти информацию в журнале /var/log/auth.log.

Еще немного безопасности

Вы можете разрешить подключение по SSH только с ключами, которые работают в паре с аппаратными токенами. Это вполне возможно и довольно легко настраивается — нужно лишь в явном виде указать, какие типы ключей мы разрешаем.

Как всегда, при настройке протоколов удаленного доступа будьте осторожны, чтобы не потерять доступ. В случае с VPS, однако, у нас есть запасной вариант доступа через портал провайдера.

Итак, подключаемся к серверу, используем sudo для получения привилегий root и открываем файл /etc/ssh/sshd_config в удобном вам текстовом редакторе.

Нам нужно добавить следующую строчку:

PubkeyAcceptedKeyTypes sk-ecdsa-sha2-nistp256@openssh.com,sk-ssh-ed25519@openssh.com

Эта настройка разрешает SSH-серверу принимать только ключи указанных двух типов. Все другие виды ключей, даже если они были настроены для доступа пользователя, будут отклонены.

Больше деталей по этой настройке можно найти в man для sshd_config

Сохраняем файл и перед перезапуском сервера SSH на всякий случай проверяем, что наши изменения в конфиге были корректны. Для этого запустите sshd -t - если все нормально, то команда не выведет ничего. Если есть какие-то ошибки в конфигурации — они будут указаны в выводе.

Итак, если все в порядке — перезапускаем сервис SSH:

systemctl restart sshd

Теперь давайте для проверки попробуем подключиться с использованием ‘старого’ ключа, настроенного на нашем сервере (то есть добавленного в .ssh/authorized_keys для соответствующего юзера).

ssh -i .ssh/id_rsa <serverIP /name>

Мы должны в ответ получить ошибку Permission denied (publickey)

А на серверной стороне в файле /var/log/auth.log должна быть запись примерно такого вида:

userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedKeyTypes [preauth]

Отлично, теперь наш сервер принимает подключения только с SSH ключами, работающими в паре с аппаратными токенами!

Как можно сохранить свой SSH-ключ на аппаратном токене FIDO2

Процитируем официальную документацию OpenSSH (в нашем переводе):

FIDO/U2F OpenSSH ключи состоят из двух частей: key handle, которая хранится в файле на диске, и приватный ключ, уникальный для каждого FIDO/U2F аппаратного токена, неизвлекаемый из него. Токен соединяет эти две части в момент аутентификации для получения реального ключа, который затем используется (для подписи запроса аутентификации).

Для ключей, которые нужно переносить между машинами, может стать проблемой то, что нужно копировать файл с приватным ключом. Чтобы избежать этого, токены, реализующие более новый стандарт FIDO2, поддерживают так называемые резидентные ключи. Это позволяет извлечь key handle из аппаратного ключа.

Итак, если у вас есть FIDO2-совместимый токен (в нашем случае это Yubikey 5), вы можете использовать ssh-keygen с параметром -O resident при генерации пары ключей SSH. В дальнейшем вы сможете извлечь ваши SSH-ключи из токена и записать на диск в виде файлов. Для этого нужно подключить ваш Yubikey (или тот токен, который вы используете) и запустить команду ssh-keygen -K.

Команда целиком может выглядеть примерно так:

ssh-keygen -t ecdsa-sk -O resident -C "yubi-8945"

Этот подход позволяет вам быть уверенным что ваши SSH-ключи у вас с собой всегда, когда с собой ваш Yubikey или другой аппаратный токен. Вам не нужно беспокоиться о копировании приватных и публичных SSH-ключей (в виде файлов) между разными машинами. Это довольно удобно, но нужно учитывать и связанные с этим риски.

Итак, если вам нужно начать работу в новом окружении (на новой машине), достаточно подключить ваш Yubikey и запустить команду

ssh-keygen -K

Эта команда извлечет все записанные SSH-ключи (как приватные, так и публичные!) и сохранит в виде файлов на диске.

Во-первых, если вы решите хранить свои SSH-ключи на токене, необходимо защитить его с помощью PIN-кода. Иначе любой, кто получит доступ к токену, сможет использовать и ваши SSH-ключи.

Для ключей Yubikey можно использовать официальное приложение Yubikey Manager.

Yubikey Manager

Yubikey Manager

Yubikey Manager

Если FIDO2 PIN установлен, любой, кто попытается извлечь SSH-ключи из вашего токена, должен будет ввести код. Мы настоятельно рекомендуем установить PIN, если вы планируете воспользоваться функцией резидентных SSH-ключей.

Во-вторых, обратите внимание, что когда вы извлекаете SSH-ключи из вашего токена с помощью команды ssh-keygen -K, файлы приватных ключей на диске не будут защищены парольной фразой! Даже если вы устанавливали пароль при генерации ключа, при извлечении из аппаратного токена файлы будут в открытом виде. Поэтому сразу после извлечения ключей из токена необходимо защитить их парольной фразой. Мы рекомендуем защищать приватные ключи паролем даже в случае использования с безопасными аппаратными токенами.

Для реализации основных услуг и функций нашего сайта, а также для сбора данных о том, как посетители взаимодействуют с нашими сайтом, продуктами и услугами, мы применяем различные инструменты, включая файлы cookie. Нажимая «Принимаю», вы соглашаетесь с текущими правилами и условиями использования сайта и даете разрешение на использование этих данных. В противном случае, пожалуйста, покиньте сайт.

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: