Expertus metuit
Меняем ssh-agent на gpg-agent
2017-12-13 23:28

Что такое и зачем нужен SSH Agent

SSH Agent (SSH Агент) хранит в памяти компьютера закрытые SSH-ключи. Когда SSH-клиент (/usr/bin/ssh в линуксе и макоси, например) пытается подключиться к серверу при помощи ключа, она сначала делает запрос к SSH Agent и просит у него закрытый ключ, если у агента он есть, то ключ отдаётся программе, которая использует его для подключения.

Без SSH Agent SSH-клиент напрямую читает ключи с диска, в линуксе и макоси они лежат в каталоге ~/.ssh, если ключ зашифрован, то клиент спрашивает соответствующую парольную фразу. SSH Agent избавляет от многочисленных запросов такой парольной фразы.

В linux и macos по умолчанию стоит /usr/bin/ssh-agent из openssh, однако он малофунциональный и сложный в настройке. Вместо него можно использовать агент из комплекта gnupg (gpg-agent), он умеет не только PGP-ключами управлять, но и выполнять функцию SSH Агента. У gpg-agent есть два преимущества: он настраивается, а также позволяет задавать пароли для шифрования ключей в памяти.

Включение в Linux (debian/ubuntu)

Сначала отключаем автоматический старт ssh-agent, для этого открываем редактором файл /etc/X11/Xsession.options и комментируем/удаляем строчку:

use-ssh-agent

Устанавливаем нужные пакеты:

$ sudo apt install gpg-agent pinentry-qt

Выбираем GUI pinentry (это диалоги, которые вы будете видеть при работе с ключами, например, для запроса парольной фразы к ключу) вместо дефолтного консольного приложения:

$ sudo update-alternatives --set pinentry /usr/bin/pinentry-qt

gpg-agent стартует автоматически в X-сессии, дополнительно никакие скрипты писать для старта не нужно. Однако по умолчанию в GPG Agent поддержка SSH-ключей выключена, для включения нужно сделать так:

$ echo enable-ssh-support >> ~/.gnupg/gpg-agent.conf

Всё, теперь после рестарта X-сессии у вас будет использоваться gpg-agent вместо ssh-agent.

Включение в Macos

Речь идёт о MacOS 10.12 (Sierra) и выше.

Вы можете получить GPG двумя способами: через brew или установкой пакета GPG Suite. Каждый из них устанавливает все нужные для работы программы и работают они в принципе одинаково, однако в GPG Suite удобнее реализована настройка через GUI. Я не рекомендую устанавливать сразу оба, так как они конфликтуют за пути установки.

Установка через brew

$ brew install gnupg pinentry-mac

Установка GPG Suite

Заходите на сайт https://gpgtools.org, скачивайте оттуда .dmg-файл и устанавливайте как обычное приложение.

Включение автозапуска агента

gpg-agent автоматически не стартует, поэтому нужно добавить в систему его автозапуск, я написал конфиг для launchd, вот команда, которая его скачивает в корректный каталог:

$ curl -L https://raw.githubusercontent.com/sigsergv/dotfiles/master/mac/gnupg/gpg-agent.daemon.plist \
  -o ~/Library/LaunchAgents/gpg-agent.daemon.plist

Теперь запускаем:

$ launchctl load -F ~/Library/LaunchAgents/gpg-agent.daemon.plist

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

$ pgrep -lf gpg-agent
15092 gpg-agent --homedir /Users/sigsergv/.gnupg --use-standard-socket --daemon
$

Если команда ничего не выводит, значит, вы что-то сделали не так.

Поддержка SSH включается точно так же, как и для linux:

$ echo enable-ssh-support >> ~/.gnupg/gpg-agent.conf

Замена ssh-agent на gpg-agent

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

Изначально в системе запущен ssh-agent и для него глобально выставляется переменная окружения SSH_AUTH_SOCK. В новых версиях макоси поменять её значение просто так невозможно, поэтому мы пойдём другим путём — в процессе входа пользователя просто перезапишем файл-сокет исходного ssh-agent и превратим его в симлинк, указывающий на сокет gpg-agent. Эту идею и соответствующий скрипт я взял из отличной статьи Stick with security: YubiKey, SSH, GnuPG, macOS.

