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, вот команда, которая его скачивает в корректный каталог:

$ mkdir ~/Library/LaunchAgents
$ 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 SettingsGPG SuiteStore in macOS Keychain.

Также, похоже, GPG Suite игнорирует настройки времени кеширования ключа из файла ~/.gnupg/sshcontrol и всегда использует значение из собственного глобального конфига (System SettingsGPG SuiteRemember for XXXX seconds).

Особенности 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 по умолчанию.

Это значение по умолчанию формируется следующим образом:

  • если в ~/.gnupg/gpg-agent.conf указан аргумент max-cache-ttl-ssh, то значение будет сверху ограничено указанным. Если аргумент не указан, то TTL будет ограничен двумя часами независимо от значения default-cache-ttl-ssh или TTL, указанного в sshcontrol;
  • если в ~/.gnupg/gpg-agent.conf указан аргумент default-cache-ttl-ssh, то оно будет использовано. Если аргумента нет, то по умолчанию будет 30 минут.

И здесь важно отметить, что если вы в файле sshcontrol укажете TTL ключа выше максимального, то всё равно будет использовано максимальное значение. Поэтому для срока жизни больше двух часов обязательно нужно указать аргумент max-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, то нужно будет запустить агент снова, так как в процессе апгрейда, процесс агента прибивается.

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

Эта команда показывает список отпечатков ключей:

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

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

$ ssh-add -L
ssh-rsa AAAAB3NzaC1yc2E...a+oGmV /home/sigsergv/.ssh/id_rsa-TEST
ssh-rsa AAAAB3NzaC...x8t05UA8N /home/sigsergv/.ssh/id_rsa-cc

В конце каждой строчки показывает имя файла, которое было указано при добавлении ключа в команде ssh-add.

Для просмотра можно также пользоваться командой gpg-агента:

$ gpg-connect-agent 'KEYINFO --ssh-list' /bye
S KEYINFO 63ABF6149911C8987A1E849851E80BDFA727FE0F D - - 1 P - 600 Sc
S KEYINFO 75EDE7E2FAE5BD4336DB38328B23D71E8143855D D - - - P - 3600 Sc
OK

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

$ 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 секунд

$ 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! Для изменения параметров смотрите раздел Модификация параметров ключа в агенте.

GPG Suite игнорирует этот параметр.

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

$ 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, включить/выключить подтверждение, удалить ключ.

gpg-agent монитори изменения в этом файле и они применяются сразу после сохранения.

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

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

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

Работа агента ломается после второй одновременной сессии этого же пользователя

Если запустить вторую сессию с этим же пользователем (например, через RDP), то старая отваливается. Починить можно так:

$ pkill -f gpg-agent
$ gpg-connect-agent /bye
$ pkill -f gpg-agent
$ rm -rf $(dirname $SSH_AUTH_SOCK)/*

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

  • 2017-12-13 — оригинальная статья
  • 2021-05-01 — новый и более эффективный способ замены штатного ssh-agent на gpg-agent в макоси
  • 2024-06-10 — исправление ошибок и добавление информации о новых настройках
  • 2024-09-20 — добавлены уточнения об особенностях работы GPG Suite (он не использует время кеширования ключа из sshcontrol)

Комментарии

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