修复 apt-key deprecated 警告

2023-07-28 ⏳3.2分钟(1.3千字) g

我的服务器使用 Ubuntu LTS 版本。之前是 20.04,升级到 22.04 之后,每次更新系统都会报一堆警告信息。虽然不影响功能,但肯定是哪里出了问题,作为强迫症患者的我肯定要一查究究。查一来发现是跟 apt-key 被弃用有关,整个 apt 的签名系统有了新的配置方案。今天就跟大家分享一下处理过程。

系统升级之后每次运行 apt-get update 都有如下警告:

W: http://security.ubuntu.com/ubuntu/dists/jammy-security/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/a
pt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.
W: http://archive.ubuntu.com/ubuntu/dists/jammy/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted
.gpg), see the DEPRECATION section in apt-key(8) for details.
W: http://archive.ubuntu.com/ubuntu/dists/jammy-updates/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt
/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.
W: http://archive.ubuntu.com/ubuntu/dists/jammy-backports/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/a
pt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.

说是上面几个软件仓库的签名公钥1还保存在 trusted.gpg 文件,现在已经不推荐使用了,具体请参考 apt-key(8) 手册的 DEPRECATION 部分。

我看了手册,主要说现在推荐把签名公钥保存到 /etc/apt/trusted.gpg.d 目录,每个 key 使用单独的文件保存。现在的问题是怎么提取上面的 key。

apt-key 有一个 list 子命令:

$ sudo apt-key list
Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).
/etc/apt/trusted.gpg
--------------------
pub   rsa4096 2018-09-17 [SC]
      F6EC B376 2474 EDA9 D21B  7022 8719 20D1 991B C93C
uid           [ unknown] Ubuntu Archive Automatic Signing Key (2018) <ftpmaster@ubuntu.com>

这是 Ubuntu 官方用的签名 key。讲道理应该在系统升级的时候自动保存到新的位置才对。现在我们需要手工导出再导入。

导出 key 需要用到它的 ID,也就是第二行的最后两部分 991B 和 C93C,合到一起,配使 apt-key export 命令:

$ sudo apt-key export 991BC93C
Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBFufwdoBEADv/Gxytx/LcSXYuM0MwKojbBye81s0G1nEx+lz6VAUpIUZnbkq
...
-----END PGP PUBLIC KEY BLOCK-----

BEGIN 和 END 之间的部分就是导出的公钥。因为 apt-key 已经不推荐使用,所以屏幕上会输出另一条警告信息:

Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).

apt-key 输出的是 ASCII 格式,但我们需要保存成二进制格式,所以需要使用 gpg 工具做转换:

sudo apt-key export 991BC93C | sudo gpg --dearmour \
  -o /etc/apt/trusted.gpg.d/ubuntu-keyring-2018-archive.gpg

-o 表示将转换后的内容保存到指定路径。执行这步操作之后再更新系统,就不会看到上面的警告了。我们现在可以删除 trusted.gpg 中的公钥:

sudo apt-key del 991BC93C

其实在我的服务器上,trusted.gpg.d 已有存在 ubuntu-keyring-2018-archive.gpg~ 文件。我也不知道是怎么创建的。直接去掉最后 ~ 就可以了。

现在更新系统就不会有告警,就很治愈~

$$ sudo aptitude update
Hit http://archive.ubuntu.com/ubuntu jammy InRelease
Get: 1 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [108 kB]
Get: 2 https://deb.goaccess.io jammy InRelease [3062 B]
Get: 3 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
Get: 4 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]
Hit https://apt.syncthing.net syncthing InRelease
Fetched 340 kB in 1s (345 kB/s)

从 trusted.gpg 到 trusted.gpg.d 是一个进步,因为可以比较方便地查看当前有哪些 key,添加删除也比较容易。但这种方式还有一个安全隐患,这就是所谓的交叉签名。

假如我们添加了 A 和 B 两个软件仓库,那么就需要添加 GAG_A 和 GBG_B 两个公钥。公钥添加之后,系统就会局信任 GAG_A 和 GBG_B,哪怕是 B 仓库的软件用了 A 的密钥来签名,系统也认,对应的软件也能正常安装。这就是所谓的交叉签名。

为了让系统安全运行,我们应该坚持最小信任原则。也就是说 GAG_A 只能用于签 A 仓库的软件,GBG_B 亦然。那么原来使用 trusted.gpg 和 trusted.gpg.d 这种全局信任的方式就不推荐了。

我的系统里装有 Syncthing2。因为 Ubunut 官方仓库里的版本较低,我配置了 Syncthing 官方的 APT 源。正好以此为例说明。

首先在创建 /etc/apt/sources.list.d/syncthing.list 文件,内容如下:

deb [signed-by=/etc/apt/keyrings/syncthing.gpg] https://apt.syncthing.net/ syncthing stable

然后下载签名公钥:

sudo curl -o /etc/apt/keyrings/syncthing.gpg https://syncthing.net/release-key.gpg

这里用 signed-by 指定签名公钥的文件路径,我把它保存到 /etc/apt/keyrings/ 目录。这也是 APT 系统推荐的位置。这样配置下来,syncthing.gpg 只能用于验证 Syncthing 官方仓库的软件。Syncthing 的签名密钥就算是意外泄露,也不会被用来签其他仓库的软件。

虽然实现这种攻击的可能性比较小,但安全无小事。所以建议大家都尽快迁移到最新的 signed-by 方案。

除了做软件签名外,GPG 还被用到各种各样的领域。离我们最近的要属 Git 了。我们可以用自己的私钥对 Git 的提交内容做签名,别人可以用我们发布的公钥验证。我们也可以把 GPT 公钥发布到 GitHub 上,GitHub 会自动显示验证标志。不过普通的开发者很少有生成自己的 GPG 官钥。后来 OpenSSH 也支持使用 SSH 密钥给任意数据做签名,一定程度上可以替换 GPG。大多数开发者应该都有自己的 SSH 官钥吧,甚至 Git 也支持使用 SSH 官钥做签名了。有兴趣的读者可以参考我的另一篇文章。