固件更新验签实战:fwupd 与 LVFS 防投毒配置

固件更新验签是供应链安全里被忽视的一层:系统包有 GPG 签名、容器镜像有 cosign,而 BIOS、网卡、SSD、RAID 控制器的固件往往靠「从官网下个 exe 或者 cab 双击装」完成,既没有签名校验记录,也没有版本留痕。既然攻击者已经开始在更新服务器上做文章,把固件更新通道纳入验签与审批,是服务器安全基线里必须补上的一环。本文以 fwupd 与 LVFS 为主线,给出可执行的配置与核对方法,适用于 Linux 服务器与工作站批量运维。

适用场景

  • Linux 服务器、工作站需要批量更新 BIOS、网卡、NVMe、RAID 卡固件,且要求可追溯。
  • 内网环境无法访问外网,需要自建固件镜像并保证来源可信。
  • 等保、ISO 27001 或客户审计要求「固件更新具备完整性与来源验证」。
  • 出现固件被降级、被替换的可疑迹象,需要核对版本与签名状态。

前置条件

  • fwupd 1.8 及以上(Ubuntu 22.04+、Debian 12+、RHEL 9+ 自带版本满足)。
  • systemd 环境,ESP 分区可读写(UEFI 固件更新需要写入 capsule)。
  • root 权限;若开启 UEFI Secure Boot,需确认固件更新组件已被签名信任。
  • 可选:libjcat、cabextract 用于离线包的签名与内容核对。

原理说明

