从脚本运行时避免 gpg-agent 问题 - gpg2

11
我正在尝试使用 gpg 通过脚本来进行文件的 --clearsign 操作(用于 Debian 打包目的)。
我已经导出了一个无需密码的 private-key.gpg 文件,并希望执行以下操作:
gpg --clearsign -o output input

我不想干扰当前用户的~/.gnupg/run/user/$(id -u)/gnupg,因为它们与我的脚本无关。此外,脚本可能同时运行多个实例,我不希望它们互相干扰。
我以为这很容易。将$GNUPGHOME设置为一个临时目录就可以了。但是我无法弄清楚如何在不完全干扰用户标准配置的情况下在脚本中运行gpg。似乎gpg已经竭尽全力使其不可能避免gpg-agent,而gpg-agent坚持使用全局/硬编码路径。
我能否将所有内容保留在$GNUPGHOME下?或者我该如何安全地从shell脚本中使用gpg,而不影响用户的配置或使用gpg或其他我的脚本实例?
详情请见gpg文档
--use-agent
--no-use-agent

    This is dummy option. gpg always requires the agent.

gpg-agent文档中写道:

--use-standard-socket
--no-use-standard-socket
--use-standard-socket-p

    Since GnuPG 2.1 the standard socket is always used.
    These options have no more effect. The command gpg-agent
    --use-standard-socket-p will thus always return success.

这个“标准套接字”可能位于/run/user/$(id -u)/gnupg中,因此似乎我无法避免gpg干扰用户正常使用gpg。

版本:Debian 9 / stretch / stable上的gpg 2.1.18


不确定这是否是新的,但显然Linux上的套接字目录取决于GNUPGHOME。来自GnuPG on ArchWiki:“_如果您使用非默认的GnuPG主目录,则需要编辑所有套接字文件以使用gpgconf --list-dirs的值。套接字名称使用非默认GnuPG主目录的哈希,因此您可以硬编码它而不必担心它会更改。_”在这种情况下,您无需担心套接字路径冲突。 - Franklin Yu
@FranklinYu:就像我之前写的一样,我已经尝试过更改$GNUPGHOME,你实际上已经让它工作了吗? - Peter V. Mørch
是的,它在我的机器上使用/run/user/1001/gnupg/d.19dny8pcrzpq1uj8ykkupwfr/作为套接字目录。另一个例子,GNUPGHOME=/tmp/gnupg gpgconf --list-dirs socketdir给我返回了/run/user/1001/gnupg/d.p63as1i1pzzqkuqzs6d175fn - Franklin Yu
2个回答

1
在搜索了多个小时互联网上关于启用运行不同gnupg主目录下的多个gpg-agent实例的选项后,我尝试限制对全局套接字位置的访问,这样就成功了。它会回退到将套接字文件放置在gnupg主目录中。我使用了bwrap来完成这个过程。以下是完整的可行命令:bwrap --dev-bind / / --tmpfs /run/user/$(id -u)/gnupg gpg-agent ...。由于您的问题是关于脚本的一般性问题,您可能不能依赖已安装bwrap,因此下一个最好的方法是使用一个shim来防止gpg-agent使用用户的xdg运行时目录作为其套接字。
在查看strace输出后,当在bwrap下运行时,切换位置到gnupg主目录似乎是在发出对/run/user/${UID}/gnupgstat之后发生的。通过查看gpg-agent.c代码,这似乎进行了检查:
  /* Check that it is a directory, owned by the user, and only the
   * user has permissions to use it.  */
  if (!S_ISDIR(sb.st_mode)
      || sb.st_uid != getuid ()
      || (sb.st_mode & (S_IRWXG|S_IRWXO)))
    {
      *r_info |= 4; /* Bad permissions or not a directory. */
      if (!skip_checks)
        goto leave;
    }

使用这个方法,我们可以告诉 gpg-agent 目录 /run/user/${UID}/gnupg 存在,但它不是一个目录,因此它将无法通过第一个检查。
以下是一个解决方法的代码(可能还有更好的,但它有效):
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include <stdio.h>

#define STR_MAX 4096

#define MIN(a, b) (a < b ? a : b)
#define MAX(a, b) (a > b ? a : b)

// Set up checking stuff
#define MAX_UID_LEN 11

#define PREFIX_1 "/run/user"
#define PREFIX_2 "/var/run/user"

#define TEST_LEN_1 (sizeof(PREFIX_1) - 1 + 1 + MAX_UID_LEN + sizeof("/gnupg") - 1)
#define TEST_LEN_2 (sizeof(PREFIX_2) - 1 + 1 + MAX_UID_LEN + sizeof("/gnupg") - 1)

#define MAX_TEST_LEN MAX(TEST_LEN_1, TEST_LEN_2)

// Override stat function
int stat(const char *restrict pathname, struct stat *restrict statbuf) {
    int (*original_stat)(const char *restrict, struct stat *restrict) = dlsym(RTLD_NEXT, "stat");

    // Call original stat function
    int retval = original_stat(pathname, statbuf);

    if (retval == 0) {
        // Check if a path we want to modify
        size_t pathlen = strnlen(pathname, STR_MAX);
        char path_check[MAX_TEST_LEN + 1];

        snprintf(path_check, MAX_TEST_LEN + 1, "%s/%u/gnupg", PREFIX_1, getuid());
        if (strncmp(pathname, path_check, MIN(MAX_TEST_LEN, pathlen)) == 0) {
            // Report a regular file with perms: rwxrwxrwx
            statbuf->st_mode = S_IFREG|0777;
        }

        snprintf(path_check, MAX_TEST_LEN + 1, "%s/%u/gnupg", PREFIX_2, getuid());
        if (strncmp(pathname, path_check, MIN(MAX_TEST_LEN, pathlen)) == 0) {
            // Report a regular file with perms: rwxrwxrwx
            statbuf->st_mode = S_IFREG|0777;
        }
    }

    return retval;
}

您可以使用以下命令进行编译:clang -Wall -O2 -fpic -shared -ldl -o gpg-shim.so gpg-shim.c,然后将其添加到LD_PRELOAD中,这样就可以运行多个gpg-agent,只要它们有不同的gnupg主目录即可。
我知道这个答案来得很晚,但我希望它能帮助一些像我一样寻找运行具有不同homedirs的多个gpg-agent实例的方法的人。(对于我的特定情况,我想运行多个gpg-agent实例,以便密钥缓存不同的时间)。

1
如果无法停止gpg创建文件,给gpg提供一个当前进程唯一的存放位置是否有帮助?
# Create a temporary directory for gpg.
dir="$(mktemp -d)"

# Remove the directory and its contents when the script exits.
trap '[[ ! -d "${dir}" ]] || rm -r "${dir}"' EXIT

# Put your private-key.gpg in the temporary directory.
$(your command here)

# Tell gpg to use the temporary directory.
gpg --homedir "${dir}" --clearsign -o output input

唉,gpg --homedir 就相当于我设置 $GNUPGHOME。就像我写的一样: "我以为那很容易。把 $GNUPGHOME 设置成一个临时目录,然后就能搞定了"。但是它仍然会使用 /run/user/$(id -u)/gnupg 等路径,目前我不知道有任何避免这种情况的方法。 - Peter V. Mørch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接