那天早上我像往常一样在Ubuntu服务器上执行apt update,准备更新Gitlab-ce到最新版本。突然终端跳出一行刺眼的红色错误:
bash复制Err:36 https://packages.gitlab.com/gitlab/gitlab-ce/ubuntu focal InRelease
The following signatures were invalid: EXPKEYSIG 3F01618A51312F3F GitLab B.V.
这个错误直接打断了整个更新流程。作为每天要和apt打交道的运维,我立刻意识到这是密钥验证出了问题。简单来说,Ubuntu的包管理器在下载软件包时,会用GPG密钥验证软件包的合法性。当它发现当前使用的密钥已经过期(EXPKEYSIG就是expired key signature的缩写),就会拒绝继续操作——这是Linux系统保护你免受恶意软件包侵害的重要机制。
查了Gitlab官方文档才知道,他们的仓库密钥确实设置了两年有效期,这次遇到的是2024年3月1日到期的密钥。不过官方已经将有效期延长到了2026年2月27日,只是我们本地的密钥库还不知道这个更新。这就好比你的身份证到期换了新证,但银行系统还没同步最新信息,需要你主动去更新登记。
在动手修复之前,有必要了解下Ubuntu系统中密钥管理的两种主要方式。老派的方法是使用apt-key,这是直接将密钥添加到系统级的信任链中。你可以用apt-key list查看当前所有受信的密钥,它们通常存放在/etc/apt/trusted.gpg或/etc/apt/trusted.gpg.d/目录下。
但这种方法有个明显问题:所有软件源共享同一个密钥池,缺乏隔离性。想象一下如果某个第三方源的密钥被泄露,会影响整个系统的安全性。因此从Ubuntu 20.04开始,更推荐使用signed-by方式——它为每个软件源单独指定密钥文件,实现精细化管理。
怎么判断你的系统用的是哪种方式?执行这个命令就知道了:
bash复制grep 'deb \[signed-by=' /etc/apt/sources.list.d/gitlab_gitlab-*.list
如果没有任何输出,说明在使用传统的apt-key;如果返回了类似deb [signed-by=/usr/share/keyrings/gitlab-archive-keyring.gpg]的内容,则是signed-by方式。我的测试环境显示使用的是apt-key,而生产环境已经升级到signed-by——正好可以演示两种修复方案。
对于仍在使用apt-key的老系统,修复流程相对直接。首先需要删除过期的旧密钥,注意这里的密钥ID就是错误提示中的那串字符:
bash复制sudo apt-key del 3F01618A51312F3F
接着从Gitlab官方获取最新的公钥并添加到信任列表。这里用一条管道命令完成下载和添加:
bash复制curl -s "https://packages.gitlab.com/gpg.key" | sudo apt-key add -
最后验证下新密钥是否添加成功,应该能看到更新后的有效期:
bash复制sudo apt-key list | grep -A5 3F01618A51312F3F
执行完这三步后,再次运行sudo apt update,那个烦人的EXPKEYSIG错误就应该消失了。不过要提醒的是,apt-key虽然在短期内还能用,但已经被标记为废弃(deprecated),建议尽快迁移到signed-by方案。
如果你的系统已经采用signed-by方式管理密钥,修复过程需要更精细的操作。首先定位当前使用的密钥文件路径:
bash复制awk '/deb \[signed-by=/{ pubkey = $2; sub(/\[signed-by=/, "", pubkey); sub(/\]$/, "", pubkey); print pubkey }' \
/etc/apt/sources.list.d/gitlab_gitlab-*.list
这个awk命令会提取出类似/usr/share/keyrings/gitlab-archive-keyring.gpg的路径。接下来用curl下载最新密钥并覆盖原文件:
bash复制curl -s "https://packages.gitlab.com/gpg.key" | sudo gpg --dearmor > /usr/share/keyrings/gitlab-archive-keyring.gpg
这里gpg --dearmor的作用是将ASCII格式的密钥转换为二进制格式,这是apt要求的存储形式。整个过程不需要手动删除旧密钥,因为直接覆盖文件就完成了更新。
无论采用哪种修复方式,最后都要确认密钥确实更新成功了。对于signed-by方案,可以检查密钥文件的修改时间:
bash复制ls -lh /usr/share/keyrings/gitlab-archive-keyring.gpg
更彻底的方法是直接用gpg查看密钥信息:
bash复制gpg --show-keys /usr/share/keyrings/gitlab-archive-keyring.gpg
在输出中找pub开头的行,应该能看到类似[expires: 2026-02-27]的信息。如果更新后问题依旧,可能是缓存未清除,试试:
bash复制sudo apt clean
sudo rm -rf /var/lib/apt/lists/*
sudo apt update
经历过这次EXPKEYSIG错误后,我总结了几条密钥管理的最佳实践:
定期检查密钥有效期:每月执行apt-key list或检查/usr/share/keyrings/下的文件,提前发现即将过期的密钥。
优先使用signed-by:新部署的系统都应该采用这种更安全的方式,老系统可以逐步迁移。迁移方法是在sources.list中用方括号指定密钥路径:
bash复制deb [signed-by=/usr/share/keyrings/gitlab-archive-keyring.gpg] https://packages.gitlab.com/gitlab/gitlab-ce/ubuntu focal main
备份关键密钥:将重要软件源的gpg.key文件保存在安全位置,在紧急修复时能快速获取。
订阅官方公告:像Gitlab这样的厂商通常会在密钥到期前发布通知,关注他们的博客或issue跟踪系统能提前做好准备。
那次修复之后,我在所有服务器上设置了定时任务,每周自动检查密钥有效期并邮件告警。毕竟在运维的世界里,预防永远比救火来得轻松。密钥过期看似是小问题,但如果不及时处理,可能导致关键更新无法安装,最终影响系统安全。