Вам нужно скачать plist-конфиг gpg-agent-socket-override.plist с моего гитхаба и положить в каталог ~/Library/LaunchAgents, вот команда, которая всё это делает:

$ curl -L https://raw.githubusercontent.com/sigsergv/dotfiles/master/mac/gnupg/gpg-agent-socket-override.plist \
  -o ~/Library/LaunchAgents/gpg-agent-socket-override.plist

Загружем этот конфиг:

$ launchctl load -F ~/Library/LaunchAgents/gpg-agent-socket-override.plist

И проверяем, что всё запустилось:

$ launchctl list | grep gpg-agent-socket-override
-   0   gpg-agent-socket-override
$ ls -lah $SSH_AUTH_SOCK
lrwxr-xr-x  1 sigsergv  wheel    35B  1 май 10:21 /private/tmp/com.apple.launchd.ekWVW4zD9n/Listeners -> /Users/sigsergv/.gnupg/S.gpg-agent.ssh

Для гарантии перезагружаемся.

❈ ❈ ❈

По умолчанию GPG Suite сохраняет пароль к расшифрованному ключу в системном Keychain, это можно отключить в настройках: System PreferencesGPG SuiteStore in macOS Keychain.

Особенности gpg-agent

Отличия gpg-agent от ssh-agent:

  • можно выбирать, какие конкретно ключи хранить в агенте и они там будут храниться после перезагрузки;
  • для каждого ключа можно отдельно задать время его кеширования и дополнительное подтверждение на использование;
  • у gpg-agent есть дополнительный уровень защиты — пароль на дешифрованные ключи в памяти;
  • сразу есть GUI для контроля доступа без необходимости колдовать с переменными окружения и сторонними пакетами.

Если у вас в /etc/ssh/ssh_config или ~/.ssh/config не включена опция AddKeysToAgent, то ключи сами в агент не попадут. Идентификаторы одобренных для агента ключей сохраняются в ~/.gnupg/sshcontrol. Их можно туда добавлять вручную или через ssh-add.

Например, если вы хотите для ключа ~/.ssh/id_rsa-TEST использовать агент, то можно сделать так:

$ /usr/bin/ssh-add ~/.ssh/id_rsa-TEST

Сначала ssh спросит парольную фразу для ключа, после чего появится диалог, где нужно выбрать пароль для шифрования ключа в агенте, обычно это простая короткая фраза (НЕ пароль к оригинальному ключу!):

GnuPG Agent key add password

Этот пароль (а не оригинальную парольную фразу!) нужно будет ввести, когда сторонняя программа затребует ключ:

GnuPG Agent password

Идентификатор добавленного ключа сохраняется в файле ~/.gnupg/sshcontrol в виде блока такого формата:

# RSA key added on: 2017-12-13 14:07:27
# Fingerprints:  MD5:0c:01:91:7c:49:40:f8:7c:74:64:74:bd:7e:c8:7a:e9
#                SHA256:hnOuTZzfe2ak10Jd6bl60kE9yAUqw34KinJ3H4opNbs
3BC01B6F0043256039006294F76C45139B703DBF 0

~/.gnupg/sshcontrol — это текстовый файл. Строки с # в начале считаются комментариями (то есть игнорируются системой). Все остальные строки должны иметь такой формат:

                ┌─────────────────────────── идентификатор ключа  
                │                        ┌── TTL кеширования в секундах
────────────────┴─────────────────────── ┴
3BC01B6F0043256039006294F76C45139B703DBF 0

TTL кеширование — это время в секундах после расшифровки, в течение которого запрос ключа не потребует пароля. Если указать 0, будет использоваться TTL по умолчанию (он задаётся аргументом default-cache-ttl-ssh в конфиге ~/.gnupg/gpg-agent.conf).

Перед идентификатором можно указать восклицательный знак (!) такой ключ будет выключен, это по сути то же самое, что и комментирование этой строчки через #.

