陳 劍
(浙江訊飛智能科技有限公司 浙江 杭州 311202)
在Linux系統(tǒng)的安全啟動中,通常會使用用戶密鑰來確保內核和Initrd文件的完整性和安全性。對于內核鏡像,有許多方法可以進行簽名驗證,但對于單獨的Initrd文件進行簽名驗證的方法卻比較少見。這是因為Initrd文件通常被認為是內核鏡像的一部分,因此它的安全性往往被忽略。然而在一些場景下,需要對單獨的Initrd鏡像進行簽名驗證,這就需要使用一些技術和工具來實現(xiàn)[1]。Grub是一款廣泛使用的內核引導工具。歷經多年發(fā)展,Grub已經成為一個功能強大、穩(wěn)定可靠的多操作系統(tǒng)引導工具。
本文的方案是創(chuàng)建一個grub.efi的可執(zhí)行文件,其中包含用戶公鑰,以及所需的模塊和基本配置,在執(zhí)行時將加載已簽名的grub.cfg配置文件,該配置文件將加載已簽名的Initrd和內核文件。Grub的簽名驗證機制是基于 GPG(GNU Privacy Guard)的數(shù)字簽名驗證。
Linux內核通過配置能掛載一個早期根文件系統(tǒng)來實現(xiàn)系統(tǒng)初始化,這種方法叫做初始RAM盤(Initrd)。通過這個早期根文件系統(tǒng),來引導存儲到其他分區(qū)的根文件系統(tǒng),它是現(xiàn)代操作系統(tǒng)加載時必不可少的文件之一。
操作系統(tǒng)加載器是計算機啟動時運行的第一個程序,它負責加載內核軟件并將控制權轉交給操作系統(tǒng)內核。在早年PC領域尚不成熟時,各種各樣的操作系統(tǒng)紛繁復雜,Grub這樣一款小巧強大的多系統(tǒng)引導工具應運而生,它最初于1995年由Erich Boleyn創(chuàng)建,它可以加載常見的各種開源操作系統(tǒng),現(xiàn)在是GNU計劃的一部分,由Grub開發(fā)團隊維護[2]。
Grub到今天已經成長為一個非常強大和靈活的工具,具有許多好的特性。例如:(1)支持模塊化。它能夠將許多功能放在動態(tài)加載的模塊中,常見的有命令模塊(command.lst)、加密模塊(crypto.lst)、文件系統(tǒng)模塊(crypto.lst)等,這種設計能夠使核心映像文件更小,以更靈活的方式進行構建。本文的簽名驗證將使用Grub提供的verify模塊。(2)支持友好可讀的配置文件。該配置文件可作為預設命令,也支持將配置文件嵌入到Grub映像中。這能夠使得對Grub的開發(fā)更靈活高效。(3)提供靈活的命令接口。Grub的命令與Bash命令行非常相似,可根據(jù)上下文使用TAB完成命令、設備、分區(qū)和目錄中的文件,對用戶更友好。本文的介紹基于grub2.06。
內核在進行初始化時,進入用戶空間環(huán)境之前系統(tǒng)需要提供一個文件系統(tǒng)。為此,內核需要知道文件系統(tǒng)的存儲位置以及該設備的驅動程序。當設備驅動程序作為組件包含在內核可執(zhí)行文件中時,可能導致映像文件過大而無法在內存有限的計算機上啟動,可能導致設備探測不存在或硬件沖突以至于系統(tǒng)崩潰或其他問題[3]。為了避免將文件系統(tǒng)直接編碼到內核文件中所出現(xiàn)的這些問題,Initrd(initial RAM disk)機制得以產生,它是一個臨時的根文件系統(tǒng),現(xiàn)在被稱為早期用戶空間。這個根文件系統(tǒng)能夠進行硬件檢測、模塊加載和設備探測,這些功能是加載真正的根文件系統(tǒng)前所必需的,如圖1所示。
Initrd作為內核啟動初始化時的臨時根文件系統(tǒng),很可能成為攻擊者進行惡意活動的目標。若沒有對Initrd進行正確的簽名驗證,使之被篡改或惡意替換,攻擊者可能會在引導過程中注入惡意代碼或獲取系統(tǒng)的敏感信息等,從而對系統(tǒng)進行攻擊或進行其他惡意行為,系統(tǒng)安全性將無法得到保障。加載簽名后的Initrd,確保了它的來源和完整性,如果簽名驗證失敗,系統(tǒng)將會拒絕啟動,并給出錯誤提示,從而保護系統(tǒng)免受惡意軟件或黑客攻擊,提高系統(tǒng)的安全性和可靠性[4]。
Grub端的環(huán)境搭建及配置如圖2所示。
圖2 Grub端的環(huán)境搭建及配置
對Grub源碼包進行編譯,以取得Grub開發(fā)環(huán)境,如圖3所示。
圖3 Grub源碼包的編譯
(1)運行./bootstrap。該腳本文件包含一些用于構建Grub引導程序的腳本和配置文件,運行后將生成configure、configure.ac等配置文件。
(2)運行./configure。該腳本文件根據(jù)當前系統(tǒng)的環(huán)境和用戶的配置選項來生成 Makefile。configure 腳本會檢查系統(tǒng)環(huán)境,包括操作系統(tǒng)類型、處理器架構、編譯器等,并設置一些環(huán)境變量。它也能夠讀取用戶提供的選項,例如安裝路徑、編譯選項等,根據(jù)這些選項生成Makefile。其中with-platform需要被顯示指定為efi,該選項用于指定要構建的平臺的類型,以便在編譯過程中生成適當?shù)拇a。這是因為Grub可以在不同平臺上運行,并且不同的平臺可能需要不同的代碼來支持它們的硬件、固件和操作系統(tǒng)。target需要被顯示指定為x86_64,該選項用于指定主機平臺。
(3)運行make進行編譯。編譯成功后會生成Grub-mkimage、Grub-install等可執(zhí)行文件。
如上文所述,Grub支持友好可讀的配置文件。配置文件從存放位置看有兩種:(1)嵌入式配置文件。在編譯Grub映像時該配置文件將集成到映像內部,在映像得到運行時,該配置文件的命令將被順序執(zhí)行。一般該文件會配置設備根、用戶口令、環(huán)境變量等。這種方式的配置文件可以在進入正常模式之前被預先加載,這樣能夠在外部配置文件無法找到的情況下,也能夠確保應用程序的正常運行,可以避免配置文件被意外刪除或者被惡意篡改,從而提高核心映像的安全性和可靠性。(2)外部配置文件。外部配置文件一般存放在/boot/grub/grub.cfg或/boot/grub/menu.lst(取決于GRUB版本和Linux發(fā)行版),并由root用戶編輯。一般情況下,該配置文件是由grub-mkconfig自動生成的,不需要開發(fā)人員修改。
外部配置文件一般包括指定一些環(huán)境變量、包含必要的模塊、給出圖形菜單等功能[5]。
本文的嵌入式配置文件如圖4所示。首先指定root變量。環(huán)境變量可以用于控制啟動過程中各種配置參數(shù)的值,Grub有三個核心變量,分別是:cmdpath、prefix、root,核心變量不依賴于任何可加載模塊。Root變量用于指定根文件系統(tǒng)所在的設備或者分區(qū)。Search.fs_uuid表示找出第一個設備id為d9002a0f-1c87-4e61-9fff-a4d5d19ef880并將其賦值給root變量。Check_signatures變量用于控制 GRUB 是否對加載的文件強制執(zhí)行數(shù)字簽名驗證。Grub僅支持gpg方式的簽名驗證,使用分離式簽名,簽名文件在同級目錄下。當該變量被設置為enforce時,在Grub下加載的所有文件將被默認檢查簽名文件。一般在Grub下要加載的文件有config文件、kernels文件及Initrd文件等。Export用于導出環(huán)境變量,以使其對于使用"configfile"命令載入的配置文件可見。Configurefile用于指出外部配置文件的位置。本文將其放在/boot/grub/grub.cfg.t。(hd0,gpt2)表示第一個硬盤的第二個gpt分區(qū)表,即為文件系統(tǒng)根目錄。若執(zhí)行出錯或未找到外部配置文件,將打印報錯信息并重啟。
圖4 嵌入式配置文件
本文的外部配置文件編寫如圖5所示。外部配置文件只需要完成系統(tǒng)啟動功能即可。配置文件中的腳本語句可在Shell命令行中執(zhí)行。在Grub中啟動操作系統(tǒng)時,需要指定內核文件、根設備以及Initrd文件。命令linux用于從文件加載內核,同時需要給出root根設備的參數(shù),本文使用UUID的方式獲取到根設備。在執(zhí)行l(wèi)inux命令后用Initrd命令將Initrd文件加載,Initrd命令只能在linux命令后使用。在準備好相應的參數(shù)后,即可執(zhí)行boot命令啟動操作系統(tǒng)。
圖5 外部配置文件的編寫
Grub2僅支持gpg方式的簽名驗證。在編譯生成grub.efi映像時,公鑰文件可作為編譯選項,這樣可將公鑰嵌入在映像內部,在映像運行被運行后,當需要進行簽名驗證時,該被嵌入的公鑰將被隱式引用。
GPG是一款開源密碼學軟件,發(fā)展至今已經較為完善,應用廣泛,它是基于 PGP(pretty good privacy)機制的加密及簽名軟件,可以極大地保證網絡用戶傳輸及使用數(shù)據(jù)的安全性。
在操作系統(tǒng)進行引導時,被加載的文件有Initrd、linux內核及外部配置文件,這三個文件都需要經過正確的簽名驗證。步驟如下:
(1)生成GPG公私鑰對。如圖6所示。
圖6 GPG公私鑰對
--gen-key用于產生公私鑰對,成功產生后將得到公鑰id,公私鑰文件存儲在本地密鑰環(huán)中,使用時將自動調用。
(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同級),簽名文件環(huán)境即配置完成。如果在同級目錄下沒有相應的簽名文件,將會加載失敗,即無法通過簽名驗證,無法成功地進行引導。
Grub-mkimage用于生成Grub啟動映像文件,該命令支持多種不同的選項和參數(shù),可以根據(jù)不同的需求生成不同的啟動映像文件,用戶可以選擇在啟動映像中包含支持特定文件系統(tǒng)的模塊,或者選擇在啟動映像中包含特定的內核參數(shù)來生成最合適的Grub可執(zhí)行映像。
簽名驗證的公鑰嵌入在映像文件內部,在進行簽名驗證時將隱式讀取該公鑰值。在編譯映像文件時由編譯選項指定公鑰文件。編譯選項中指定的公鑰文件首先需要由公鑰id導出生成公鑰文件。如圖9所示。
圖9 公鑰id導出生成公鑰文件
使用Grub-mkimage編譯Grub映像時,公鑰文件由-k指定。-c選項用于執(zhí)行嵌入式配置文件,上文已做過介紹。-p選項用于指定prefix環(huán)境變量。-d選項用于指定映像模塊所在的目錄。-O用于指定目標編譯平臺。需要包含的模塊用MODULES變量進行定義,該腳本包含了一些必要的模塊,例如分區(qū)模塊(part_gpt)、文件系統(tǒng)模塊(fat、ext2)、加密模塊(gcry_sha512、gcry_rsa)等。正確執(zhí)行該腳本后,即可得到grub.efi映像程序,如圖10所示。
圖10 grub.efi映像程序
在得到grub.efi可執(zhí)行映像程序后,將其放在/boot/efi/grub.efi處(gpt1分區(qū)),即可在OVMF固件shell下運行。若配置文件、簽名文件等都正確配置,運行grub.efi后,將正確加載配置文件中指定的內核,如圖11所示。
圖11 正確加載的配置文件
若某一文件的簽名文件不存在或簽名錯誤,則無法進入系統(tǒng),并打印報錯log,如圖12所示。
圖12 報錯的log
本文闡述了在Grub環(huán)境下對Initrd做簽名驗證的方案及其實現(xiàn)細節(jié)。相比于常見的將Initrd編碼進內核并進行簽名驗證的方式,本文提出了對單獨的Initrd做簽名驗證的方法和實現(xiàn)細節(jié)。現(xiàn)代操作系統(tǒng)一般將Initrd單獨安排,該方案使操作系統(tǒng)加載時更加安全可靠。