我需要能够在两个不同的 Linux 环境中对 git 提交进行 GPG 签名。
在完整的 GUI X-Window 设置中使用基于 GUI 的密码对话框
在 SSH 中,没有 X-forwarding,仅使用命令行输入密码
在这两种情况下,我都需要能够使用不同的 UID 进行签名,所有这些 UID 都在我的密钥环中。本地和远程访问在一台机器上使用同一个用户帐户,并且可能同时处于活动状态。计算机运行的是 openSUSE Leap 15 (4.12.14),使用 GnuPG 2.2.11 和 libgcrypt 1.8.4。我的典型远程连接在 Android v5.1.1 上使用 ConnectBot v1.9.5,但我也经常在 iOS 12.1.1 上使用 Terminus v4.3.12,有时我可以使用公共 Win 10 PC。
我不想要一个“永久”的解决方案,迫使我在本地使用 CLI 方法。这样做会影响 gpg 的所有其他用途,包括电子邮件。
阅读这个问题,我尝试了几种似乎可行的变体。尽管那里的解决方案(包括已接受的解决方案)旨在提供永久解决方案。我希望它们能在我的用例中找到解决方案。
我尝试了几种在文件pinentry-command
中设置的方法gpg-agent.conf
。在所有情况下,gpg-connect-agent reloadagent /bye
命令都是在配置文件更改后执行的。远程会话在任何更改后都重新启动并按原样进行测试,并在以下每个命令之后按顺序进行测试:
export GPG_TTY=$(tty)
unset DISPLAY
gpg-connect-agent updatestartuptty /bye
三个命令执行前后的结果均相同,如下所示:
- 使用
pinentry
在本地提供 GUI 方法 - 无论我是在本地还是远程执行命令(远程屏幕只是挂起,直到对话框超时。) - 在所有情况下都强制使用
pinentry-tty
CLI。(远程使用提示在远程终端中,本地使用提示在本地终端中。) - 使用
pinentry-gtk-2
重复的结果pinentry
- 使用
pinentry-qt
也会重复结果pinentry
- 使用
pinentry-curses
提供了诅咒框,但其他方面重复了结果pinentry-tty
在所有情况下,如果该设置导致本地出现命令行提示符,则所有非 CLI 使用(例如 Thunderbird)都会失败并出现错误。
我将不会在命令行或文本文件中包含密码(--passphrase-*
GnuPG 没有选项)。
我将不会使用无密码密钥。
对任何一个键进行硬编码都没有用,因为我经常在短时间内使用多个键。
如果可能的话,我将使用命令行选项来强制选择正确的 pinentry。我不确定是否可以gpg
通过 传递选项git
。
我希望 pinentry 能够自动“选择”正确的选项。我还希望命令行 pinentry 适用于所有命令。不过,目前,我只能需要它与 git 结合使用。(签名通常是一个密钥,而推送/拉取操作需要不同的密钥。)目前,我的主要远程 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 上最常做的两件事是 git commit 和 push,所以我为它们都准备了一个包装脚本,它将切换到远程 conf,执行 git 操作,并将 conf 切换回本地版本,以便可以从本地机器使用。其他一切都要求我记得在尝试使用 GPG 之前切换到远程,完成后再切换回本地。
如果我忘记切换到远程,经过一段时间的暂停后,我会意识到什么都没有发生,因为 GPG 正在主机上显示 GUI 对话框,并且可以CTRL
+C
退出,执行切换并重新运行命令。如果我忘记返回本地版本,在本地机器上使用命令会在 CLI 中给我一个密码提示,我可以输入密码,然后将配置重置为本地,以完成会话的剩余部分。
我认为这种解决方法不是最优的原因是所有操作都是手动完成的。进行切换的脚本或封装其他命令的程序(应用和重置切换)会有所帮助,但不是由系统或配置自动处理的。始终需要我的干预才能实现,并在我完成或切换终端会话时撤销所做的事情。
这建议按用户米歇尔使用export DBUS_SESSION_BUS_ADDRESS=/dev/null
可能会对我必须做的事情产生很大的影响,但仍然需要我将该命令设为手动执行的命令。我可以将其作为“第一个”命令添加到我当前的 SSH 客户端,但如果/当我使用不同的客户端时,我仍然必须记住使用该命令,并记住它是什么。
也许有了我的解决方法,以及米歇尔,其他人可能会有解决方案就是有效。
答案1
似乎(至少在我的系统上) gpg 实际上使用 DBUS 来确定在何处显示提示。在 CLI 上,让它回退到 ncurse pinentry 提示的一个解决方法是设置
导出 DBUS_SESSION_BUS_ADDRESS=/dev/null
或其他无效位置。我将其设置在 .bashrc 中,并按照 OP 的要求执行操作 - 每当在 CLI 上时,ncurses 都会提示,每当从 GUI 进程调用 gpg 时,GUI 上都会提示。
答案2
这个问题的根本原因是gpg-agent
(和pinentry
)共享GUI 和 ssh/serial/terminal 用户之间。
最无缝的解决方案是创建一个 pinentry-auto 脚本,例如在 ~/bin/
#!/bin/sh
# By defaulting to this alternative, we could place this script
# at /usr/bin/pinentry to avoid extra configuration elsewhere,
# however, it might be overwritten by upgrades.
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
# I have logged in via SSH
export PINENTRY_USER_DATA=USE_CURSES
fi
答案3
作为一种解决方法,可以使用替换/别名gpg2
:
#!/bin/bash
if [ -n "$DISPLAY" ] ; then
echo "gui |$DISPLAY|"
/usr/bin/gpg2 $@
else
echo "no gui"
/usr/bin/gpg2 --pinentry-mode loopback $@
fi
但恼人的部分是 GUIpinentry
仍然显示在远程机器上(“远程”是指我正在 ssh 连接的那台到),所以它是不可见的。
执行时pinentry-tty
我仍然遇到一个问题,即在某些情况下没有提示(可能取决于代理的启动方式/位置),并且它只是失败了。所以现在我求助于添加到.gnupg/gpg.conf
:
pinentry-mode loopback