Arch Linux GRUB安全启动和一些优化配置

现在很多Linux发行版都是“开箱即用”,即安装好之后已经包含了大部分常用的软件包,一套完整的桌面环境(如果有),并且已经完成的大部分系统配置。而Arch Linux并非如此,它讲究的是高度的个性化,以及和上游保持高度一致。因此,Arch Linux若要做到“日常用起来舒适”,则需要大量的经验、能力、时间、想法去做配置,甚至有的时候会遇到坑。

GRUB就是一个典型的代表。在其他大部分Linux发行版(如Debian,Rocky Linux),都对自带的GRUB进行了少量修改,并会在安装期间或更新软件包期间自动维护GRUB的相关文件。除非需要,使用这些发行版的用户完全感知不到GRUB配置的存在。

而Arch Linux上的GRUB,只是提供了一个非常接近上游的最新软件包版本。相信大家在安装Arch Linux时都有印象,若要使用GRUB,安装GRUB相关软件包之后,在arch-chroot中需要手动使用grub-installgrub-mkconfig来生成GRUB EFI文件,并生成启动配置文件。

有些Arch Linux用户可能也希望能开启Secure Boot(安全启动),来保证Windows 11上的一些功能能正常使用。而此时,Arch Linux的一个特性,即“只提供了一个非常接近上游的最新软件包版本”,有时候也会变成一个坑。

这篇文章中我提到过的一个事情,若按照Arch Wiki的指南,一步一步将GRUB安全启动配置好,虽然最后确实能启动起来,但是可能会遇到其他Linux发行版遇不到的一个问题:字体渲染问题,即是Arch Linux这个特性的具象表现。

15F9DC3C77684EA033636916C8552797.png

这篇文章,就把我在Arch Linux上配置GRUB遇到的一些问题,解决方案,以及做的一些优化,分享出来。

这次为了方便,直接使用Hyper-V虚拟机进行实际演示,Arch Linux不安装任何界面软件,仅保留tty命令行。全程操作通过SSH进行。

实际演示效果参考

【BiliBili】 打开安全启动时的Arch Linux/Windows 11双系统切换+GRUB字体正常加载效果演示

先决条件,以及一些说明

本文已经假定,你已经按照常规的关闭安全启动的方式,已经成功安装Arch Linux,并且使用UEFI GRUB引导启动Arch Linux。

本文会把安全启动相关的配置文件存放在/etc/secureboot文件夹下,如果你需要修改保存路径,请注意本文提供的相关配置命令和文件的路径设置。

如果操作过程中发现了没有创建的文件夹,请手动创建。

进行过程中,请随身备好一个Arch Linux启动盘,以在操作错误时及时还原配置。

文中所涉及的所有命令和配置文本建议认真且逐行阅读,并且了解每条命令和配置文件的实际用途。

请不要直接照抄配置文件,请根据你的实际情况进行修改。因为你的Arch Linux只有你最熟悉应该怎么去配置。

安全启动(Secure Boot)配置

正常情况下,安装和使用Arch Linux,都是推荐关闭安全启动的。不过本文开头也提到了,部分情况下会存在打开安全启动使用Arch Linux的情况。

即便之前的一篇文章提到过常规的安全启动配置方式,这篇文章会重新概述一下配置方法,以及一些配置细节。

GRUB侧的配置

首先,安装相应的软件包:shim-signed(AUR包),sbsigntoolsmokutil

使用OpenSSL生成一对安全启动签名密钥,记得妥善保管。

sudo mkdir /etc/secureboot/keys

# Generate key pair
KEYPAIR_PATH='/etc/secureboot/keys'

sudo openssl req -newkey rsa:4096 -nodes -keyout "$KEYPAIR_PATH/MOK.key" -new -x509 -sha256 -days 3650 -subj "/CN=My Arch Linux Machine Owner Key/" -out "$KEYPAIR_PATH/MOK.crt"
sudo openssl x509 -outform DER -in "$KEYPAIR_PATH/MOK.crt" -out "$KEYPAIR_PATH/MOK.cer"

现在,我们来编写具有GRUB EFI生成和自动签名脚本。

之前有篇文章中的mok_sign函数,我拆分到了其他目录中,方便复用。

/etc/secureboot/libs/mok_sign.sh文件内容,注意修改签名密钥的路径:

mok_sign() {
    KEYPAIR_PATH='/etc/secureboot/keys'
    # sign if not already done so.
    if ! /usr/bin/sbverify --list "$1" 2>/dev/null | /usr/bin/grep -q "signature certificates"; then
        printf 'Signing %s...\n' "$1"
        sudo sbsign --key "$KEYPAIR_PATH/MOK.key" --cert "$KEYPAIR_PATH/MOK.crt" --output "$1" "$1"
    else
        printf 'Skip sign: %s\n' "$1"
    fi
}

