Я хочу, чтобы pinentry использовал GUI локально и CLI при подключении через SSH.

Вопрос или проблема

Мне нужно иметь возможность подписывать коммиты в git с помощью GPG в двух разных Linux-средах.

  1. В полном GUI X-Window с использованием диалогового окна для ввода пароля

  2. Через SSH, без X-forwarding, с вводом пароля только через командную строку

В обоих случаях мне нужно уметь использовать разные UID для подписания, все из которых находятся в моем ключевом хранилище. Местный и удаленный доступ используют одну и ту же учетную запись пользователя на одном компьютере, и оба могут быть активны одновременно. Компьютер работает под управлением openSUSE Leap 15 (4.12.14), с использованием GnuPG 2.2.11 и libgcrypt 1.8.4. Моя типичная удаленная сессия использует ConnectBot v1.9.5 на Android v5.1.1, хотя я также часто использую Terminus v4.3.12 на iOS 12.1.1, и время от времени могу использовать общественный ПК с Win 10.

Я не хочу “постоянного” решения, которое заставит меня всегда использовать метод CLI локально. Это повлияет на все другие использования gpg, включая электронную почту.

Прочитав этот вопрос, я попробовал несколько вариантов, которые, казалось, предлагались в ответах. Хотя решения там, включая принятое, были нацелены на постоянное решение. Я надеялся, что они приведут к решению в моем случае использования.

Я попробовал несколько вариантов настройки pinentry-command в файле gpg-agent.conf.
Во всех случаях, после изменения файла конфигурации выполняется команда gpg-connect-agent reloadagent /bye. Удаленная сессия начиналась заново после каждого изменения и тестировалась как есть, а также после каждой из следующих команд по порядку:

  1. export GPG_TTY=$(tty)
  2. unset DISPLAY
  3. gpg-connect-agent updatestartuptty /bye

Результаты оставались теми же до и после всех трех команд, и таковы:

  • Использование pinentry дает метод GUI локально – независимо от того, выполняю ли я команду локально или удаленно (Удаленный экран просто зависает до истечения времени ожидания диалогового окна).
  • Использование pinentry-tty принуждает использовать CLI во всех случаях. (Удаленное использование запрашивает ввод в удаленном терминале, а локальное использование — в локальном терминале.)
  • Использование pinentry-gtk-2 дублирует результаты pinentry
  • Использование pinentry-qt также дублирует результаты pinentry
  • Использование pinentry-curses предоставляет окно curses, но в остальном дублирует результаты pinentry-tty

Во всех случаях, если настройка приводит к вводу через командную строку локально, все не CLI использования, такие как Thunderbird, завершаются с ошибкой.

Я не буду указывать пароль в командной строке или в текстовом файле (никакой опции --passphrase-* для GnuPG).

Я не буду использовать ключ без пароля.

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

Я готов использовать опцию командной строки, если возможно, чтобы принудительно выбрать правильный вариант pinentry. Я не уверен, возможно ли передать параметры gpg через git.

Я предпочел бы возможность автоматического выбора нужного pinentry. Я также хотел бы, чтобы командная строка pinentry работала для всех команд. На данный момент, однако, мне только нужно, чтобы это работало в связи с использованием git. (Подпись часто требует одного ключа, в то время как операции push/pull требуют другого.) В настоящее время мой основной удаленный SSH-клиент может выполнять пользовательские команды при входе, поэтому сценарий, который влияет только на текущую сессию/tty, также был бы приемлем, хотя и не идеален. Изменение gpg-agent.conf для каждого соединения (вручную или посредством скриптов) было бы неприемлемо.

Был один обнадеживающий ответ для решения этой проблемы на mac. Попытка его использования с вышеизложенными опциями, однако, не привела к другим результатам. (Я также попробовал ="USE_TTY=1", на всякий случай.)

Я готов работать с предложениями в попытке найти реальное решение (командная работа, так сказать), если есть возможности, которые могут сработать, но не могут быть протестированы вне моей среды.


Текущее обходное решение

В качестве “фикса” для моей проблемы я разработал обходное решение, которое является не идеальным, но работает. Я также создал несколько оболочечных скриптов для использования обходного решения полуавтоматическим образом.

