基于Grub的对Initrd签名验证的分析与实现

2023-09-20 02:20
信息记录材料 2023年8期
关键词:配置文件公钥内核

陈 剑

(浙江讯飞智能科技有限公司 浙江 杭州 311202)

0 引言

在Linux系统的安全启动中,通常会使用用户密钥来确保内核和Initrd文件的完整性和安全性。对于内核镜像,有许多方法可以进行签名验证,但对于单独的Initrd文件进行签名验证的方法却比较少见。这是因为Initrd文件通常被认为是内核镜像的一部分,因此它的安全性往往被忽略。然而在一些场景下,需要对单独的Initrd镜像进行签名验证,这就需要使用一些技术和工具来实现[1]。Grub是一款广泛使用的内核引导工具。历经多年发展,Grub已经成为一个功能强大、稳定可靠的多操作系统引导工具。

本文的方案是创建一个grub.efi的可执行文件,其中包含用户公钥,以及所需的模块和基本配置,在执行时将加载已签名的grub.cfg配置文件,该配置文件将加载已签名的Initrd和内核文件。Grub的签名验证机制是基于 GPG(GNU Privacy Guard)的数字签名验证。

1 Grub及Initrd背景介绍

Linux内核通过配置能挂载一个早期根文件系统来实现系统初始化,这种方法叫做初始RAM盘(Initrd)。通过这个早期根文件系统,来引导存储到其他分区的根文件系统,它是现代操作系统加载时必不可少的文件之一。

操作系统加载器是计算机启动时运行的第一个程序,它负责加载内核软件并将控制权转交给操作系统内核。在早年PC领域尚不成熟时,各种各样的操作系统纷繁复杂,Grub这样一款小巧强大的多系统引导工具应运而生,它最初于1995年由Erich Boleyn创建,它可以加载常见的各种开源操作系统,现在是GNU计划的一部分,由Grub开发团队维护[2]。

Grub到今天已经成长为一个非常强大和灵活的工具,具有许多好的特性。例如:(1)支持模块化。它能够将许多功能放在动态加载的模块中,常见的有命令模块(command.lst)、加密模块(crypto.lst)、文件系统模块(crypto.lst)等,这种设计能够使核心映像文件更小,以更灵活的方式进行构建。本文的签名验证将使用Grub提供的verify模块。(2)支持友好可读的配置文件。该配置文件可作为预设命令,也支持将配置文件嵌入到Grub映像中。这能够使得对Grub的开发更灵活高效。(3)提供灵活的命令接口。Grub的命令与Bash命令行非常相似,可根据上下文使用TAB完成命令、设备、分区和目录中的文件,对用户更友好。本文的介绍基于grub2.06。

2 对Initrd签名验证的意义

内核在进行初始化时,进入用户空间环境之前系统需要提供一个文件系统。为此,内核需要知道文件系统的存储位置以及该设备的驱动程序。当设备驱动程序作为组件包含在内核可执行文件中时,可能导致映像文件过大而无法在内存有限的计算机上启动,可能导致设备探测不存在或硬件冲突以至于系统崩溃或其他问题[3]。为了避免将文件系统直接编码到内核文件中所出现的这些问题,Initrd(initial RAM disk)机制得以产生,它是一个临时的根文件系统,现在被称为早期用户空间。这个根文件系统能够进行硬件检测、模块加载和设备探测,这些功能是加载真正的根文件系统前所必需的,如图1所示。

Initrd作为内核启动初始化时的临时根文件系统,很可能成为攻击者进行恶意活动的目标。若没有对Initrd进行正确的签名验证,使之被篡改或恶意替换,攻击者可能会在引导过程中注入恶意代码或获取系统的敏感信息等,从而对系统进行攻击或进行其他恶意行为,系统安全性将无法得到保障。加载签名后的Initrd,确保了它的来源和完整性,如果签名验证失败,系统将会拒绝启动,并给出错误提示,从而保护系统免受恶意软件或黑客攻击,提高系统的安全性和可靠性[4]。