现在,回到/etc/secureboot这个文件夹,新建update-sb-grub-efi.sh文件,内容如下(记得chmod +x给予可执行权限):

#! /bin/bash

set -u

BASIC_MODULES="all_video boot btrfs cat chain configfile echo efifwsetup efinet ext2 fat
 font gettext gfxmenu gfxterm gfxterm_background gzio halt help hfsplus iso9660 jpeg 
 keystatus loadenv loopback linux ls lsefi lsefimmap lsefisystab lssal memdisk minicmd
 normal ntfs part_apple part_msdos part_gpt password_pbkdf2 png probe read reboot regexp
 search search_fs_uuid search_fs_file search_label sleep smbios squash4 test true video videoinfo
 xfs zfs zstd zfscrypt zfsinfo cpuid play tpm usb tar"

GRUB_MODULES="$BASIC_MODULES cryptodisk crypto gcry_arcfour gcry_blowfish gcry_camellia
 gcry_cast5 gcry_crc gcry_des gcry_dsa gcry_idea gcry_md4 gcry_md5 gcry_rfc2268 gcry_rijndael
 gcry_rmd160 gcry_rsa gcry_seed gcry_serpent gcry_sha1 gcry_sha256 gcry_sha512 gcry_tiger 
 gcry_twofish gcry_whirlpool luks lvm mdraid09 mdraid1x raid5rec raid6rec"

SCRIPT_PATH="$(dirname "$(realpath $0)")"

sudo grub-mkimage -c "$SCRIPT_PATH/grub-sb-stub/grub-pre.cfg" \
    -o /boot/efi/EFI/arch/grubx64.efi -O x86_64-efi \
    --sbat "$SCRIPT_PATH/grub-sbat.csv" \
    -m "$SCRIPT_PATH/grub-sb-stub/memdisk.tar" \
    $GRUB_MODULES

source "$(dirname "$0")/libs/mok_sign.sh"

mok_sign /boot/efi/EFI/arch/grubx64.efi

这份文件是根据Ubuntu官方编译脚本,并做了适当修改制作的(这个在ArchWiki中也有提及)。也许你已经发现了,我并没有直接使用grub-install生成EFI文件,而是使用grub-mkimage实现更加定制的配置。

复制/usr/share/grub/sbat.csv/etc/secureboot/grub-sbat.csv,并可对文件做部分修改,以避免出现SBAT问题。

新建目录/etc/secureboot/grub-sb-stub,我们接下来要在这里面放一些GRUB的配置数据。

GRUB MemDisk和预加载脚本

新建文件夹/etc/secureboot/grub-sb-stub/memdisk,然后在里面新建fonts文件夹。将你需要的字体的PF2文件(比如/usr/share/grub/unicode.pf2)复制到fonts文件夹中。

随后修改当前路径到/etc/secureboot/grub-sb-stub,执行tar -cf memdisk.tar -C memdisk .。该命令会创建一个memdisk,包含我们的字体文件数据,并给前面我们创建的签名脚本使用。

创建文件/etc/secureboot/grub-sb-stub/grub-pre.cfg,根据前面的脚本的配置的设置,这个GRUB脚本文件将在GRUB启动时立刻执行。

insmod part_msdos
insmod part_gpt
insmod font

loadfont /fonts/unicode.pf2

search.fs_uuid a5c1a3cb-ff2c-43a8-83d7-9c8e8a13a33f root hd0,gpt2
set prefix=($root)/boot/grub
configfile grub.cfg

上面的配置首先加载相应的模块,读取memdisk中的字体数据(如果不考虑复杂的OpenGPG签名加载方式,这是目前安全启动下GRUB读取字体的最好办法),之后通过UUID搜索包含GRUB配置文件的分区,并立刻读取其中的grub.cfg内容。因此,你必须将search.fs_uuid中的硬盘UUID换成你系统硬盘的真实UUID。

可以通过lsblk -fs命令快速确认包含GRUB配置文件的分区UUID。

image.png

此外,如果你使用的是BTRFS,请注意set prefix那一行的/boot/grub,应该修改为/boot/grub相对于BTRFS主分区的完整路径。比如我的Arch Linux实体机系统将系统文件放在名字为@的子卷上,GRUB的/boot/grub数据也在此子卷中,则需要改为set prefix=($root)/@/boot/grub