Обходное решение требует наличия двух версий gpg-agent.conf, в моем случае gpg-agent.local и gpg-agent.remote, которые различаются только одной строкой. Локальная версия содержит pinentry-program /usr/bin/pinentry, а удаленная версия имеет pinentry-program /usr/bin/pinentry-tty. gpg-agent.conf — это символическая ссылка на одну из этих версий, обычно на локальную. Чтобы переключиться на удаленную, я выполняю:

rm ~/.gnupg/gpg-agent.conf
ln -s ~/.gnupg/gpg-agent.remote ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent
gpg-connect-agent reloadagent /bye
gpg-connect-agent updatestartuptty /bye

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

Возврат к локальной версии осуществляется тем же процессом, с заменой удаленной версии на локальную в символической ссылке.

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

Если я забуду переключиться на удаленное, после паузы я понимаю, что ничего не происходит, потому что GPG отображает GUI-диалоговое окно на основном компьютере, и могу нажать CTRL+C, сделать переключение и перезапустить команду. Если я забуду вернуться на локальную версию, использование команды на локальной машине даст мне приглашение ввести пароль в CLI, и я смогу ввести пароль, а затем сбросить конфигурацию на локальное использование на оставшуюся часть сеанса.

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

Предложение пользователя Michele использовать export DBUS_SESSION_BUS_ADDRESS=/dev/null может значительно упростить то, что мне нужно делать, но все равно потребуется, чтобы я использовал эту команду вручную. Я могу добавить ее в мой текущий SSH-клиент как первую команду, но если/когда я использую другой клиент, мне все равно нужно будет помнить, чтобы использовать эту команду, и помнить, что это.

Возможно, с моим обходным решением и идеей от Michele, кто-то еще может предложить решение, которое “просто работает”.

Кажется, что (по крайней мере на моей системе) gpg фактически использует DBUS для определения места отображения запроса. Обходной путь, который заставляет его возвращаться к ncurse pinentry запросу при CLI, это установить

export DBUS_SESSION_BUS_ADDRESS=/dev/null

или другой недействительный адрес.
Я установил это в своей .bashrc, и это делает то, что запросил OP – текстовое приглашение в ncurses, когда на CLI, и GUI, когда gpg вызывается из GUI процесса.

Коренная причина этой проблемы заключается в том, что gpg-agentpinentry) разделяются между пользователями GUI и ssh/serial/терминальными пользователями.

Наиболее безшовное решение — создать скрипт pinentry-auto, например в ~/bin/

#!/bin/sh
# По умолчанию для этой альтернативы, мы могли бы разместить этот скрипт
# в /usr/bin/pinentry, чтобы избежать дополнительной настройки в других местах,
# однако, он может быть перезаписан при обновлениях.
pe=/etc/alternatives/pinentry
bin=/usr/bin
case "$PINENTRY_USER_DATA" in
*USE_TTY*)  pe=$bin/pinentry-tty  ;;
*USE_CURSES*)   pe=$bin/pinentry-curses ;;
*USE_GTK2*) pe=$bin/pinentry-gtk-2 ;;
*USE_GNOME3*)   pe=$bin/pinentry-gnome3 ;;
*USE_X11*)  pe=$bin/pinentry-x11 ;;
*USE_QT*)   pe=$bin/pinentry-qt ;;
esac
exec $pe "$@"

И сослаться на этот скрипт в gpg-agent.conf

pinentry-program bin/pinentry-auto

Переменная окружения PINENTRY_USER_DATA будет передаваться от клиента gpg агенту, а затем в pinentry-program при каждом вызове gpg, поэтому вы можете использовать это в вашей пользовательской среде для контроля того, какой pinentry следует использовать.

В вашем .bash_profile вы можете установить переменную только для ssh входов:

if [ "$SSH_CLIENT" ]; then
   # Я вошел в систему через SSH
   export PINENTRY_USER_DATA=USE_CURSES
fi

В качестве обходного пути можно использовать замену/алиас gpg2 на:

#!/bin/bash

if [ -n "$DISPLAY" ] ; then
  echo "gui |$DISPLAY|"
  /usr/bin/gpg2 $@
else
  echo "no gui"
  /usr/bin/gpg2 --pinentry-mode loopback $@
