解决方案:
#!/bin/bash
set -e
tempName=$(mktemp)
gpg --status-fd 1 --keyserver keyserver.ubuntu.com --recv-keys $fingerprint 1> $tempName 2>/dev/null
grep "^\[GNUPG\:\] IMPORT_OK "[[:digit:]]*" "$fingerprint"$" $tempName
grep "^\[GNUPG\:\] IMPORT_RES 1" $tempName
如果无法下载密钥或密钥服务器返回恶意密钥,此脚本将返回非零退出代码。注意:恶意密钥仍然存在于 gpg 密钥环中,因此如果您在 Dockerfile 之外使用它,您可能需要在之后恢复原始密钥环。在 Dockerfile 中使用的命令(以添加 rust PPA 为例):
RUN echo "deb http://ppa.launchpad.net/hansjorg/rust/ubuntu trusty main" >> /etc/apt/sources.list
RUN echo "deb-src http://ppa.launchpad.net/hansjorg/rust/ubuntu trusty main" >> /etc/apt/sources.list
RUN bash -c 'set -e;tempName=$(mktemp);apt-key adv --status-fd 1 --keyserver keyserver.ubuntu.com --recv-keys C03264CD6CADC10BFD6E708B37FD5E80BD6B6386 1> $tempName 2>/dev/null;grep "^\[GNUPG\:\] IMPORT_OK [[:digit:]]* C03264CD6CADC10BFD6E708B37FD5E80BD6B6386$" $tempName;grep "^\[GNUPG\:\] IMPORT_RES 1" $tempName'
解释:
要考虑的第一个构建块是 GnuPGs --status-fd 选项。它告诉 gpg 将机器可读的输出写入给定的文件描述符。文件描述符1 总是引用标准输出,所以我们将使用它。然后我们必须找出--status-fd 的输出是什么样的。该文档不在联机帮助页中,而是在 doc/DETAILS 中。示例输出如下所示:
# gpg --status-fd 1 --keyserver keyserver.ubuntu.com --recv-keys BD6B6386 2>/dev/null
[GNUPG:] IMPORTED 37FD5E80BD6B6386 Launchpad PPA for Hans Jørgen Hoel
[GNUPG:] IMPORT_OK 1 C03264CD6CADC10BFD6E708B37FD5E80BD6B6386
[GNUPG:] IMPORT_RES 1 0 1 1 0 0 0 0 0 0 0 0 0 0
所以我们正在寻找IMPORT_OK 和IMPORT_RES 行。 IMPORT_OK之后的第二个参数是导入密钥的实际指纹。 IMPORT_RES之后的第一个参数是导入的key的个数。
在输出中,gpg escapes newlines,因此可以匹配以[GNUPG:] 开头的行,以断言我们在攻击者控制的字符串中不匹配(例如,密钥中的名称字段可能包含 @ 987654335@ 并通过创建匹配来欺骗我们。
使用 grep 我们可以匹配以[GNUPG] sometext 开头的行到grep "^\[GNUPG\:\]" 和以grep "^\[GNUPG\:\] sometext$" 匹配的整行(^ 和$ 代表一行的开始和结束)。根据文档,IMPORT_OK 后面的任何数字对我们来说都可以,所以我们匹配 "[[:digit:]]*"。因此,作为正则表达式,我们得到"^\[GNUPG\:\] IMPORT_OK "[[:digit:]]*" "$fingerprint"$" 和"^\[GNUPG\:\] IMPORT_RES 1"
由于我们想要匹配输出两次,我们将其保存到一个临时文件中(通过使用mktemp 创建一个空的临时文件并在该文件中重新路由输出)。如果grep 不匹配任何行,则返回非零错误代码。我们可以通过 set -e 指示 bash 中止任何错误来使用它。总的来说,我们最终得到:
set -e
tempName=$(mktemp)
gpg --status-fd 1 --keyserver keyserver.ubuntu.com --recv-keys $fingerprint 1> $tempName 2>/dev/null
grep "^\[GNUPG\:\] IMPORT_OK "[[:digit:]]*" "$fingerprint"$" $tempName
grep "^\[GNUPG\:\] IMPORT_RES 1" $tempName
如何使用 apt 添加存储库密钥:
apt-key 具有adv 命令将命令行参数直接交给gpg(运行上述命令而不输出重新路由以查看apt-key 生成的实际gpg 命令)。所以我们可以简单地将gpg 与apt-key adv 交换来对存储库密钥环进行操作。