Также можно для ключа указать набор флагов (третьим полем после TTL, оно опциональное):

                ┌───────────────────────────── идентификатор ключа  
                │                        ┌──── TTL кеширования в секундах (0 — не кешировать)
                │                        │ ┌── флаги (пока только confirm)
────────────────┴─────────────────────── ┴ ┴──────
3BC01B6F0043256039006294F76C45139B703DBF 0 confirm

На данный момент поддерживается только один флаг — confirm, если он выставлен, то перед каждым обращением к ключу (даже если оно в пределах действующего TTL) будет показываться дополнительный диалог с подтверждением:

GnuPG Agent key request confirmation

Базовые сценарии и операции

Я очень советую не использовать автоматическое добавление ключей в агент. Для /usr/bin/ssh и других программ, использующих /usr/bin/ssh (это git, например), рекомендую посмотреть файлы /etc/ssh/ssh_config и ~/.ssh/config удалить оттуда упоминания параметра AddKeysToAgent. Дальше все нужные ключи добавлять только вручную.

Программа ssh-add из комплекта openssh-client обращается к текущему агенту по стандартному протоколу, поэтому её можно использовать вместе с gpg-agent. Так что примеры ниже могут пригодиться и для ssh-agent тоже.

Также помните, что в некоторых системах менеджер паролей может запомнить пароль для расшифровки ключа, поэтому я настоятельно советую эту фичу отключить, не ставить галочку «Запомнить пароль», ну или как минимум самые критичные ключи добавлять только с флагом confirm, чтобы вы явно разрешали каждое использование ключа.

Как остановить/запустить/перезапустить агент

gpg-agent работает как демон, поэтому для его запуска или остановки нужно использовать специальные команды. Это демон текущей десктопной сессии, а не системы в целом.

Вообще,

Для линукса:

# остановить
gpgconf --kill gpg-agent

# запустить
/usr/bin/gpg-agent --daemon

# перезапустить
gpgconf --kill gpg-agent
/usr/bin/gpg-agent --daemon

Для макоси (подразумевается, что агент настроен по этому руководству):

# остановить
$ launchctl unload ~/Library/LaunchAgents/gpg-agent.daemon.plist

# запустить
$ launchctl load ~/Library/LaunchAgents/gpg-agent.daemon.plist

# перезапустить
$ launchctl unload ~/Library/LaunchAgents/gpg-agent.daemon.plist
$ launchctl load ~/Library/LaunchAgents/gpg-agent.daemon.plist

Если вы обновили GPG Suite, то нужно будет запустить агент снова, так как в процессе апгрейда, процесс агента прибивается.

Просмотр списка ключей в агенте

$ /usr/bin/ssh-add -l                
2048 SHA256:hnOuTZzfe2ak10Jd6bl60kE9yAUqw34KinJ3H4opNbs /home/sigsergv/.ssh/id_rsa-TEST (RSA)
2048 SHA256:OV0fErCIpclIvjl8hCaFmdgfJmGYmdYNOhUus1i7GUg /home/sigsergv/.ssh/id_rsa-cc (RSA)

Добавление ключа в агент

$ /usr/bin/ssh-add ~/.ssh/id_rsa-TEST
Enter passphrase for /home/sigsergv/.ssh/id_rsa-TEST: 
Identity added: /home/sigsergv/.ssh/id_rsa-TEST (/home/sigsergv/.ssh/id_rsa-TEST)

Добавление ключа с TTL=60 секунд

$ /usr/bin/ssh-add -t 60 ~/.ssh/id_rsa-TEST
Enter passphrase for /home/sigsergv/.ssh/id_rsa-TEST: 
Identity added: /home/sigsergv/.ssh/id_rsa-TEST (/home/sigsergv/.ssh/id_rsa-TEST)
Lifetime set to 60 seconds

Данный вызов не модифицирует существующий ключ в ~/.gnupg/sshcontrol! Для изменения параметров смотрите раздел Модификация параметров ключа в агенте.

Добавление ключа с включенным подтверждением