fi

но неприятно то, что GUI pinentry по-прежнему отображается на удаленном компьютере (под “удаленным” я подразумеваю тот, на который я вхожу по ssh), т.е. он невидим.

При принуждении pinentry-tty у меня по-прежнему была проблема, что в некоторых случаях без приглашения (вероятно, в зависимости от того, как/где агент был запущен) и она просто провалилась. Поэтому пока я прибегаю к добавлению в .gnupg/gpg.conf:

pinentry-mode loopback

Когда DISPLAY установлено в значение, отличное от пустого, у меня были именно те проблемы, что я не мог плавно переключаться между CLI и GUI при использовании pinentry.

Как только оно сбрасывается или пусто, и у меня установлено по умолчанию GUI pinentry (т.е. pinentry-program /usr/bin/pinentry-qt5), я могу использовать GUI-pinentry при использовании GUI, и как только я вызываю команду gpg из CLI (даже в GUI-сеансе) я могу использовать CLI-pinentry, так как GUI-варианты автоматически переключаются на CLI-вариант, когда DISPLAY не установлено.

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

Таким образом:

  • Проверьте, не установлено ли ваше DISPLAY в сценариях, где оно не должно быть установлено.
  • Установите вашу программу pinentry на GUI-вариант и воспользуйтесь ее автоматическим переключением.

Ответ или решение

Чтобы настроить GPG-систему для использования графического интерфейса (GUI) на локальной машине и командной строки (CLI) при подключении через SSH на единой машине, можно предложить несколько стратегий, которые учитывают вашу текущую конфигурацию и ограничения. Основная задача заключается в том, чтобы обеспечить возможность различного ввода пароля в зависимости от среды, в которой осуществляется GPG-операция.

Теория

Ключевая проблема заключается в выборе подходящей программы для ввода пароля, известной как pinentry. Эта утилита отвечает за запрос пароля от пользователя, и GPG использует её как в графическом, так и в текстовом режимах. Однако управление средами ввода представляет собой задачу, поскольку gpg-agent кеширует конфигурацию, и её сложно изменить без перезапуска агента или текущей сессии.

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

Пример

Ваше текущее рабочее решение предлагает смену конфигурационных файлов для gpg-agent через символьные ссылки. Это рабочий, но не идеальный метод из-за своей ручной природы и необходимости перезапуска агента. Однако, есть более динамичные способы решения задачи:

  1. Скрипт автоматического выбора pinentry: Вы можете создать скрипт, который будет выбирать правильный режим работы pinentry, основываясь на текущем окружении. Например:

    #!/bin/sh
    pe=/usr/bin/pinentry
    case "$PINENTRY_USER_DATA" in
    *USE_TTY*)  pe=/usr/bin/pinentry-tty  ;;
    *USE_CURSES*)   pe=/usr/bin/pinentry-curses ;;
    *USE_GTK2*) pe=/usr/bin/pinentry-gtk-2 ;;
    *USE_GNOME3*)   pe=/usr/bin/pinentry-gnome3 ;;
    *USE_X11*)  pe=/usr/bin/pinentry-x11 ;;
    *USE_QT*)   pe=/usr/bin/pinentry-qt ;;
    esac
    exec $pe "$@"
  2. Конфигурация gpg-agent.conf: Настройте gpg-agent.conf для использования вашего нового скрипта:

    pinentry-program /path/to/your/script
  3. Задание переменной окружения SSH: В своем SSH-скрипте или профиле, например в ~/.bash_profile, установите переменную окружения PINENTRY_USER_DATA только для SSH-сессий:

    if [ "$SSH_CLIENT" ]; then
       export PINENTRY_USER_DATA=USE_TTY
    fi

    Это позволит вашему скрипту автоматически определять текущую сессию и выбирать соответствующий режим pinentry.

Применение

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

Такой подход позволяет более разумно использовать встроенные инструменты Unix систем для адаптации к различным рабочим условиям, не жертвуя удобством и не вызывая других проблем в процессе.

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

Надеюсь, эти рекомендации помогут вам оптимизировать ваш рабочий процесс, улучшив взаимодействие с системой аутентификации GPG.

Оцените материал
Добавить комментарий

Капча загружается...