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