SSH, PGP, TOTP в Yubikey 5

image-loader.svg

Что такое Yubikey

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

Год назад я приобрел Yubikey, чтобы использовать его в качестве второго фактора для аккаунтов Google, Github и других, которые работают с U2F. Однако я для себя выяснил, что Yubikey обладает куда более расширенным функционалом. Большая часть из нижеописанных действий актуальна для работы и без Yubikey, однако преимущество в портативности будет потеряно.

Все действия описаны для Yubikey 5 и Ubuntu 21.04.

SSH-ключи

Многие знакомы с такой простой операцией, как подключение по SSH. У нас в TOT Systems имеется собственный менеджер паролей, и чтобы подключиться к удаленной машине, нужно зайти в него, скопировать логин, адрес и пароль.

Проблема паролей в том, что их можно подобрать, поэтому для авторизации по SSH часто используют RSA-ключи. Ключ состоит из двух частей — открытой и закрытой. Открытая часть должна быть на удалённой машине, а закрытая часть у пользователя в надёжном месте, т.е. приватный ключ никому показывать нельзя. Кто-то считает надежным местом облако или флешку — места, из которых теоретически можно достать этот самый ключ. Преимущество Yubikey в том, что приватный ключ из него извлечь не получится — даже пользователь его не знает.

Для работы нам понадобятся OpenSSH версии 8.2 и библиотека libfido2. Вместо привычного RSA-файла с приватным ключом давайте создадим ключ прямо на Yubikey с алгоритмом Ed25519.

Вставляем Yubikey в USB-порт и вводим команду с опцией resident, которая означает, что приватный ключ всегда будет храниться на Yubikey, а непосредственно в компьютере находится резидентный ключ, который без аппаратного ключа бесполезен:

$ ssh-keygen -t ed25519-sk -O resident

user@pc:~$ ssh-keygen -t ed25519-sk -O resident 
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter PIN for authenticator: 
Enter file in which to save the key (/home/user/.ssh/id_ed25519_sk): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/user/.ssh/id_ed25519_sk
Your public key has been saved in /home/user/.ssh/id_ed25519_sk.pub
The key fingerprint is:
SHA256:ztpJ8rTSlo07BQ0LyL6hI2X9/CE9fTdy0EQZhAnxc74 user@pc
The key's randomart image is:
+[ED25519-SK 256]-+
|   . .    oo =+o |
|    o . .  .o o  |
|   o   . +  oo.  |
|  o +   o . .+.  |
| o . = .So   ..  |
|. o . +o+ o o +. |
| . .  .+=B . +E. |
|      .BBo.      |
|      .o*o       |
+----[SHA256]-----+

Сначала нас попросят ввести пин-код от Yubikey. Если до этого вы не настраивали свой юбикей, то User PIN по-умолчанию — 123456, а Admin PIN — 12345678.

После ввода User PIN кнопка юбикей начнет мигать — нажимаем на неё. Выбираем место для сохранения ключей (предлагаю не менять директорию). Далее 2 раза вводим фразу-пароль для этого ключа.

В директории ~/.ssh появятся два файла: id_ed25519_sk.pub, который является обычным публичным ключом, и id_ed25519_sk, который является резидентным ключом и ссылается на приватный ключ в Yubikey.

Теперь добавим публичный ключ на SSH-сервер, к которому мы хотим подключиться. Для этого заходим на сервер как обычно с помощью пароля, переходим в папку ~/.ssh и находим в ней файл authorized_keys, а если его нет, то создаём. В этот файлик копируем содержимое нашего публичного ключа. Пробуем авторизоваться:

