GPG密钥验证是RPM系Linux保障软件包安全的核心机制,DNF在安装每一个软件包前都会校验其签名,一旦校验失败就会中止安装并抛出类似“Public key for xxx.rpm is not installed”或“The GPG keys listed for the repository are already installed but they are not correct”的错误提示。这类问题在RHEL系统中相当常见,尤其出现在更换软件源、离线导入RPM包、系统重装后恢复仓库配置等场景。要彻底解决它,需要先理解校验机制的工作原理,再根据具体报错对症下药。

一、理解DNF的GPG校验机制与报错定位
GPG校验的本质是用软件仓库的公钥去验证RPM包内嵌的签名。软件打包者用私钥对包签名,系统本地导入对应的公钥后,DNF在安装时用公钥解密签名并与包内容的哈希值比对,一致则说明包在传输过程中未被篡改。这个过程依赖两个前提:一是仓库配置中开启了gpgcheck,二是本地密钥环中存在正确且未过期的公钥。
遇到报错时,第一步是仔细阅读完整错误信息,不同报错对应的处理方向完全不同。如果提示“Public key for package xxx is not installed”,说明本地根本没有导入该仓库的公钥;如果提示“signatures could not be verified”或“key is not correct”,则可能是导入的密钥与仓库不匹配,常见于更换镜像源后密钥没有同步更换;如果提示“密钥已过期”,则需要重新获取并导入新版本密钥。可以先执行以下命令查看当前系统已导入的密钥列表:
# 查看系统中所有已导入的GPG公钥
rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n'
输出的每一行对应一个公钥,其中version字段实际是密钥的十六进制ID。将这个ID与错误信息中提到的密钥ID对比,就能快速判断是密钥缺失还是密钥错误。此外还应检查系统时间,执行date命令确认时钟是否准确,因为GPG签名带有时间戳,系统时间严重偏差可能导致验证逻辑异常。
二、导入正确的GPG公钥的三种方法
确认问题属于密钥缺失或错误后,最规范的解决方式就是导入正确的公钥。第一种方法是从密钥服务器直接导入,适合系统可以联网的场景。Red Hat官方的密钥可以从其发布页面获取,执行命令如下:
# 方法一:从密钥服务器导入
rpm --import https://www.redhat.com/security/data/fd431d51.txt
# 导入本地密钥文件(适合离线环境)
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
# 验证是否导入成功
rpm -q gpg-pubkey --qf '%{version}\n' | grep -i fd431d51
第二种方法是通过仓库配置文件自动导入。打开/etc/yum.repos.d/目录下对应的repo文件,确保gpgkey字段指向正确的密钥地址,DNF在首次使用该仓库时会自动导入:
# 查看并编辑仓库配置 vi /etc/yum.repos.d/redhat.repo # 配置文件中的关键字段 [rhel-9-baseos-rpms] name=Red Hat Enterprise Linux 9 BaseOS baseurl=https://cdn.redhat.com/content/rhel/9/$basearch/baseos/os enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
第三种方法是在执行dnf安装时让系统交互式确认导入,当仓库配置了gpgkey但本地密钥环缺失时,DNF会显示密钥指纹并询问是否导入,确认指纹与官方公布的一致后输入y即可。这种方式相对安全,因为人工核对指纹可以避免导入伪造密钥。需要强调的是,无论采用哪种方式,都务必从官方网站获取密钥文件,千万不要从不明来源下载密钥,否则整个GPG校验机制就形同虚设。
导入完成后清理缓存再重试安装:
dnf clean all dnf makecache dnf install 软件包名
三、密钥过期与密钥轮换场景的处理
RHEL在版本生命周期中会进行密钥轮换,例如从RHEL 8后期开始引入了新的签名密钥。如果系统长期未更新,旧密钥可能已被官方弃用,此时即使密钥存在也会验证失败。处理这类问题的思路是同时导入新旧密钥,让系统用匹配的那把完成校验。可以先移除疑似有问题的旧密钥再重新导入:
# 删除指定的公钥(后面跟密钥ID) rpm -e gpg-pubkey-xxxxxxxx # 重新导入官方发布的新旧密钥文件 rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta
另一个容易被忽视的坑是自建仓库或第三方仓库的场景。企业内部常用createrepo_c搭建私有仓库,如果重新签名了软件包却没有在客户端更新公钥,所有客户端都会报验证失败。运维人员应建立密钥轮换的规范流程:更换签名密钥时同步更新所有客户端,并在repo文件中使用多个gpgkey地址(用空格分隔),实现平滑过渡:
# repo文件中可以配置多个密钥地址,空格分隔
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-internal-old \
file:///etc/pki/rpm-gpg/RPM-GPG-KEY-internal-new
四、临时绕过校验的正确姿势与安全建议
在某些紧急排障场景下,比如需要临时安装一个确认过来源的本地RPM包,可以使用跳过校验的方式。安装本地包时可以加--nogpgcheck参数:
# 单次安装跳过GPG校验,仅限确认安全的包 dnf install ./package.rpm --nogpgcheck # 从仓库安装时临时关闭校验 dnf install 软件包名 --nogpgcheck
也可以临时修改repo文件,将gpgcheck设为0,但这种做法风险很高,等于完全放弃了防篡改能力,攻击者可以通过篡改仓库内容植入恶意软件。强烈建议只在临时排障时使用,问题解决后立即恢复gpgcheck=1。生产环境中的最佳实践包括:所有仓库必须开启gpgcheck,gpgkey统一指向file:///本地路径而非网络地址(防止密钥本身被劫持),定期审计密钥环中的公钥来源,以及在密钥轮换前提前分发新密钥。如果遇到的是订阅型仓库的密钥问题,还需要执行subscription-manager refresh刷新订阅证书后再重新导入密钥。通过以上方法系统化地排查和处理,DNF的GPG密钥验证失败问题基本都能得到彻底解决。