签名软件包与验证公钥

接触过服务器的朋友对 yumrpmaptapt-get 这些指令应该不陌生,它们属于不同 Linux 发行版的软件包管理系统(Package Management System, PMS),其历史可以追溯到上世纪 90 年代。现代操作系统中,我们常通过软件包管理器来安装或更新软件——服务器系统尤为常见,桌面系统如 macOS 的 brew 也是同类工具。软件包管理器通过从远程仓库拉取并下载,与当前系统匹配的预编译的软件包,简化安装和更新过程。

在安全层面,软件包管理器依赖数字签名技术来确保软件包的完整性和来源可信。开发者会使用自己的私钥为发布的软件包签名,而用户系统则使用开发者提供的公钥来验证下载的完整性(确保软件包完整且未被篡改或伪造)。这一过程可以由软件包管理器自动完成,也可以手动执行。例如,在基于 RPM 的系统上可以使用 rpm --checksig package.rpm 来验证签名。比如在一个centOS7服务器检测手动下载的openssl包:

rpm --checksig openssl-1.0.2k-25.el7_9.x86_64.rpm
openssl-1.0.2k-25.el7_9.x86_64.rpm: rsa sha1 (md5) pgp md5 OK

GPG

GPG 是 GNU Privacy Guard 的缩写,也称 GnuPG,是一个免费软件,用于确保分发文件的真实性。

各系统的密钥管理方式

Debian/Ubuntu 系统

早期的apt-key(已弃用)

在较早的 Debian 和 Ubuntu系统中,apt-key 工具被用来管理软件包验证公钥。用户可以通过 apt-key add filename 命令将公钥添加到系统中,通过该工具添加密钥默认被存储在 /etc/apt/trusted.gpg 文件中。apt-key list 则用于列出已添加的公钥。该命令默认会显示所有存储在 /etc/apt/trusted.gpg 以及 /etc/apt/trusted.gpg.d/ 目录下的密钥。

通过man apt-key可以看到:

apt-key(8) will last be available in Debian 12 and Ubuntu 24.04.

在Debian 12和 Ubuntu 24.04之后,apt-key 将被完全弃用,我的系统是Ubuntu 24.04 所以还可以使用list指令,同时检查到trusted.gpg.bak文件备份,可以测试

apt-key list --keyring /etc/apt/trusted.gpg.bak

该工具将遍历/etc/apt/trusted.gpg.d/目录下的每个文件,将其中的公钥与备份的公钥二进制文件合并显示。

/etc/apt/trusted.gpg.d/syncthing.gpg
------------------------------------
pub   rsa4096 2024-11-24 [SC]
      FBA2 E162 F2F4 4657 B38F  0309 E566 5F9B D597 0C47
uid           [ unknown] Syncthing Release Management <[email protected]>
sub   rsa4096 2024-11-24 [E]

/etc/apt/trusted.gpg.bak
------------------------
pub   rsa4096 2024-11-24 [SC]
      FBA2 E162 F2F4 4657 B38F  0309 E566 5F9B D597 0C47
uid           [ unknown] Syncthing Release Management <[email protected]>
sub   rsa4096 2024-11-24 [E]

pub   rsa4096 2015-03-16 [SC]
      DF00 FAF1 C577 104B 50BF  1D00 93D6 889F 9F0E 78D5
uid           [ unknown] Igor Pecovnik <[email protected]>
uid           [ unknown] Igor Pecovnik (Ljubljana, Slovenia) <[email protected]>
sub   rsa4096 2015-03-16 [E]

治标不治本:受信任的公钥列表(不推荐)

一种改良方法是将手动编辑/etc/apt/trusted.gpg.d/目录下的文件,增加.asc.gpg格式的公钥文件,文件名提供公钥用途的解释。参考apt-key 工具中的建议:

add filename (deprecated)

...

Note: Instead of using this command a keyring should be placed directly in the /etc/apt/trusted.gpg.d/ directory with a descriptive name and either "gpg" or "asc" as file extension

但这种方法实际上只是对原来单一文件进行了拆分,并不安全。在该目录下的公钥文件都会被所有仓库信赖,参考 stark overflow上的讨论[1]

推荐的细粒度公钥管理方式

上述公钥列表方案也即将被废弃,apt (2.9.24) 更新报告表示不再将该公钥列表认定为安全的配置方式[2]

apt (2.9.24) unstable; urgency=medium

/etc/apt/trusted.gpg is no longer trusted. Setting the Dir::Etc::trusted option manually continues to work for some more time.

Debian 和 Ubuntu 推荐将每个仓库的公钥存储在仅root可写的目录下,比如 /usr/share/keyrings/,并在仓库源(/etc/apt/sources.list/etc/apt/sources.list.d/ 目录)通过 signed-by 参数指定对应的公钥文件。相较于先前较为粗暴的单一白名单文件,这种细粒度的管理方式,让每个仓库公钥可以独立管理,避免公钥信任扩展,同时提高配置的灵活性。具体而言分为以下步骤

  1. 新建目录

如果没有独立的PGP钥匙串目录,新建并按推荐方式赋予权限[3]

sudo mkdir -m 0755 -p /etc/apt/keyrings/

一般而言这个过程只对于 Debian 12 和 Ubuntu 22.04 之前的系统是必须的,使用者可以自行检查是否已经有该默认目录用来存放公钥。

  1. 下载公钥

从受信任来源下载 APT 仓库的公钥文件:

wget -O- <https://example.com/key/repo-key.gpg> | gpg --dearmor | sudo tee /usr/share/keyrings/<myrepository>-archive-keyring.gpg

解释其中的指令

  • wget 从受信任来源 https://example.com/key/repo-key.gpg 下载公钥并输出至控制台 stdout (参数 -O-)。这里的 URL 应替换为你想要下载的公钥地址,比如docker
  • gpg --dearmor 使用 gpg 工具 unpack 符合 OpenPGP ASCII armor的公钥
  • sudo tee /usr/share/keyrings/<myrepository>-archive-keyring.gpg 以管理员身份(/usr/share/keyrings目录只有root写权限),读取unpack后的公钥并将其写入该目录下的文件,<myrepository>应替换为描述公钥用途的名称,按照要求一般要包含被签名仓库名称。

也可以通过 curl 下载,并通过 gpg--output <file>-o <file>合并后两条命令指定输出目录:

curl -fsSL <https://example.com/key/repo-key.gpg> | sudo gpg --dearmor --output /usr/share/keyrings/<myrepository>-archive-keyring.gpg
  1. 编辑源

上述只是添加公钥,对于软件包管理工具我们需要进一步编辑软件源,指明其使用的是哪一个公钥并验证,以apt工具为例。如果手动添加源,应该在/etc/apt/sources.list.d/新建.sources文件,文件格式可使用单行格式(one-line format)或者使用更新的 DEB822 格式(DEB822 format)。以下是两种格式的示例:

  • 单行格式(one-line format):
deb [signed-by=/usr/share/keyrings/<myrepository>-archive-keyring.gpg] https://example.com/debian stable main
  • DEB822 格式(DEB822 format):
Types: deb
URIs: https://example.com/debian
Suites: stable
Components: main
Signed-By: /usr/share/keyrings/<myrepository>-archive-keyring.gpg

apt(2.3.10)及更新版本还支持在sources.list中内嵌公钥[1]

macOS 系统

在 macOS 上,Homebrew 使用 GPG 签名来验证软件包的完整性和来源。用户可以通过 Homebrew 安装 GPG 工具,并使用 GPG 密钥对软件包进行签名和验证。

参考资料


  1. Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead ↩2

  2. APT release news

  3. Debian Wiki