$ /usr/bin/ssh-add -c ~/.ssh/id_rsa-TEST
Enter passphrase for /home/sigsergv/.ssh/id_rsa-TEST (will confirm each use): 
Identity added: /home/sigsergv/.ssh/id_rsa-TEST (/home/sigsergv/.ssh/id_rsa-TEST)
The user must confirm each use of the key

Данный вызов не модифицирует существующий ключ в ~/.gnupg/sshcontrol! Для изменения параметров смотрите раздел Модификация параметров ключа в агенте.

Удаление ключа из агента

В ssh-add есть параметр -d, однако для gpg-agent он не сработает, то есть вы можете выполнить команду, но она ничего не сделает. Для полного удаления нужно пользоваться командами агента. К сожалению, нормальной процедуры или GUI для этого нет и нужно пользоваться несколькими консольными командами.

Сначала посмотрим список хранимых ключей:

$ ssh-add -l
2048 SHA256:hnOuTZzfe2ak10Jd6bl60kE9yAUqw34KinJ3H4opNbs /Users/sigsergv/.ssh/id_rsa-TEST (RSA)
2048 SHA256:MApXMQ8qUqBd4yfbA07RSh+VLT+ARsg5k8fbY72LVCc /Users/sigsergv/.ssh/id_rsa-XYZ (RSA)
2048 SHA256:8X/7LtWAFl962369KYW9ADgNCiPDUE7ilwI9QWXrBxk /Users/sigsergv/.ssh/id_rsa-SECURE (RSA)

Хотим удалить первый, для него ssh-add показывает SHA256 равный hnOuTZzfe2ak10Jd6bl60kE9yAUqw34KinJ3H4opNbs, открываем файл ~/.gnupg/sshcontrol и находим его там:

# RSA key added on: 2017-12-23 21:04:21
# Fingerprints:  MD5:0c:01:91:7c:49:40:f8:7c:74:64:74:bd:7e:c8:7a:e9
#                SHA256:hnOuTZzfe2ak10Jd6bl60kE9yAUqw34KinJ3H4opNbs
3BC01B6F0043256039006294F76C45139B703DBF 600 confirm

Агент использует keygrip ключа для его идентификации, это тоже хеш-сумма, но другая. Для нашего ключа keygrip — это 3BC01B6F0043256039006294F76C45139B703DBF. Именно эту строку будем использовать для удаления (команда может несколько раз запросить подтверждение действия):

$ gpg-connect-agent 'DELETE_KEY 3BC01B6F0043256039006294F76C45139B703DBF' /bye
OK

Убедимся, что всё получилось:

$ ssh-add -l
2048 SHA256:MApXMQ8qUqBd4yfbA07RSh+VLT+ARsg5k8fbY72LVCc /Users/sigsergv/.ssh/id_rsa-TEST2 (RSA)
2048 SHA256:8X/7LtWAFl962369KYW9ADgNCiPDUE7ilwI9QWXrBxk /Users/sigsergv/.ssh/id_rsa-INSECURE (RSA)

Ключа в списке нет, всё нормально. И дальше вручную удалите упоминания ключа из ~/.gnupg/sshcontrol.

Очистить закешированные пароли

Чтобы очистить закешированные пароли к ключам, можно перезагрузить агент такой командой:

$ gpg-connect-agent reloadagent /bye

Модификация параметров ключа в агенте

Откройте файл ~/.gnupg/sshcontrol в любом текстовом редакторе и поправьте что нужно, описание формата я давал выше. Вы можете изменить TTL, включить/выключить подтверждение, удалить ключ.

Модификация TTL по умолчанию

Откройте файл ~/.gnupg/gpg-agent.conf и поменяйте значение параметра default-cache-ttl (в секундах).

История изменений

  • 2017-12-13 — оригинальная статья
  • 2021-05-01 — новый и более эффективный способ замены штатного ssh-agent на gpg-agent в макоси

Комментарии

Текст комментария (разметка: *курсив*, **полужирная**, [ссылка](http://example.com) или <http://example.com> ещё)
Имя (обязательно, 50 символов или меньше)
Опциональный email, на который получать ответы (не будет опубликован)
Веб-сайт
© 2006—2019 Sergey Stolyarov | Работает на Pyrone