user@pc:~$ ssh user@192.168.0.12
Enter passphrase for key '/home/user/.ssh/id_ed25519_sk': 
Confirm user presence for key ED25519-SK SHA256:ztpJ8rTSlo07BQ0LyL6hI2X9/CE9fTdy0EQZhAnxc74
Welcome to Ubuntu 21.04 (GNU/Linux 5.11.0-25-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

0 updates can be applied immediately.

*** System restart required ***
Last login: Sat Jul 24 20:12:35 2021 from 192.168.0.47

Нас попросят ввести фразу-пароль от ключа, а затем нажать на кнопку Yubikey — успешно авторизуемся. При этом возможность авторизоваться по паролю никуда не пропала.

Если предварительно вынуть юбикей из USB-порта и попробовать авторизоваться, то после ввода парольной фразы от ключа у нас будет ошибка и нам будет предложено ввести пароль от сервера.

Сгенерировать ключи и хранить их на компьютере можно и без Yubikey, но в таком случае возникает потребность в переносе приватного ключа между компьютерами, на которых необходим доступ к серверу. Преимущество Yubikey в том, что ключи всегда хранятся на нём, и при работе на другом компьютере можно легко получить резидентный и публичный ключи.

К примеру, импортируем ключи с юбикея на новый компьютер. Запускаем терминал в папке ~/.ssh:

$ ssh-keygen -K

Вводим пин-код, парольную фразу и получаем два файла: id_ed25519_sk_rk (резидентный ключ) и id_ed25519_sk_rk.pub (публичный ключ). Переименовываем id_ed25519_sk_rk в id_ed25519_sk. Успешно авторизуемся на новом компьютере.

SSH-ключ и U2F на примере Github

С помощью SSH-ключа можно клонировать репозиторий на гитхабе. Для этого добавляем публичный ключ в аккаунт на гитхабе. Теперь попробуем клонировать репозиторий по SSH:

user@pc:~/Документы$ git clone git@github.com:oleguka/Store.git
Клонирование в «Store»…
Enter passphrase for key '/home/user/.ssh/id_ed25519_sk': 
Confirm user presence for key ED25519-SK SHA256:ztpJ8rTSlo07BQ0LyL6hI2X9/CE9fTdy0EQZhAnxc74
remote: Enumerating objects: 30, done.
remote: Counting objects: 100% (30/30), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 30 (delta 0), reused 30 (delta 0), pack-reused 0
Получение объектов: 100% (30/30), 55.69 KiB | 670.00 KiB/s, готово.

Нас попросят ввести парольную фразу, а также коснуться кнопки юбикея. Вуаля — репозиторий клонирован. Теперь можно проверить, будет ли клонирован репозиторий без юбикея. Вытаскиваем его из USB-порта и попытаемся клонировать другую репу.

user@pc:~/Документы$ git clone git@github.com:oleguka/Cookie.git
Клонирование в «Cookie»…
Enter passphrase for key '/home/user/.ssh/id_ed25519_sk': 
Confirm user presence for key ED25519-SK SHA256:ztpJ8rTSlo07BQ0LyL6hI2X9/CE9fTdy0EQZhAnxc74
sign_and_send_pubkey: signing failed for ED25519-SK "/home/user/.ssh/id_ed25519_sk": invalid format
git@github.com: Permission denied (publickey).
fatal: Не удалось прочитать из внешнего репозитория.

Удостоверьтесь, что у вас есть необходимые права доступа
и репозиторий существует.

Клонировать репу не получается.

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

GPG

Теперь поговорим про GPG — программу с ассиметричным шифрованием. С её помощью можно шифровать, подписывать данные, а также авторизовываться по SSH на серверах. Давайте подпишем коммит с помощью gpg.

Для работы с Yubikey Нужно установить scdaemon:

$ sudo apt install scdaemon

При выводе списка ключей gpg по умолчанию показывает ключи без id — только их отпечаток. Предлагаю это исправить, поскольку для дальнейших действий удобно использовать именно id ключей. Для этого в папке ~/.gnupg создаем конфиг gpg.conf, а в нём пишем одну строчку — keyid-format long.

Перед созданием RSA-ключа и сабключей поменяем их размер на 4096 бит.

Для этого перейдём в меню смарт-карты.

user@pc:~$ gpg --card-edit 

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006134534820000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: 13453482
Name of cardholder: Oleg Tolstykh
Language prefs ...: ru
Salutation .......: Mr.
URL of public key : https://github.com/otolstykh.gpg
Login data .......: olegulka
Signature PIN ....: не требуется
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 5
KDF setting ......: off
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

Появилась основная информация о нашей карте. Видим, что длина ключа 2048 — переключаемся в админ-режим:

gpg/card> admin

и отредактируем длину ключа на 4096:

gpg/card> key-attr 
Изменение атрибутов ключа на карте: Ключа для подписи
Выберите тип ключа:
   (1) RSA
   (2) ECC
Ваш выбор? 1
Какой размер ключа Вам необходим? (2048) 4096
Изменение атрибутов ключа на карте: Ключа для шифрования
Выберите тип ключа:
   (1) RSA
   (2) ECC
Ваш выбор? 1
Какой размер ключа Вам необходим? (2048) 4096
Изменение атрибутов ключа на карте: Ключа для удостоверения личности
Выберите тип ключа:
   (1) RSA
   (2) ECC
Ваш выбор? 1
Какой размер ключа Вам необходим? (2048) 4096

Теперь перейдём к созданию ключей. В самом начале у нас спросят, делать ли резервную копию ключа шифрования. Также спросят срок жизни ключей. Отредактировать этот срок можно в любой момент.

gpg/card> generate 
Сделать вне карты архивную копию ключа шифрования? (Y/n) n

Выберите срок действия ключа.
         0 = не ограничен
        = срок действия ключа - n дней
      w = срок действия ключа - n недель
      m = срок действия ключа - n месяцев
      y = срок действия ключа - n лет
Срок действия ключа? (0) 0
Срок действия ключа не ограничен
Все верно? (y/N) y

GnuPG должен составить идентификатор пользователя для идентификации ключа.

Ваше полное имя: Oleg Tolstykh
Адрес электронной почты: otolstykh@yandex.ru
Примечание: Yubikey
Вы выбрали следующий идентификатор пользователя:
    "Oleg Tolstykh (Yubikey) "

Сменить (N)Имя, (C)Примечание, (E)Адрес; (O)Принять/(Q)Выход? O
gpg: ключ 60D70CA6AFBAB10F помечен как абсолютно доверенный
gpg: создан каталог '/home/user/.gnupg/openpgp-revocs.d'
gpg: сертификат отзыва записан в '/home/user/.gnupg/openpgp-revocs.d/306836E54FE0FF47B9277BDB60D70CA6AFBAB10F.rev'.
открытый и секретный ключи созданы и подписаны.

Проверяем, что ключ сгенерировался и отображается на карте:

gpg/card> list

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006134534820000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: 13453482
Name of cardholder: Oleg Tolstykh
Language prefs ...: ru
Salutation .......: Mr.
URL of public key : https://github.com/otolstykh.gpg
Login data .......: olegulka
Signature PIN ....: не требуется
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 4
KDF setting ......: off
Signature key ....: 3068 36E5 4FE0 FF47 B927  7BDB 60D7 0CA6 AFBA B10F
      created ....: 2021-07-29 07:27:33
Encryption key....: E1CB FF3A 8F81 8B63 59A7  068C F92E 2F31 6078 5D40
      created ....: 2021-07-29 07:27:33
Authentication key: 8493 6F5D 23D0 8D09 D918  BEE6 9C3A 3873 BA8A 4CC1
      created ....: 2021-07-29 07:27:33
General key info..: 
pub  rsa4096/60D70CA6AFBAB10F 2021-07-29 Oleg Tolstykh (Yubikey) 
sec>  rsa4096/60D70CA6AFBAB10F  создан: 2021-07-29  годен до: никогда     
                                номер карты: 0006 13453482
ssb>  rsa4096/9C3A3873BA8A4CC1  создан: 2021-07-29  годен до: никогда     
                                номер карты: 0006 13453482
ssb>  rsa4096/F92E2F3160785D40  создан: 2021-07-29  годен до: никогда     
                                номер карты: 0006 13453482

Выводим содержимое публичного ключа в терминал:

user@pc:~$ gpg --armor --export 60D70CA6AFBAB10F

Или экспортируем публичный ключ в файл, а затем добавляем его в свой аккаунт в гитлабе:

user@pc:~$ gpg --output .gnupg/yubikey.asc --armor --export

Теперь создадим глобальный конфиг файл ~/.gitconfig с содержимым:

[user]
	name = otolstykh
	email = otolstykh@yandex.ru
	signingkey = 60D70CA6AFBAB10F
[commit]
  gpgsign = true

Или если у вас IDEA версии 2021.2 и выше, то вы можете прямо в её настройках выбрать ключ для подписи коммитов — Settings | Version Control | Git | Configure GPG Key

После этого ваши коммиты будут подписаны:

image-loader.svg

Допустим, мы хотим воспользоваться нашими ключами на новом компьютере. Для этого на карте нужно указать url публичного ключа. Для этого переходим к редактированию карты и в админ-режиме редактируем url:

gpg/card> url
URL для получения открытого ключа: https://github.com/otolstykh.gpg

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

Теперь на новом компьютере вводим команду fetch

gpg/card> fetch 
gpg: запрос ключа из 'https://github.com/otolstykh.gpg'
gpg: ключ 60D70CA6AFBAB10F: импортирован открытый ключ "Oleg Tolstykh (Yubikey) "
gpg: Всего обработано: 1
gpg:               импортировано: 1

Далее повысим уровень доверия к ключу командой trust:

user@home:~$ gpg --edit-key 60D70CA6AFBAB10F

gpg> trust 
sec  rsa4096/60D70CA6AFBAB10F
     создан: 2021-07-29  годен до: никогда       назначение: SC  
     номер карты: 0006 13453482
     доверие: неизвестно достоверность: неизвестно
ssb  rsa4096/9C3A3873BA8A4CC1
     создан: 2021-07-29  годен до: никогда       назначение: A   
     номер карты: 0006 13453482
ssb  rsa4096/F92E2F3160785D40
     создан: 2021-07-29  годен до: никогда       назначение: E   
     номер карты: 0006 13453482
[ неизвестно ] (1). Oleg Tolstykh (Yubikey) 

Укажите, насколько Вы доверяете данному пользователю в вопросах проверки
достоверности ключей других пользователей (проверяет паспорт,
сверяет отпечатки ключей из разных источников и т.п.)

  1 = Не знаю или не буду отвечать
  2 = НЕ доверяю
  3 = Доверяю ограниченно
  4 = Полностью доверяю
  5 = Абсолютно доверяю
  m = вернуться в главное меню

Ваше решение? 5
Вы действительно хотите сделать этот ключ абсолютно доверенным? (y/N) y

sec  rsa4096/60D70CA6AFBAB10F
     создан: 2021-07-29  годен до: никогда       назначение: SC  
     номер карты: 0006 13453482
     доверие: абсолютное достоверность: неизвестно
ssb  rsa4096/9C3A3873BA8A4CC1
     создан: 2021-07-29  годен до: никогда       назначение: A   
     номер карты: 0006 13453482
ssb  rsa4096/F92E2F3160785D40
     создан: 2021-07-29  годен до: никогда       назначение: E   
     номер карты: 0006 13453482
[ неизвестно ] (1). Oleg Tolstykh (Yubikey) 
Учтите, что показанная достоверность ключа может быть неверной,
пока Вы не перезапустите программу.

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

TOTP

И последнее, о чем я хочу рассказать — это использование Yubikey для генерации одноразовых кодов, причём коды будут храниться, разумеется, на Yubikey. В этом случае не нужно думать о бэкапе кодов в приложении при смене смартфона или переустановке системы. Однако нужно будет пользоваться приложением от Yubikey, которые есть для iOS/Android. У меня версия Yubikey c обычным USB-портом и NFC. Чтобы добавить код или считать список всех кодов, достаточно поднести юбикей к NFC считывателю смартфона. Таким образом я добавил все сервисы, которыми пользуюсь и в которых нет поддержки U2F, однако добавить второй фактор в Яндексе у меня не получилось, так как они используют Двухфакторную аутентификацию, которой удобно пользоваться (на самом деле не так удобно), и коды для Яндекса возможно генерировать только в их приложении.

В заключение. Лично мне безумно удобно использовать Yubikey для личных и рабочих целей, особенно в формате рабочий/домашний компьютер — пользуюсь и радуюсь :)

© Habrahabr.ru