LVFS(Linux Vendor Firmware Service)把每个固件更新打成 .cab 包,同时发布一份 XML 元数据描述「哪个设备 GUID、哪个版本、对应哪个文件」。这条链路上有两处签名:

  • 元数据签名:元数据由 LVFS 用私钥签名,fwupd 内置公钥验证。校验失败时 fwupd 直接拒绝刷新,这是防「假更新源」的第一道闸。
  • 载荷签名:cab 包内的 .jcat 文件对固件二进制做 detached 签名(PKCS#7 或 Ed25519)。fwupd 安装前校验签名与设备 GUID 匹配性,签名不符不会写入。

真实世界里的风险点有三类:把 MetadataURI 指向了不可信镜像却沿用了原公钥、安装了未经审计的第三方 remote、以及最容易被忽略的——绕过 fwupd 直接用厂商 exe 或裸 bin 刷写,此时所有校验都被跳过。所以固件更新验签的核心动作是:固定可信源、强制验签、离线包人工核对哈希、更新留痕。

操作步骤

步骤一:盘点设备与当前固件版本

# 版本确认
fwupdmgr --version
systemctl status fwupd --no-pager | head -5

# 列出可更新的设备与当前版本
fwupdmgr get-devices

# 导出 JSON 台账,便于批量比对
fwupdmgr get-devices --json | tee /var/log/fwupd/inventory-$(date +%F).json

# 查看已配置的更新源
fwupdmgr get-remotes

台账要重点记录 Device ID、当前版本、Flags(是否支持 UEFI capsule、是否需要重启)。后续出现版本回退时,这份快照就是判断依据。

步骤二:收敛更新源,只保留可信 remote

默认安装通常自带 lvfs 与 lvfs-testing 两个源,测试源会推送 beta 固件,生产环境一律关掉;第三方 remote(厂商自建、社区源)没有明确签名信任链的,一并停用:

# 关闭测试源与不需要的第三方源
fwupdmgr disable-remote lvfs-testing
# 逐个确认后关闭,不要盲关
fwupdmgr get-remotes | grep -i 'Remote ID'

# 只保留生产源
fwupdmgr enable-remote lvfs

# 内网自建镜像:在 remotes.d 下写独立配置文件,不改动官方文件
# /etc/fwupd/remotes.d/lvfs-mirror.conf
[fwupd Remote]
Enabled=true
Title=LVFS Internal Mirror
MetadataURI=https://fwupd.internal.example.com/lvfs/

自建镜像必须解决一个问题:镜像里的元数据仍然是 LVFS 官方签名,fwupd 用内置公钥校验,所以只要能同步上游文件、不要重新签名,信任链就是完整的。反过来,如果镜像平台自行重签了元数据,fwupd 会直接报签名错误——这恰恰是配置正确的表现。

步骤三:强制只信任已签名固件

fwupd 提供 OnlyTrusted 开关,打开后未签名或签名无法验证的固件一律拒绝安装。这是防投毒最直接的一行配置:

# /etc/fwupd/fwupd.conf
[fwupd]
OnlyTrusted=true
UpdateMotd=false

# 生效并确认
systemctl restart fwupd
grep -E 'OnlyTrusted|UpdateMotd' /etc/fwupd/fwupd.conf
fwupdmgr get-remotes >/dev/null && echo "fwupd OK"

不同发行版打包的 fwupd 版本对配置键的支持略有差异,改完务必用 systemctl status fwupd 确认没有因未知配置项启动失败。OnlyTrusted 打开后,任何来路不明的 cab 都会安装失败,这正是期望行为,不要为了「先把活干完」临时关掉。

步骤四:刷新元数据并检查待更新列表

# 强制刷新元数据(会重新校验签名)
fwupdmgr refresh --force

# 查看可用更新,注意区分安全更新与普通版本升级
fwupdmgr get-updates

# 关注 dbx 条目:它是 UEFI 吊销列表更新,属于安全补丁
fwupdmgr get-updates | grep -iE 'dbx|security|UEFI'

刷新失败并提示签名错误,先排查三件事:系统时间是否准确(TLS 与签名时间戳都依赖它)、MetadataURI 是否被劫持到自签镜像、本地缓存的公钥是否被替换。元数据缓存在 fwupd 的本地目录下,异常时可清理后重新刷新,但清理前先备份以便取证。

步骤五:离线 cab 包的人工核对

内网环境常用离线包安装,这条路绕开了 remote,必须人工核对内容完整性:

# 1) 列出 cab 内容,确认设备与版本与官网一致
cabextract -l vendor-firmware.cab

# 2) 解包并计算哈希
mkdir -p /tmp/fwcheck && cabextract -q -d /tmp/fwcheck vendor-firmware.cab
sha256sum /tmp/fwcheck/*.bin /tmp/fwcheck/*.cap 2>/dev/null

# 3) 核对结果与 LVFS 或厂商页面公布的校验值
# 4) 通过 fwupd 安装(会做 jcat 签名与 GUID 校验)
fwupdmgr install vendor-firmware.cab

不要用厂商的裸刷写工具跳过 fwupd:那一步会同时丢掉签名校验、设备 GUID 匹配和更新历史记录。哈希核对与签名校验是两条独立防线,前者防下载被替换,后者防上游被投毒,缺一不可。

步骤六:更新执行与留痕

# 单机灰度:先在一台非核心机器上更新
fwupdmgr update

# 查看历史记录(版本、时间、结果)
fwupdmgr get-history

# 重启后确认新版本已生效
fwupdmgr get-devices | grep -A2 -iE 'BIOS|Firmware Version'

固件更新有掉电风险,执行前确认服务器接在 UPS 上,笔记本确认电量充足(fwupd 会主动检查并拒绝在低电量下更新)。批量执行建议按机房分批次,每批更新后等待一轮监控确认,再推下一批。

配置验证

# 1) 仅保留生产源,测试源已关闭
fwupdmgr get-remotes | grep -E 'lvfs|Enabled'

# 2) OnlyTrusted 生效
grep OnlyTrusted /etc/fwupd/fwupd.conf

# 3) 元数据签名校验通过(无 signature 报错)
fwupdmgr refresh --force 2>&1 | tail -3

# 4) 安全评估:查看 HSI 属性,确认固件更新与 Secure Boot 状态
fwupdmgr security

# 5) dbx 已更新到最新(需 efitools)
efi-readvar -v dbx | head -20

五项全部通过,说明更新通道是可信的:源被收敛、未签名包装不进来、元数据校验正常、UEFI 吊销列表跟得上。

常见问题

Q1:固件更新会不会把机器刷成砖?怎么降低风险?

主流商用设备普遍带双 BIOS 或回滚分区,正常签名固件刷写失败的概率很低,真正的风险来自三处:更新中掉电、跨大版本跳级、以及刷入来源不明的定制固件。降低风险的做法是接 UPS、按版本逐级升级而不是直接跳最新、只允许 fwupd 走签名校验通道。另外更新前把 fwupdmgr get-devices –json 的台账导出,万一版本异常可以立即定位是哪个组件被改了。

Q2:内网完全不能出公网,怎么保证固件来源可信?

在能出网的一台机器上同步 LVFS 官方文件到内网镜像,不要重新签名,让 fwupd 继续用内置公钥校验,这样信任链不变。内网 remote 的 MetadataURI 指向镜像目录即可。对必须手工拷贝的离线 cab 包,走步骤五的哈希核对流程,核对通过再用 fwupdmgr install 安装,禁止使用厂商裸工具直接刷写。

Q3:OnlyTrusted=true 之后有些厂商固件装不上,是不是配置错了?

不是错,是这些固件没有可验证的签名,或者签名用的证书链 fwupd 不认识。处理方式有两种:一是让厂商提供走 LVFS 的签名包;二是把该 cab 加入人工审批流程,核对哈希后临时单独放行,并在变更记录里写明原因与责任人。绝不要为了省事全局关掉 OnlyTrusted,那等于把整条验签防线作废。

总结

固件更新验签落地的动作很具体:收敛更新源只留生产 remote、打开 OnlyTrusted 强制签名校验、离线包做双份核对、更新后查历史与 HSI 状态。它补的是服务器安全基线里最底层的一环——操作系统和容器的供应链都管住了,固件通道却还敞着,等于给攻击者留了一条不用打补丁就能长期驻留的路。