3 Grub下对Initrd签名验证的实现流程

3.1 Grub端的环境搭建及配置

Grub端的环境搭建及配置如图2所示。

图2 Grub端的环境搭建及配置

对Grub源码包进行编译,以取得Grub开发环境,如图3所示。

图3 Grub源码包的编译

(1)运行./bootstrap。该脚本文件包含一些用于构建Grub引导程序的脚本和配置文件,运行后将生成configure、configure.ac等配置文件。

(2)运行./configure。该脚本文件根据当前系统的环境和用户的配置选项来生成 Makefile。configure 脚本会检查系统环境,包括操作系统类型、处理器架构、编译器等,并设置一些环境变量。它也能够读取用户提供的选项,例如安装路径、编译选项等,根据这些选项生成Makefile。其中with-platform需要被显示指定为efi,该选项用于指定要构建的平台的类型,以便在编译过程中生成适当的代码。这是因为Grub可以在不同平台上运行,并且不同的平台可能需要不同的代码来支持它们的硬件、固件和操作系统。target需要被显示指定为x86_64,该选项用于指定主机平台。

(3)运行make进行编译。编译成功后会生成Grub-mkimage、Grub-install等可执行文件。

如上文所述,Grub支持友好可读的配置文件。配置文件从存放位置看有两种:(1)嵌入式配置文件。在编译Grub映像时该配置文件将集成到映像内部,在映像得到运行时,该配置文件的命令将被顺序执行。一般该文件会配置设备根、用户口令、环境变量等。这种方式的配置文件可以在进入正常模式之前被预先加载,这样能够在外部配置文件无法找到的情况下,也能够确保应用程序的正常运行,可以避免配置文件被意外删除或者被恶意篡改,从而提高核心映像的安全性和可靠性。(2)外部配置文件。外部配置文件一般存放在/boot/grub/grub.cfg或/boot/grub/menu.lst(取决于GRUB版本和Linux发行版),并由root用户编辑。一般情况下,该配置文件是由grub-mkconfig自动生成的,不需要开发人员修改。

外部配置文件一般包括指定一些环境变量、包含必要的模块、给出图形菜单等功能[5]。

本文的嵌入式配置文件如图4所示。首先指定root变量。环境变量可以用于控制启动过程中各种配置参数的值,Grub有三个核心变量,分别是:cmdpath、prefix、root,核心变量不依赖于任何可加载模块。Root变量用于指定根文件系统所在的设备或者分区。Search.fs_uuid表示找出第一个设备id为d9002a0f-1c87-4e61-9fff-a4d5d19ef880并将其赋值给root变量。Check_signatures变量用于控制 GRUB 是否对加载的文件强制执行数字签名验证。Grub仅支持gpg方式的签名验证,使用分离式签名,签名文件在同级目录下。当该变量被设置为enforce时,在Grub下加载的所有文件将被默认检查签名文件。一般在Grub下要加载的文件有config文件、kernels文件及Initrd文件等。Export用于导出环境变量,以使其对于使用"configfile"命令载入的配置文件可见。Configurefile用于指出外部配置文件的位置。本文将其放在/boot/grub/grub.cfg.t。(hd0,gpt2)表示第一个硬盘的第二个gpt分区表,即为文件系统根目录。若执行出错或未找到外部配置文件,将打印报错信息并重启。

图4 嵌入式配置文件

本文的外部配置文件编写如图5所示。外部配置文件只需要完成系统启动功能即可。配置文件中的脚本语句可在Shell命令行中执行。在Grub中启动操作系统时,需要指定内核文件、根设备以及Initrd文件。命令linux用于从文件加载内核,同时需要给出root根设备的参数,本文使用UUID的方式获取到根设备。在执行linux命令后用Initrd命令将Initrd文件加载,Initrd命令只能在linux命令后使用。在准备好相应的参数后,即可执行boot命令启动操作系统。

图5 外部配置文件的编写

3.2 编译及运行效果