如果之后希望读取更多字体,只需要将相应的PF2文件复制到上面创建的memdisk中,并在grub-pre.cfg中使用loadfont命令加载,并重新生成GRUB EFI文件,即可正常显示对应字体。

完成上述操作之后,回到/etc/secureboot文件夹,执行update-sb-grub-efi.sh。不出意外的话你会看到下面两行输出,即代表没有问题:

$ sudo ./update-sb-grub-efi.sh
Signing /boot/efi/EFI/arch/grubx64.efi...
Signing Unsigned original image

内核签名

现在,我们要对内核进行签名。可以在ArchWiki的这个章节找到相应的说明,不过为了方便,这里也同样说明一下我的做法。

新建/etc/initcpio/post/kernel-sbsign,内容如下,并同时使用chmod +x给予可执行权限。

#!/usr/bin/env bash

kernel="$1"
[[ -n "$kernel" ]] || exit 0

# use already installed kernel if it exists
[[ ! -f "$KERNELDESTINATION" ]] || kernel="$KERNELDESTINATION"

keypairs=(/etc/secureboot/keys/MOK.key /etc/secureboot/keys/MOK.crt)

for (( i=0; i<${#keypairs[@]}; i+=2 )); do
    key="${keypairs[$i]}" cert="${keypairs[(( i + 1 ))]}"
    if ! sbverify --cert "$cert" "$kernel" &>/dev/null; then
        sbsign --key "$key" --cert "$cert" --output "$kernel" "$kernel"
    fi
done

之后,立刻使用pacman重新安装所有已经安装的内核,不仅可以给内核打上安全启动签名,还可以确认脚本的正确性。如果在重新安装内核时,确认有下面的输出,即算配置正确。

==> Initcpio image generation successful
==> Running post hooks
  -> Running post hook: [kernel-sbsign]
Signing Unsigned original image
==> Post processing done

准备重启

在你的EFI分区下,放入之前创建的签名密钥的cer文件。我将其放入到/EFI/arch/keys/MOK.cer中。

同时复制Shim相关的已签名EFI,并添加相关的引导项。

# 复制cer文件
sudo mkdir /boot/efi/EFI/arch/keys
sudo cp /etc/secureboot/keys/MOK.cer /boot/efi/EFI/arch/keys/

# 或使用mokutil进行签名导入
mokutil --import /etc/secureboot/keys/MOK.cer

# 复制mmx64.efi和shimx64.efi
sudo cp /usr/share/shim-signed/mmx64.efi /boot/efi/EFI/arch/
sudo cp /usr/share/shim-signed/shimx64.efi /boot/efi/EFI/arch/

# 添加Shim引导选项
# /dev/sda记得改为你的EFI分区所在硬盘对应的block文件
# --part后面的1记得改成EFI分区所在分区的位置(以1开始)
sudo efibootmgr --unicode --disk /dev/sda --part 1 --create --label "arch-shim" --loader /EFI/arch/shimx64.efi

一切完成之后,重启,进入UEFI配置选项,打开安全启动,并经由arch-shim启动项启动GRUB。

在这个界面,选中我们复制的MOK.cer,并导入到Machine Owner Key列表中,重新启动,配置即可完成。

image-mok

自动更新GRUB的EFI文件和配置数据

首先,准备一下update-grub脚本。可以通过AUR包的形式安装(包名为update-grub),也可以在/usr/local/bin下新建一个。文件的内容可以参考这里

/etc/pacman.d/hooks文件夹下(没有则新建),新建两个文件(pacman hooks)。

/etc/pacman.d/hooks/1-update-grub-efi.hook,用于实时更新GRUB EFI文件:

[Trigger]
Operation=Install
Operation=Upgrade
Type=Package
Target=grub

[Action]
Description=Update GRUB UEFI binaries
When=PostTransaction
NeedsTargets
Exec=/bin/sh -c '/etc/secureboot/update-sb-grub-efi.sh'

/etc/pacman.d/hooks/999-update-grub-cfg.hook,用于在适时的时候重新生成/boot/grub/grub.cfg

[Trigger]
Operation=Install
Operation=Upgrade
Operation=Remove
Type=Package
Target=grub
Target=linux
Target=linux-lts
Target=linux-zen
Target=linux-hardened

[Action]
Description=Update GRUB configuration file
When=PostTransaction
NeedsTargets
Exec=/bin/sh -c '/usr/bin/update-grub'
Depends=grub

记得将/usr/bin/update-grub改为你的update-grub的实际路径。

重新安装GRUB,看看是否有执行pacman hook,如果成功执行则配置成功。

操作演示视频

Coming soon...

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