如何通过gpg --recv-key
导入密钥并自动检查指纹?理想情况下,我会直接使用gpg --recv-key $fingerprint
,但是gpg only recently added a check,所收到的密钥实际上具有正确的指纹,而不是盲目地信任密钥服务器,并且修复程序没有落在我关心的所有发行版中关于(例如Docker Ubuntu Image仍有旧的gpg版本)。
我希望将它与apt-key
结合使用,将PPA添加到泊坞窗容器中。
答案 0 :(得分:3)
#!/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的命令(以生成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
总是引用stdout,因此我们将使用它。然后我们将不得不找出--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
之后的第一个参数是导入的键数。
在输出中,gpg escapes newlines,因此可以匹配以[GNUPG:]
开头的行来断言我们在攻击者控制的字符串中不匹配(例如,键中的名称字段)否则可以包含\n[GNUPG:] IMPORT_OK 1 C03264CD6CADC10BFD6E708B37FD5E80BD6B6386]
并通过创建匹配来欺骗我们。
使用grep,我们可以通过[GNUPG] sometext
匹配以grep "^\[GNUPG\:\]"
开头的行,并使用grep "^\[GNUPG\:\] sometext$"
(^
和$
代表整行。和一行结束)。根据文档,IMPORT_OK
后面的任何数字对我们都没问题,因此我们会与"[[:digit:]]*"
匹配。因此,作为正则表达式,我们得到"^\[GNUPG\:\] IMPORT_OK "[[:digit:]]*" "$fingerprint"$"
和"^\[GNUPG\:\] IMPORT_RES 1"
由于我们希望将输出匹配两次,我们将其保存到一个临时文件中(通过创建一个带有mktemp
的空临时文件并重新路由该文件中的输出)。如果grep
与任何行匹配,则返回非零错误代码。我们可以通过指示bash
通过set -e
中止任何错误来使用它。总的来说,我们最终得到:
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
即可对存储库密钥环进行操作。
答案 1 :(得分:0)
实际上我已经遇到过这个页面,正在寻找类似于OP描述的问题的解决方案。提到gpg --recv-key $fingerprint
解决方案是可以的,并且应该在大多数常见发行版中得到gpg的支持。但在我的情况下,我有另一个限制。不久之前,Gpg的网络通信功能已移至单独的包dirmngr
。从Yakkety Yak(即this bug)开始,默认的Ubuntu安装中不包含该软件包,您必须手动安装它才能使上述命令工作。只要我的Docker镜像是非常小的Ubuntu设置,我就试图避免这种情况。所以我找到了一些可能有用的替代解决方案。即我正在以这种方式从位于nginx.org的存储库安装Nginx服务器:
RUN set -Eeuxo pipefail; \
# The keyring is placed in temporary directory
export GNUPGHOME="$(mktemp -d)"; \
# Nginx public key (used for signing packages and repositories)
NGINX_GPGKEY=0x573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
# Pool of high-available keyservers
KEYSERVER=ha.pool.sks-keyservers.net:11371; \
# HKP protocol can be easily represented as HTTP query. The key is imported into temporary keyring.
curl -LfSs "http://$KEYSERVER/pks/lookup?op=get&search=$NGINX_GPGKEY&options=mr&exact=on" | gpg --import -; \
# Additional check that imported key is the right one before copying it to apt trusted database
gpg --export "$NGINX_GPGKEY" | apt-key --keyring /etc/apt/trusted.gpg.d/nginx.gpg add -; \
# Adding nginx.org repository to the sources list
echo "deb [arch=amd64] http://nginx.org/packages/mainline/ubuntu/ $(. /etc/lsb-release; echo $DISTRIB_CODENAME) nginx" > /etc/apt/sources.list.d/nginx.list; \
# Installing Nginx
apt-get update; \
apt-get install -y --no-install-recommends --no-install-suggests nginx; \
# Removing apt cached files
apt-get clean; \
rm -rf /var/lib/apt/lists/*; \
# Removing temporary keyring
rm -rf "$GNUPGHOME"
代码摘自Dockerfile,在shell中运行它删除RUN
部分和所有注释(以#
开头的行)。此外,如果没有在Dockerfile中运行导出GNUPGHOME
可能会干扰正常的gpg行为。为了避免这种情况,要么在子shell中运行整个命令 - 用括号(command)
换行,要么在之后运行未设置 - unset GNUPGHOME
。因此,使用curl从密钥服务器检索密钥(普通的http查询和特定的URL参数用于模拟HKP协议)并导入临时密钥环。然后通过使用特定的KEYID参数导出它,我们在密钥服务器的答案被篡改的情况下校对密钥身份。密钥将导入到不同的密钥环/etc/apt/trusted.gpg.d/nginx.gpg
,以便以后通过删除文件轻松删除它。实际上没有必要使用apt-key
,您可以改为将gpg --export
输出重定向到文件:gpg --export "$NGINX_GPGKEY" > /etc/apt/trusted.gpg.d/nginx.gpg