Grub2仅支持gpg方式的签名验证。在编译生成grub.efi映像时,公钥文件可作为编译选项,这样可将公钥嵌入在映像内部,在映像运行被运行后,当需要进行签名验证时,该被嵌入的公钥将被隐式引用。

GPG是一款开源密码学软件,发展至今已经较为完善,应用广泛,它是基于 PGP(pretty good privacy)机制的加密及签名软件,可以极大地保证网络用户传输及使用数据的安全性。

在操作系统进行引导时,被加载的文件有Initrd、linux内核及外部配置文件,这三个文件都需要经过正确的签名验证。步骤如下:

(1)生成GPG公私钥对。如图6所示。

图6 GPG公私钥对

--gen-key用于产生公私钥对,成功产生后将得到公钥id,公私钥文件存储在本地密钥环中,使用时将自动调用。

(2)对文件进行签名

如图7所示,编写脚本文件,定义存储公钥id的变量,使用GPG的—detach-sign选项对文件进行签名。签名成功后将在同级目录生成*.sig签名文件。图中的脚本语句对grub.cfg.test进行的签名操作,内核文件vmlinuz和Initrd文件同理。对这些文件签名后的目录如图8所示。

图7 脚本文件

图8 文件签名后的目录

将Initrd.img-5.15.0-60-generic.sig与vmlinuz-5.15.0-60-generic.sig放至/boot目录下(与Initrd和vmlinuz同级),将grub.cfg.test.sig放至/boot/efi目录下(与grub.cfg.test同级),签名文件环境即配置完成。如果在同级目录下没有相应的签名文件,将会加载失败,即无法通过签名验证,无法成功地进行引导。

Grub-mkimage用于生成Grub启动映像文件,该命令支持多种不同的选项和参数,可以根据不同的需求生成不同的启动映像文件,用户可以选择在启动映像中包含支持特定文件系统的模块,或者选择在启动映像中包含特定的内核参数来生成最合适的Grub可执行映像。

签名验证的公钥嵌入在映像文件内部,在进行签名验证时将隐式读取该公钥值。在编译映像文件时由编译选项指定公钥文件。编译选项中指定的公钥文件首先需要由公钥id导出生成公钥文件。如图9所示。

图9 公钥id导出生成公钥文件

使用Grub-mkimage编译Grub映像时,公钥文件由-k指定。-c选项用于执行嵌入式配置文件,上文已做过介绍。-p选项用于指定prefix环境变量。-d选项用于指定映像模块所在的目录。-O用于指定目标编译平台。需要包含的模块用MODULES变量进行定义,该脚本包含了一些必要的模块,例如分区模块(part_gpt)、文件系统模块(fat、ext2)、加密模块(gcry_sha512、gcry_rsa)等。正确执行该脚本后,即可得到grub.efi映像程序,如图10所示。

图10 grub.efi映像程序

在得到grub.efi可执行映像程序后,将其放在/boot/efi/grub.efi处(gpt1分区),即可在OVMF固件shell下运行。若配置文件、签名文件等都正确配置,运行grub.efi后,将正确加载配置文件中指定的内核,如图11所示。

图11 正确加载的配置文件

若某一文件的签名文件不存在或签名错误,则无法进入系统,并打印报错log,如图12所示。

图12 报错的log

4 结语

本文阐述了在Grub环境下对Initrd做签名验证的方案及其实现细节。相比于常见的将Initrd编码进内核并进行签名验证的方式,本文提出了对单独的Initrd做签名验证的方法和实现细节。现代操作系统一般将Initrd单独安排,该方案使操作系统加载时更加安全可靠。

猜你喜欢
配置文件公钥内核
提示用户配置文件错误 这样解决
强化『高新』内核 打造农业『硅谷』
互不干涉混用Chromium Edge
基于嵌入式Linux内核的自恢复设计
一种基于混沌的公钥加密方案
Linux内核mmap保护机制研究
忘记ESXi主机root密码怎么办
打印机设置
微生物内核 生态型农资
HES:一种更小公钥的同态加密算法