在Debian或Ubuntu服务器执行 apt update 时,如果看到 NO_PUBKEY、The following signatures couldn't be verified,说明APT无法用本机信任的公钥验证该仓库的Release元数据。软件包不一定已经损坏,但这条信任链当前无法成立。
正确做法不是跳过签名检查,而是确认报错仓库、从官方渠道取得对应密钥,并用Signed-By限制它只信任该仓库。 这样既能恢复更新,也不会把第三方密钥变成全局信任。

先锁定报错的软件源和密钥指纹
重新刷新索引并完整保留输出:
sudo apt update 2>&1 | tee /tmp/apt-update.log
重点记录仓库域名、发行版代号、组件名称,以及 NO_PUBKEY 后面的长十六进制指纹。不要只凭最后8位短ID下载密钥;短ID更容易发生碰撞,也不足以证明密钥身份。
随后列出所有已配置源:
grep -RhsE '^[[:space:]]*(deb|Types:)' \
/etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null
如果同一仓库同时存在传统 .list 和新式 .sources 文件,应先消除重复项。密钥必须与具体仓库地址对应,不能看到NO_PUBKEY就随便导入搜索结果中的公钥。
判断是密钥缺失、过期还是仓库配置错误
NO_PUBKEY最常见于首次添加第三方源、系统升级后遗留旧源、仓库轮换签名密钥,以及keyring文件被删除或权限不正确。若同时出现 EXPKEYSIG,更接近密钥过期;出现 BADSIG 则要怀疑下载损坏、代理缓存或仓库端签名异常。
先核对系统代号与源中suite是否匹配:
. /etc/os-release
printf 'ID=%s VERSION_CODENAME=%s\n' "$ID" "$VERSION_CODENAME"
把第三方源暂时注释后再次执行 apt update,可以确认问题是否来自该源。不要通过修改系统时间、反复清缓存或更换镜像来掩盖指纹不匹配;这些动作不能建立可信身份。
从软件源官方渠道获取签名密钥
优先使用仓库官方安装文档给出的HTTPS地址。以通用写法为例,先创建专用目录,再下载并转换为二进制keyring:
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://packages.example.com/repository-key.gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/example.gpg
sudo chmod 0644 /etc/apt/keyrings/example.gpg
如果官方直接提供去装甲后的 .gpg 文件,可用 curl -fsSL 下载到该目录,不要重复执行 gpg --dearmor。在继续配置前检查完整指纹:
gpg --show-keys --with-fingerprint /etc/apt/keyrings/example.gpg
将输出与官方文档在另一个可信通道公布的指纹逐字符比较。下载成功只说明拿到了一个文件,指纹一致才说明拿对了密钥。
用Signed-By把密钥绑定到单一仓库
传统单行源可以这样写:
deb [signed-by=/etc/apt/keyrings/example.gpg] https://packages.example.com/debian stable main
新式deb822源可写成:
Types: deb
URIs: https://packages.example.com/debian
Suites: stable
Components: main
Signed-By: /etc/apt/keyrings/example.gpg
Signed-By 会限制该源只能使用指定keyring中的密钥,而不是让它信任系统中所有第三方密钥。keyring还必须能被 _apt 用户读取,因此常用权限是目录0755、文件0644。
不要把新密钥继续塞进全局trusted.gpg。 现代APT推荐每个仓库使用独立文件,删除某个源时也能一并移除其信任边界。
为什么不建议继续使用apt-key
apt-key add 管理的是全局受信密钥集合,任何被加入其中的密钥理论上都可能为其他未严格绑定的仓库签名。官方手册已将大多数 apt-key 用法标记为弃用,仅保留少数兼容用途。
迁移时先找出旧文件:
ls -l /etc/apt/trusted.gpg /etc/apt/trusted.gpg.d/ 2>/dev/null
不要一次性删除所有旧keyring。应逐个确认它们服务的仓库,为仍在使用的源建立独立文件和 Signed-By,刷新成功后再清理对应旧条目。迁移目标是缩小信任范围,不是简单换一个存放目录。
修复后刷新索引并做完整验证
完成配置后清理旧索引并重新拉取:
sudo rm -rf /var/lib/apt/lists/partial/*
sudo apt update
不必常规删除整个 /var/lib/apt/lists;先清理未完成下载即可。检查输出应没有NO_PUBKEY、BADSIG、EXPKEYSIG或未签名仓库警告,再查看包候选来源:
apt-cache policy package-name
确认版本确实来自预期域名和suite后,先用测试包或 apt-get -s upgrade 模拟升级。验收标准不仅是命令退出码为0,还包括来源、指纹和候选版本都符合预期。
避免不安全的临时绕过
不要设置 Acquire::AllowInsecureRepositories=true,不要给源添加 trusted=yes,也不要使用允许未认证软件包的参数。这些选项会把身份验证失败变成静默风险,攻击者或错误镜像提供的包可能进入系统。
如果业务急需恢复而官方密钥无法取得,应先禁用有问题的第三方源,让Debian或Ubuntu官方源继续更新安全补丁;需要其中软件时,可回退到已验证的内部镜像或固定的已知版本。
需要在隔离环境演练软件源迁移时,可以用 萤光云 建立同版本测试机,也可以通过 LightNode 准备临时节点。测试机仍要使用真实的签名校验,不要复制生产凭据。
建立可维护的软件源密钥流程
把每个仓库的源文件、keyring路径、完整指纹、官方获取地址和预计轮换日期记录在配置管理中。部署时固定HTTPS地址,并在导入后自动校验指纹;密钥轮换则先并行加入新密钥,验证新签名可用后再移除旧密钥。
定期检查已经下线的仓库和孤立keyring,避免服务器长期保留无用信任。真正稳定的修复应当可重复部署、可审计,并能在密钥再次轮换时快速定位。
常见问题
只有一台服务器报NO_PUBKEY,其他机器正常,是什么原因?
通常是该机缺少keyring、文件权限不对、源配置未写 Signed-By,或仍引用旧路径。对比源文件和完整指纹比复制整台机器的trusted.gpg更安全。
可以从公钥服务器按短ID获取密钥吗?
不建议。应从仓库官方文档提供的地址获取,并核对完整指纹。公钥服务器只负责分发,不能替你证明密钥属于目标仓库。
更换镜像地址后还需要重新导入密钥吗?
如果新镜像属于同一仓库并由同一密钥签名,可以复用;若运营方或Release签名发生变化,必须按新仓库文档重新验证。
温馨提示
软件源签名是服务器供应链安全的第一道门。任何以关闭认证换取apt update成功的做法,都只能算绕过故障,不能算修复。 修改前备份源文件,修改后保存完整指纹和验证记录。


