收起左侧

飞牛OS启用安全启动支持

0
回复
309
查看
[ 复制链接 ]

1

主题

0

回帖

0

牛值

系统先锋体验团🛩️

写在前面:

  1. 请注意备份重要数据,以免操作发生意外!
  2. 目前飞牛官方并未对安全启动进行支持,系统安装过程中安全启动仍然需要保持关闭状态。
  3. 请尽可能更新BIOS/UEFI固件为设备制造商的最新状态,以免安全启动证书过期影响使用。
  4. 如果用户打算启用安全启动,建议在全新安装飞牛OS后就执行该操作,已有的系统(尤其是已经安装并正在使用某些DKMS驱动时)增加安全启动支持可能会出现异常,请严格按照步骤中所给的脚本进行一次手动签名。
  5. 虽然脚本对将来的更新系统、安装其他DKMS驱动时的操作做了全自动签名操作,以尽可能保证安全启动一直可用(笔者已经经过1.1.26-->1.1.30的更新验证,系统更新后安全启动仍然可用),但仍能十分确保一些用户自身非常规的操作会破坏信任链,导致安全启动效,从而无法启动机器。如果出现这种情况,可以尝试关闭安全启动来恢复。
  6. 希望飞牛官方针对于安全启动支持进行优化。该帖子后续会视官方对安全启动功能做出的变更而变化。

部署指南


一、理解信任链

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 的操作。
收藏
送赞
分享
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则