写在前面:
- 请注意备份重要数据,以免操作发生意外!
- 目前飞牛官方并未对安全启动进行支持,系统安装过程中安全启动仍然需要保持关闭状态。
- 请尽可能更新BIOS/UEFI固件为设备制造商的最新状态,以免安全启动证书过期影响使用。
- 如果用户打算启用安全启动,建议在全新安装飞牛OS后就执行该操作,已有的系统(尤其是已经安装并正在使用某些DKMS驱动时)增加安全启动支持可能会出现异常,请严格按照步骤中所给的脚本进行一次手动签名。
- 虽然脚本对将来的更新系统、安装其他DKMS驱动时的操作做了全自动签名操作,以尽可能保证安全启动一直可用(笔者已经经过1.1.26-->1.1.30的更新验证,系统更新后安全启动仍然可用),但仍能十分确保一些用户自身非常规的操作会破坏信任链,导致安全启动效,从而无法启动机器。如果出现这种情况,可以尝试关闭安全启动来恢复。
- 希望飞牛官方针对于安全启动支持进行优化。该帖子后续会视官方对安全启动功能做出的变更而变化。
部署指南
一、理解信任链
UEFI 固件(内置 Microsoft 证书)
↓ 验证(Microsoft 已签名)
shimx64.efi ← 微软背书,信任链入口
↓ 验证(MOK 数据库)
grubx64.efi ← 用你的 MOK 签名
↓ 验证(MOK 数据库)
vmlinuz-* ← 用你的 MOK 签名
↓ 验证(MOK 数据库)
*.ko 内核模块 ← 用你的 MOK 签名
二、安装必要工具
sudo apt update
sudo apt install shim-signed grub-efi-amd64-signed sbsigntool mokutil dkms
三、部署 shim 到 ESP
fnOS 虽然安装了 shim 软件包,但没有自动部署到 EFI 系统分区,需要手动复制:
# 部署 shim 和 MokManager
sudo cp /usr/lib/shim/shimx64.efi.signed /boot/efi/EFI/debian/shimx64.efi
sudo cp /usr/lib/shim/mmx64.efi.signed /boot/efi/EFI/debian/mmx64.efi
# 更新 fallback 引导(部分固件只认 BOOTX64.EFI)
sudo cp /usr/lib/shim/shimx64.efi.signed /boot/efi/EFI/BOOT/BOOTX64.EFI
# 确认文件到位
sudo ls -lh /boot/efi/EFI/debian/
sudo ls -lh /boot/efi/EFI/BOOT/
四、修正 EFI 引导项指向 shim
# 删除原有引导项(原来直接指向** GRUB)
sudo efibootmgr --bootnum $(efibootmgr -v | grep -i "debian" | grep -oP 'Boot\K[0-9A-F]+') --delete-bootnum
# 创建新引导项指向 shimx64.efi
# ESP 在 /dev/nvme0n1p1,所以 --disk /dev/nvme0n1 --part 1
sudo efibootmgr \
--create \
--disk /dev/nvme0n1 \
--part 1 \
--label "fnOS" \
--loader "\\EFI\\debian\\shimx64.efi"
# 确认 BootOrder 第一项指向新引导项
sudo efibootmgr -v
预期结果:
BootOrder: 0004,...
Boot0004* fnOS Secure Boot .../File(\EFI\debian\shimx64.efi)
五、生成 MOK 密钥对
sudo mkdir -p /var/lib/shim-signed/mok
sudo chmod 700 /var/lib/shim-signed/mok
# 生成 RSA-4096 私钥 + 自签名证书
sudo openssl req -newkey rsa:4096 -nodes \
-keyout /var/lib/shim-signed/mok/MOK.key \
-new -x509 -sha256 -days 3650 \
-subj "/CN=fnOS Secure Boot MOK/" \
-out /var/lib/shim-signed/mok/MOK.crt
# 转换为 DER 格式(UEFI 固件需要)
sudo openssl x509 \
-in /var/lib/shim-signed/mok/MOK.crt \
-outform DER \
-out /var/lib/shim-signed/mok/MOK.cer
# 锁定私钥权限
sudo chmod 600 /var/lib/shim-signed/mok/MOK.key
# 确认三个文件都在
sudo ls -lh /var/lib/shim-signed/mok/
六、对内核和 GRUB 签名
KVER=$(uname -r)
MOK_KEY="/var/lib/shim-signed/mok/MOK.key"
MOK_CERT="/var/lib/shim-signed/mok/MOK.crt"
# 签名内核
sudo sbsign --key "$MOK_KEY" --cert "$MOK_CERT" \
--output /boot/vmlinuz-${KVER}.signed \
/boot/vmlinuz-${KVER}
sudo mv /boot/vmlinuz-${KVER}.signed /boot/vmlinuz-${KVER}
# 签名 GRUB
sudo sbsign --key "$MOK_KEY" --cert "$MOK_CERT" \
--output /boot/efi/EFI/debian/grubx64.efi.signed \
/boot/efi/EFI/debian/grubx64.efi
sudo mv /boot/efi/EFI/debian/grubx64.efi.signed \
/boot/efi/EFI/debian/grubx64.efi
# 验证签名
sudo sbverify --cert "$MOK_CERT" /boot/vmlinuz-${KVER} && echo "✓ 内核签名 OK"
sudo sbverify --cert "$MOK_CERT" /boot/efi/EFI/debian/grubx64.efi && echo "✓ GRUB 签名 OK"
七、部署自动签名脚本
确保 fnOS 升级内核或 GRUB 后自动重新签名,无需人工干预:
sudo nano /etc/kernel/postinst.d/zz-fnos-secure-boot
#!/bin/bash
# /etc/kernel/postinst.d/zz-fnos-secure-boot
set -euo pipefail
MOK_KEY="/var/lib/shim-signed/mok/MOK.key"
MOK_CERT="/var/lib/shim-signed/mok/MOK.crt"
LOG="/var/log/secure-boot-sign.log"
GRUB_EFI="/boot/efi/EFI/debian/grubx64.efi"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
# 核心签名函数:根据返回值判断是否执行了实际的签名操作
# 返回 0 (true) 表示进行了新签名;返回 1 (false) 表示已签名或跳过
sign_file_func() {
local TARGET="$1"
local TYPE="$2"
local KVER="${3:-}" # 仅在 TYPE 为 module 时需要
if [[ "$TYPE" == "module" ]]; then
# 优先在当前内核的 build 目录寻找 sign-file 工具
local SIGN_TOOL="/lib/modules/${KVER}/build/scripts/sign-file"
if [[ ! -f "$SIGN_TOOL" ]]; then
# 备选路径:全局寻找
SIGN_TOOL=$(find /usr/lib/linux-kbuild-* -name sign-file | head -n 1 || true)
fi
if [[ -f "$SIGN_TOOL" ]]; then
# 检查魔数:是否已签名 (支持未压缩和 zst 压缩格式)
if ! (zstd -d "$TARGET" -c 2>/dev/null || cat "$TARGET") | tail -c 28 | strings | grep -q "Module signature appended"; then
log " ✍️ 签名新增模块: $(basename "$TARGET")"
"$SIGN_TOOL" sha256 "$MOK_KEY" "$MOK_CERT" "$TARGET"
return 0 # 成功执行了新签名
fi
else
log " ❌ 错误: 找不到 sign-file 工具 (内核 ${KVER})"
fi
return 1 # 已经签名或遇到错误跳过
else # binary (内核或 EFI 文件)
if ! sbverify --cert "$MOK_CERT" "$TARGET" &>/dev/null; then
log " ✍️ 签名二进制: $(basename "$TARGET")"
sbsign --key "$MOK_KEY" --cert "$MOK_CERT" --output "${TARGET}.signed" "$TARGET" \
&& mv "${TARGET}.signed" "$TARGET"
return 0 # 成功执行了新签名
fi
return 1 # 已经签名
fi
}
log "=== 开始全局安全启动签名检查 ==="
# 1 & 2. 遍历系统中所有已安装的内核
for KERNEL_IMG in /boot/vmlinuz-*; do
# 忽略软链接或不存在的文件
[[ -f "$KERNEL_IMG" && ! -L "$KERNEL_IMG" ]] || continue
# 从文件名提取内核版本号 (例如从 vmlinuz-6.6.x 提取 6.6.x)
KERNEL_VERSION=$(basename "$KERNEL_IMG" | sed 's/vmlinuz-//')
log ">>> 检查内核版本: ${KERNEL_VERSION}"
NEED_UPDATE_INITRAMFS=0
# 签名内核镜像本身
sign_file_func "$KERNEL_IMG" "binary" || true
# 扫描该内核下的所有模块
MODULES_DIR="/lib/modules/${KERNEL_VERSION}"
if [[ -d "$MODULES_DIR" ]]; then
# 统计变量,用于日志反馈
SIGNED_COUNT=0
# 遍历所有 .ko 和 .ko.zst 文件
while IFS= read -r ko; do
if sign_file_func "$ko" "module" "$KERNEL_VERSION"; then
NEED_UPDATE_INITRAMFS=1
SIGNED_COUNT=$((SIGNED_COUNT + 1))
fi
done < <(find "$MODULES_DIR" -type f \( -name "*.ko" -o -name "*.ko.zst" \))
if [[ "$SIGNED_COUNT" -gt 0 ]]; then
log " ✓ 发现并签名了 $SIGNED_COUNT 个未签名的内核模块"
else
log " ✓ 模块均已签名,无缺失"
fi
else
log " ⚠️ 警告: 找不到对应的模块目录 ${MODULES_DIR}"
fi
# 关键步骤:按需生成该内核的 initramfs
if [[ "$NEED_UPDATE_INITRAMFS" -eq 1 ]]; then
log " 🔄 检测到 ${KERNEL_VERSION} 有新签名模块,正在更新 initramfs..."
update-initramfs -u -k "${KERNEL_VERSION}" >/dev/null
log " ✓ ${KERNEL_VERSION} initramfs 更新完毕"
fi
done
# 3. 检查并签名 GRUB
if [[ -f "$GRUB_EFI" ]]; then
if sign_file_func "$GRUB_EFI" "binary"; then
log "✓ GRUB 已重新签名"
fi
fi
# 4. 同步 BOOTX64.EFI (Fallback 引导)
SHIM_SRC="/usr/lib/shim/shimx64.efi.signed"
SHIM_DST="/boot/efi/EFI/BOOT/BOOTX64.EFI"
if [[ -f "$SHIM_SRC" ]] && ! diff -q "$SHIM_SRC" "$SHIM_DST" &>/dev/null; then
cp "$SHIM_SRC" "$SHIM_DST"
log "✓ BOOTX64.EFI 已同步更新"
fi
log "=== 全局签名检查完成 ==="
sudo chmod +x /etc/kernel/postinst.d/zz-fnos-secure-boot
APT 层兜底 hook,覆盖 fnOS 非标准更新路径:
sudo tee /etc/apt/apt.conf.d/99-secure-boot-sign > /dev/null << 'EOF'
DPkg::Post-Invoke {
"/etc/kernel/postinst.d/zz-fnos-secure-boot '' 2>&1 | tee -a /var/log/secure-boot-sign.log";
};
EOF
八、配置 DKMS 模块自动签名
DKMS 提供原生的模块签名机制,通过在 framework.conf 中直接指定密钥和证书路径,由 DKMS 在每次编译模块后自动调用内核自带的 sign-file 工具完成签名,无需外部脚本。
# 编辑 DKMS 全局配置,取消注释并指向我们的 MOK 证书
sudo sed -i \
's|^#\s*mok_signing_key=.*|mok_signing_key=/var/lib/shim-signed/mok/MOK.key|' \
/etc/dkms/framework.conf
sudo sed -i \
's|^#\s*mok_certificate=.*|mok_certificate=/var/lib/shim-signed/mok/MOK.crt|' \
/etc/dkms/framework.conf
# 确认配置已生效
grep -E "mok_signing_key|mok_certificate" /etc/dkms/framework.conf
预期输出:
mok_signing_key=/var/lib/shim-signed/mok/MOK.key
mok_certificate=/var/lib/shim-signed/mok/MOK.crt
此后每次 dkms install 编译新模块时,DKMS 会自动使用我们的 MOK 密钥对模块进行签名,无需额外干预。
此时请执行以下命令立即对系统中所有内核与 ko 模块进行一次全量签名,否则用户安装了的某些 DKMS 驱动(如 i915-sriov-dkms)可能在安全启动开启后可能无法加载:
sudo /etc/kernel/postinst.d/zz-fnos-secure-boot
九、将证书注册到 UEFI NVRAM
# 证书写入待确认队列(需要设置一个临时密码,重启时仅用一次)
sudo mokutil --import /var/lib/shim-signed/mok/MOK.cer
重启,进入 UEFI,开启安全启动,保存退出,再次重启后在蓝色 MokManager 界面操作:
Enroll MOK
→ View key 0(确认显示 CN=fnOS Secure Boot MOK)
→ Continue
→ 输入刚才设置的临时密码
→ Reboot
(若该步骤意外取消或者未触发导致启动异常,请关闭安全启动后,重新执行步骤九)
启动后验证:
# 验证安全启动已激活
sudo mokutil --sb-state
# 期望输出:SecureBoot enabled
# 验证内核认可安全启动
sudo dmesg | grep -i "secure boot"
# 查看已注册证书
sudo mokutil --list-enrolled | grep "fnOS"
# 查看签名日志
sudo tail -20 /var/log/secure-boot-sign.log
2026.4.23 更新:
- 新增逻辑:系统更新后,自动签名所有内核更新所安装的预编译 ko 模块的逻辑。解决因非 DKMS 安装的 ko 驱动无法被自动签名导致无法开机的问题。
2026.4.24 更新:
- 新增逻辑:签名系统中所有已安装内核及其所属 ko 内核模块,避免无法启动旧内核的情况。
- 问题修复:“在飞牛中执行系统修复后,因要执行大量的 apt 操作,每次都会重新生成 initramfs,导致系统修复时间会变得很长。”该问题已修复,新增逻辑:若某个内核的所有 ko 内核模块均已签名,跳过重新生成 initramfs 的操作。