UEFI/安全啟動
Secure Boot是UEFI標準中的一項安全功能,旨在為pre-boot process添加一層保護;通過維護被授權或禁止的在啟動時運行的經過加密簽署的二進制文件列表,它有助於使得核心引導組件(引導管理器、內核、initramfs)不被篡改。
因此,它可以被視為對個人使用環境的securing所做努力的延續或補充,減少其他軟件安全解決方案(如系統加密)無法輕易覆蓋的攻擊面,同時完全不同且不依賴於它們。安全啟動只是作為當前安全實踐的一個組成部分獨立存在,具有自己的一套優缺點。
檢查安全啟動狀態[編輯 | 編輯原始碼]
在啟動作業系統之前[編輯 | 編輯原始碼]
此時,必須查看固件設置。如果機器已啟動並正在運行,則在大多數情況下必須重新啟動。
您可以在引導過程中通過按特殊鍵來訪問固件配置。使用的特殊鍵取決於固件。它通常是Esc
、F2
、Del
或者Fn
鍵中的一個。有時在啟動過程中特殊鍵的名稱會顯示一小段時間。主板說明書往往有關於此的記載。如果你想要按下特殊鍵,請在啟動機器後立即按下該鍵,甚至是在屏幕實際顯示任何內容之前。
進入固件設置後,請注意不要擅自更改任何設置。 通常在每個設置的底部都會有導航說明和設置的簡短幫助。設置本身可能由幾個頁面組成,您必須導航到正確的位置。有一些安全啟動設置可能簡單地表示為安全啟動選項,可以將其設置為打開或關閉 .
在啟動作業系統之後[編輯 | 編輯原始碼]
使用systemd檢查系統上的安全啟動狀態的一種簡單方法是使用systemd-boot:
$ bootctl status System: Firmware: UEFI 2.70 (American Megatrends 5.15) Secure Boot: enabled Setup Mode: user Boot into FW: supported ...
在這裏,我們看到安全啟動已啟用並強制執行。Secure Boot的其他值包括disabled
,Setup Mode的其他值包括setup
[1]。
檢查機器是否使用安全啟動的另一種方法是使用以下命令:
$ od --address-radix=n --format=u1 /sys/firmware/efi/efivars/SecureBoot-*
如果啟用了安全啟動,此命令將返回1
作為五個列表中的最後一個整數,例如:
6 0 0 0 1
但是請注意,如果使用了功能缺失的引導加載程序,內核可能不知道安全引導(即使它在固件中啟用)。 這可以通過在系統啟動後不久檢查內核消息來驗證:
# dmesg | grep -i secure
[ 0.013442] Secure boot disabled [ 0.013442] Secure boot could not be determined
能啟用的內核會打印Secure boot enabled
。
引導安裝介質[編輯 | 編輯原始碼]
安全啟動支持最初是在archlinux-2013.07.01-dual.iso
中添加的,後來在archlinux-2016.06.01-dual.iso
中被刪除。 當時 prebootloader被替換為efitools包,即使後者使用未簽署的EFI二進制文件。 從那以後,官方安裝介質中就不再支持 Secure Boot。
Archboot鏡像提供了一種在安裝介質中使用安全啟動的方式。
禁用安全啟動[編輯 | 編輯原始碼]
可以通過 UEFI 固件接口禁用安全啟動功能。如何訪問固件配置請訪問#在啟動作業系統之前.
如果使用熱鍵不起作用並且您可以啟動 Windows,您可以通過以下方式強制重新啟動到固件配置(對於 Windows 10):設置 > 更新和安全 > 恢復 > 高級啟動(立即重新啟動)> 故障排除 > 高級選項 > UEFI 固件設置 > 重啟。
請注意,某些主板(Packard Bell 筆記本電腦就是這種情況)只有在您設置了管理員密碼(之後可以刪除)時才允許禁用安全啟動。 另請參閱 Rod Smith 的禁用安全啟動。
重新製作安裝鏡像[編輯 | 編輯原始碼]
一個人或許想要像上文所說的那樣重新製作安裝鏡像。舉個例子,#PreLoader中的已簽署EFI程序PreLoader.efi
與#PreLoader中的HashTool.efi
可在此採用。另一個選項就是可以借用其他支持安全啟動的GNU+Linux發行版中的BOOTx64.EFI
(shim)以及grubx64.efi
並根據需要修改GRUB配置。在這種情況下,上述發行版安裝介質中的安全啟動認證鏈應以 grubx64.efi
為終點(Ubuntu的例子),這樣GRUB就會從archiso啟動未簽署的內核和initramfs。
注意,到目前為止,本文假定你可以訪問機器的ESP。但是當安裝一台以前從未有作業系統的機器時,就沒有ESP了。您應該閱讀其他文章,例如UEFI#從 ISO 創建 UEFI 可啟動 USB,了解如何處理這種情況。
實施安全啟動[編輯 | 編輯原始碼]
為了確保理想的安全啟動設置,有以下幾個特定條件:
- UEFI基本被認為是可信的(儘管有一些眾所周知的批評和漏洞[2]),且必須被強密碼保護。
- 不使用默認的製造商或第三方密鑰,因為它們被證明會大大削弱安全啟動的安全模型[3]。
- UEFI直接讀取帶有EFISTUB的內核鏡像(而不是引導加載程序),包含微碼(如果適用)以及 initramfs。以便在整個啟動過程維持安全啟動建立的信任鏈並減少攻擊面。
- 使用完整驅動器加密,這樣在創建內核鏡像和簽署的過程中所涉及的工具與文件就無法被具有機器物理訪問權限的人訪問並篡改。
- 可以使用TPM(可信平台模塊)獲得進一步的提升,但因為相關工具與支持使這很難實施。
#使用自己的密鑰中描述了一個簡單且完全自力更生的設置,而#使用已簽署的引導加載程序使用了第三方簽署的中間工具。
使用自己的密鑰[編輯 | 編輯原始碼]
實現安全啟動需要下列密鑰:
- 平台密鑰 Platform Key (PK):最高級的密鑰。
- 密鑰交換密鑰 Key Exchange Key (KEK):用於簽署簽名數據庫和禁止的簽名數據庫更新的密鑰。
- 簽名數據庫 Signature Database (db):包含密鑰和/或被允許的EFI二進制文件的散列值。
- 禁止的簽名數據庫 Forbidden Signatures Database (dbx):包含密鑰和/或被禁止的EFI二進制文件的散列值。
詳細解釋請閱讀所有UEFI密鑰的含義。
為了使用安全啟動,你至少需要PK,KEK和db密鑰。 雖然你可以添加多個KEK,db和dbx證書,但只允許一個平台密鑰(PK)。
一旦安全啟動處於「用戶模式」,密鑰只能通過用更高級別的密鑰簽署(使用sign-efi-sig-list)來更新。平台密鑰可以自己簽署。
安裝 efitools[編輯 | 編輯原始碼]
備份當前變量[編輯 | 編輯原始碼]
在創建新的密鑰和修改EFI變量之前,建議備份當前的變量,以便在發生錯誤時可以恢復。
運行以下命令來備份所有四個主要的安全啟動變量。
$ efi-readvar -v PK -o old_PK.esl $ efi-readvar -v KEK -o old_KEK.esl $ efi-readvar -v db -o old_db.esl $ efi-readvar -v dbx -o old_dbx.esl
如果你在新電腦或主板上執行這些命令,你提取的變量很可能是微軟提供的。
創建密鑰[編輯 | 編輯原始碼]
手動流程[編輯 | 編輯原始碼]
要生成密鑰,請執行以下步驟。
你將需要多種格式的私鑰和證書。
- .key
- PEM格式的私鑰,用於簽署EFI二進制和EFI簽名列表。
- .crt
- PEM格式的證書,用於sbsign(1)、sbvarsign(1)與sign-efi-sig-list(1)。
- .cer
- DER格式的證書,用於固件。
- .esl
- EFI簽名列表的證書,用於sbvarsign(1)、efi-updatevar(1)、KeyTool與固件。
- .auth
- EFI簽名列表中的證書,帶有認證頭(即簽名的證書更新文件),用於efi-updatevar(1)、sbkeysync、KeyTool與固件。
創建一個GUID用於自我識別:
$ uuidgen --random > GUID.txt
平台密鑰:
$ openssl req -newkey rsa:4096 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Platform Key/" -out PK.crt $ openssl x509 -outform DER -in PK.crt -out PK.cer $ cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl $ sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth
簽署一個空白文件用來在「用戶模式」中移除平台密鑰:
$ sign-efi-sig-list -g "$(< GUID.txt)" -c PK.crt -k PK.key PK /dev/null rm_PK.auth
密鑰交換密鑰:
$ openssl req -newkey rsa:4096 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Key Exchange Key/" -out KEK.crt $ openssl x509 -outform DER -in KEK.crt -out KEK.cer $ cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl $ sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth
簽名數據庫密鑰:
$ openssl req -newkey rsa:4096 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=my Signature Database key/" -out db.crt $ openssl x509 -outform DER -in db.crt -out db.cer $ cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl $ sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db db.esl db.auth
輔助腳本[編輯 | 編輯原始碼]
關於這個主題的參考頁[4]的作者提供了一個輔助/快捷腳本(需要python)。一個輕度修改的版本也被打包為sbkeysAUR。
為了使用它,請在安全位置創建一個文件夾(比如/etc/efi-keys/
,如果你接下來計劃統一內核鏡像的創建與簽署自動化過程)並且運行:
# mkdir /etc/efi-keys # cd !$ # curl -L -O https://www.rodsbooks.com/efi-bootloaders/mkkeys.sh # chmod +x mkkeys.sh # ./mkkeys.sh <輸入一個通用名,以嵌入至密鑰中,比如 "Secure Boot">
這將產生所需的不同格式的文件。
更新密鑰[編輯 | 編輯原始碼]
一旦安全啟動進入「用戶模式」,任何對KEK、db以及dbx的改變都需要更高級的密鑰簽署。
比如,如果你想要更新你的db密鑰:
- 創建新的密鑰,
- 轉換其為EFI簽名列表,
- 簽署EFI簽名列表,
- 登記已簽署的證書更新文件。
$ cert-to-efi-sig-list -g "$(< GUID.txt)" new_db.crt new_db.esl $ sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db new_db.esl new_db.auth
如果你想添加另一個密鑰至簽名數據庫中且不替換你的db密鑰,你需要使用選項-a
(請看sign-efi-sig-list(1)):
$ sign-efi-sig-list -a -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db new_db.esl new_db.auth
當new_db.auth
被創建,登記它。
簽署EFI二進制文件[編輯 | 編輯原始碼]
當安全啟動激活時(比如在「用戶模式」中),只有已被簽署的EFI二進制文件(比如應用程式、驅動、統一內核鏡像)可以被啟動。
使用sbsigntools手動操作[編輯 | 編輯原始碼]
安裝sbsigntools包來使用sbsign(1)簽署EFI二進制文件。
- 為了檢查一個二進制文件是否被簽署並列出它的簽名,使用
sbverify --list /path/to/binary
。 - rEFInd啟動管理器的
refind-install
腳本可以簽署rEFInd的EFI二進制文件並把它們與db證書一起拷貝到ESP中。 請參考rEFInd#使用你自己的密鑰。
使用sbsign來簽署你的內核與啟動管理器,比如:
# sbsign --key db.key --cert db.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux # sbsign --key db.key --cert db.crt --output esp/EFI/BOOT/BOOTx64.EFI esp/EFI/BOOT/BOOTx64.EFI
使用pacman鈎子簽署內核[編輯 | 編輯原始碼]
你也可使用mkinitcpio的pacman鈎子在安裝與更新時簽署一個內核。
複製/usr/share/libalpm/hooks/90-mkinitcpio-install.hook
到/etc/pacman.d/hooks/90-mkinitcpio-install.hook
以及複製 /usr/share/libalpm/scripts/mkinitcpio-install
到/usr/local/share/libalpm/scripts/mkinitcpio-install
。
在/etc/pacman.d/hooks/90-mkinitcpio-install.hook
裡,將:
Exec = /usr/share/libalpm/scripts/mkinitcpio-install
替換為:
Exec = /usr/local/share/libalpm/scripts/mkinitcpio-install
在/usr/local/share/libalpm/scripts/mkinitcpio-install
裡,將:
install -Dm644 "${line}" "/boot/vmlinuz-${pkgbase}"
替換為:
sbsign --key /path/to/db.key --cert /path/to/db.crt --output "/boot/vmlinuz-${pkgbase}" "${line}"
如果你正在使用systemd-boot,有一個專用的pacman鈎子會半自動的做這個任務。
用sbupdate全自動生成和簽署統一內核[編輯 | 編輯原始碼]
sbupdate是一個專門為在Arch Linux上自動生成和簽署統一內核鏡像而製作的工具。它通過Pacman#鈎子處理內核的安裝、移除和更新。
安裝sbupdate-gitAUR並按照項目主頁上的說明進行配置。[5]
OUT_DIR="EFI/Linux"
可以讓你的已簽署的內核鏡像直接被識別而不需要必須的配置。請看systemd-boot(7) § FILES與Systemd-boot#增加啟動選項。一旦完成配置,只需要以root運行sbupdate
以完成首次鏡像生成。
warning: data remaining[26413568 vs 26423180]: gaps between PE/COFF sections?
。那些是無害的且可以被安全的無視掉。[6]將固件置於 "設置模式 "下[編輯 | 編輯原始碼]
當平台密鑰被刪除時,安全啟動處於設置模式。要將固件置於設置模式,請進入固件設置工具,並找到刪除或清除證書的選項。#在啟動作業系統之前中介紹了如何進入設置工具。
在固件中登記密鑰[編輯 | 編輯原始碼]
使用下列某種方式登記'db、KEK和PK證書。
使用sbkeysync[編輯 | 編輯原始碼]
安裝sbsigntools包。按照以下目錄結構創建文件夾/etc/secureboot/keys
-
/etc/secureboot/keys ├── db ├── dbx ├── KEK └── PK
比如,這樣用:
# mkdir -p /etc/secureboot/keys/{db,dbx,KEK,PK}
接下來複製之前生成的每一個.auth文件到它們所對應的位置(比如,把PK.auth
放進/etc/secureboot/keys/PK
,以此類推)。
檢查sbkeysync
會對你系統的UEFI密鑰庫做什麼改變。
# sbkeysync --pk --dry-run --verbose
最後,使用sbkeysync
來登記你的密鑰。
# sbkeysync --verbose # sbkeysync --verbose --pk
- 如果
sbkeysync
返回了寫錯誤,那麼在執行sbkeysync
前先運行chattr -i /sys/firmware/efi/efivars/{PK,KEK,db}*
來暫時改變文件屬性,使EFI密鑰可以寫入efivars
目錄中。詳情見chattr(1)。 - 如果為
PK.auth
得到了一個permission denied
錯誤,你可以使用命令efi-updatevar -f /etc/secureboot/keys/PK/PK.auth PK
登記密鑰。 - 如果都失敗了,這可能是因為固件對BIOS設置上鎖了。對於戴爾和聯想的系統,你或許需要重置你的BIOS密碼:Sysfs固件認證文檔
對於聯想系統:
# cat > /sys/class/firmware-attributes/thinklmi/authentication/Admin/current_password
my-super-secret-password
^D
對於戴爾系統:
# cat > /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password
my-super-secret-password
^D
然後再次嘗試。下一次啟動時,UEFI應該會切回用戶模式並且執行安全啟動策略。
使用固件設置工具[編輯 | 編輯原始碼]
複製所有的*.cer
、*.esl
、*.auth
文件(除了noPK.auth
)到一個FAT格式的文件系統(你可以使用EFI 系統分區)。
noPK.auth
文件複製到你個人電腦的EFI 系統分區(ESP)!如果你這樣做了,假如有人偷了你的電腦,這個人可以刪除UEFI安全啟動固件中的個人平台密鑰,再一次打開你電腦上的「設置模式」,然後用他們自己的平台密鑰替換你的安全啟動密鑰(PK、KEK、db、dbx),這將完全違背UEFI安全啟動的目的。 應當只有你自己可以替換平台密鑰,只有你自己可以訪問noPK.auth
文件。因此把noPK.auth
文件保存在一個只有你自己能訪問的安全的地方。這些地方對於noPK.auth
文件來說都可以是安全的:
個人電腦的EFI 系統分區一定要按照UEFI的規範進行加密,否則就可以在另一台電腦上被掛載且被讀取(如果你的電腦被偷了,硬盤可以被取出來掛載到另一台電腦上)。複製noPK.auth
到你電腦的ESP後再刪除也是不可取的,因為在FAT32格式的EFI 系統分區上被刪除的文件可以通過像是PhotoRec這樣的工具被還原。
啟動固件設置工具,登記db、KEK與PK證書。 固件會有各種不同的界面,請看使用固件的設置工具更換密鑰,以了解如何登記密鑰。
如果使用的工具支持,最好使用.auth和.esl而不是.cer。
使用KeyTool[編輯 | 編輯原始碼]
KeyTool.efi
包含在efitools包包中,複製它到ESP。為了能在登記密鑰後使用它,請用sbsign
對它簽署。
# sbsign --key db.key --cert db.crt --output esp/KeyTool-signed.efi /usr/share/efitools/efi/KeyTool.efi
使用固件設置工具,引導加載程序或者UEFI Shell啟動KeyTool-signed.efi
,然後登記密鑰。
參見使用KeyTool更換密鑰了解KeyTool的菜單選項。
與其他作業系統進行雙重啟動[編輯 | 編輯原始碼]
微軟 Windows[編輯 | 編輯原始碼]
通常情況下,啟用安全引導下,僅使用私人自定義密鑰簽署微軟引導程序(EFI/Microsoft/Boot/bootmgfw.efi
)而沒有在UEFI安全啟動變量中登記「Microsoft Windows Production PCA 2011」不可能啟動Windows:
- 如果
bootmgfw.efi
同時包含來自「Microsoft Windows Production PCA 2011」和你自己的安全啟動數據庫密鑰(也就是說有兩個簽名),那麼UEFI固件實現(比如INSYDE Corp. 4096.01 (UEFI Version 2.31, Version F.70, Release Date: 07/18/2016, BIOS Revision 15.112, Firmware Revision: 29.66))將不會啟動bootmgfw.efi
,並且會拋出一個安全違規錯誤(Selected boot image did not authenticate. Press ENTER to continue.
)。像是這樣的UEFI固件實現可能只能讀取第一個簽名而忽略第二個。只有第二個簽名的證書被註冊到UEFI安全啟動變量中,所以安全啟動驗證失敗。
- 如果從
bootmgfw.efi
中剝離/移除「Microsoft Windows Production PCA 2011」證書,而只在文件中添加了你自己的安全啟動數據庫密鑰的簽名,雖然UEFI會啟動該文件但Windows將啟動一個恢復/修復環境。Windows會抱怨說:Windows的安裝被破壞了(因為bootmgfw.efi
中缺失了「Microsoft Windows Production PCA 2011」證書)。
所以為了實現雙系統:
- 你必須把
bootmgfw.efi
的散列值添加到DB
變量中允許的散列值列表中,而且每次Windows更新改變bootmgfw.efi
時,你都必須更新DB變量。這非常繁瑣,容易出錯,而且不被微軟支持,例如,Bitlocker在這種設置下將無法正常工作(Bitlocker每次解密Windows分區時都會要求你提供恢復密碼)。 - 或者你必須將微軟的證書添加到UEFI安全啟動的變量中,微軟有兩個
DB
證書和一個KEK
證書:
- 為了使Windows作業系統可以被引導,Microsoft Windows Production PCA 2011 證書必須被包含進
DB
變量。 - 為了使用第三方二進制,比如UEFI驅動、其他ROM、shim包等,Microsoft Corporation UEFI CA 2011 證書(又叫作微軟第三方UEFI CA證書)必須被包含進
DB
變量。 - 為了「通過更新
DBX
和潛在更新DB
來撤銷不良鏡像對新的Windows簽名鏡像做準備」,Microsoft Corporation KEK CA 2011 證書應當被包含進KEK
變量。然而沒有「Microsoft Corporation KEK CA 2011」證書,Windows也可以啟動。
- 為了使Windows作業系統可以被引導,Microsoft Windows Production PCA 2011 證書必須被包含進
使用微軟的GUID(77fa9abd-0359-4d32-bd60-28f4e78f784b
),根據微軟的多個DER格式的DB
證書創建多個EFI簽名列表。然後為了方便,把它們合併進一個文件中:
$ sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output MS_Win_db.esl MicWinProPCA2011_2011-10-19.crt $ sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output MS_UEFI_db.esl MicCorUEFCA2011_2011-06-27.crt $ cat MS_Win_db.esl MS_UEFI_db.esl > MS_db.esl
可選操作(為了嚴格符合的微軟UEFI安全啟動要求):使用微軟的GUID(77fa9abd-0359-4d32-bd60-28f4e78f784b
),根據微軟的KEK格式的DB
證書創建EFI簽名列表:
$ sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output MS_Win_KEK.esl MicCorKEKCA2011_2011-06-24.crt
使用你自己的KEK
簽署一個DB
變量更新。使用帶有-a
選項的sign-efi-sig-list
來添加而不是替換一個DB
證書:
$ sign-efi-sig-list -a -g 77fa9abd-0359-4d32-bd60-28f4e78f784b -k KEK.key -c KEK.crt db MS_db.esl add_MS_db.auth
可選操作(為了嚴格符合微軟的UEFI安全啟動要求):使用你自己的PK
簽署一個KEK
變量更新。使用帶有-a
選項的sign-efi-sig-list
來添加而不是替換一個KEK
證書:
$ sign-efi-sig-list -a -g 77fa9abd-0359-4d32-bd60-28f4e78f784b -k PK.key -c PK.crt KEK MS_Win_KEK.esl add_MS_Win_KEK.auth
按照#在固件中登記密鑰的內容來登記add_MS_db.auth
,為了嚴格符合微軟的UEFI安全啟動要求,將add_MS_Win_KEK.auth
放入UEFI安全啟動數據庫的變量中。
使用已簽署的引導加載程序[編輯 | 編輯原始碼]
使用已簽署的引導加載程序意味着使用由微軟的密鑰簽署的引導加載程序。有兩個已知的簽署的引導加載程序:PreLoader和shim。它們的目的是連鎖加載其他EFI二進制文件(通常是引導加載程序)。由於微軟不會簽署一個自動啟動任何未簽署的二進制文件的引導加載程序,PreLoader和shim使用一個叫做機器所有者密鑰列表的白名單,縮寫為MokList。如果二進制文件的SHA256散列值(Preloader和shim)或簽署二進制文件的密鑰(shim)在MokList中,它們就執行它,如果沒有,他們就啟動一個密鑰管理工具,允許註冊散列值或密鑰。
微軟第三方UEFI CA證書的登記需要在固件設置中啟用,以啟動用該證書籤署的EFI二進制文件和OpROM。
PreLoader[編輯 | 編輯原始碼]
運行時,PreLoader嘗試啟動loader.efi
。如果loader.efi
的散列值不在MokList中,PreLoader將會啟動HashTool.efi
。在HashTool中,你必須登記你想要啟動的EFI二進制的散列值,也就是你的引導加載程序(loader.efi
)以及內核的散列值。
設置PreLoader[編輯 | 編輯原始碼]
PreLoader.efi
與HashTool.efi
沒有被簽署,所以它們的作用是有限的。你可以從preloader-signedAUR獲取被簽署的PreLoader.efi
與HashTool.efi
,或者手動下載它們。安裝preloader-signedAUR和HashTool.efi
然後複製PreLoader.efi
和HashTool.efi
到引導加載程序目錄中。對於systemd-boot,使用:
# cp /usr/share/preloader-signed/{PreLoader,HashTool}.efi esp/EFI/systemd
現在複製一份引導加載程序的二進制文件並重命名為loader.efi
。對於systemd-boot,使用:
# cp esp/EFI/systemd/systemd-bootx64.efi esp/EFI/systemd/loader.efi
最終,創建一個新的NVRAM引導項來啟動PreLoader.efi
:
# efibootmgr --unicode --disk /dev/sdX --part Y --create --label "PreLoader" --loader /EFI/systemd/PreLoader.efi
把X
替換為驅動器字符並把Y
替換為EFI 系統分區的分區號。
這個啟動項應當添加進啟動列表中並作為第一項啟動,用efibootmgr
命令檢查,必要時調整啟動順序。
回撤[編輯 | 編輯原始碼]
如果啟動這個自定義NVRAM啟動項時出現問題,複製HashTool.efi
和loader.efi
到UEFI系統自動啟動的默認加載程序位置:
# cp /usr/share/preloader-signed/HashTool.efi esp/EFI/BOOT/ # cp esp/EFI/systemd/systemd-bootx64.efi esp/EFI/BOOT/loader.efi
複製一份PreLoader.efi
並重命名:
# cp /usr/share/preloader-signed/PreLoader.efi esp/EFI/BOOT/BOOTx64.EFI
對於特別頑固的UEFI實現,可以將PreLoader.efi
複製到Windows系統使用的默認加載程序位置:
# mkdir -p esp/EFI/Microsoft/Boot # cp /usr/share/preloader-signed/PreLoader.efi esp/EFI/Microsoft/Boot/bootmgfw.efi
bootmgfw.efi
,因為替換它可能會導致Windows更新的問題。如前所述,將HashTool.efi
和loader.efi
複製到'esp/EFI/Microsoft/Boot/
。
當系統在啟用安全啟動下啟動時,按照上述步驟登記loader.efi
和/vmlinuz-linux
(或任何使用的內核鏡像)。
如何在啟動時使用?[編輯 | 編輯原始碼]
當出現一條消息說:Failed to Start loader... I will now execute HashTool.
。要使用HashTool來登記loader.efi
和vmlinuz.efi
的散列值,請遵循以下步驟。這些步驟假定了一個重製的archiso安裝介質的項目。你得到的確切的項目取決於你的引導加載程序的設置。
- 選擇 OK
- 在HashTool的主菜單,選擇Enroll Hash,選擇
\loader.efi
然後選Yes確認。同樣,再選擇Enroll Hash和archiso
來進入archiso的目錄,然後選擇vmlinuz.efi
,用Yes確認。接下來選擇Exit返回至啟動設備選單。 - 在啟動設備選單中選擇Arch Linux archiso x86_64 UEFI CD
移除PreLoader[編輯 | 編輯原始碼]
卸載preloader-signedAUR然後
並簡單地刪除複製的文件並恢復配置。 對於systemd-boot使用:
# rm esp/EFI/systemd/{PreLoader,HashTool}.efi # rm esp/EFI/systemd/loader.efi # efibootmgr --unicode --bootnum N --delete-bootnum # bootctl update
這裏N
指的是用來啟動PreLoader.efi
所創建的NVRAM啟動項。
如有必要,用efibootmgr命令檢查並調整啟動順序。
刪除已登記的散列值[編輯 | 編輯原始碼]
在MOK數據庫中登記的每一條散列值都會吃掉NVRAM的一小塊空間。你可能想刪除無用的散列值,以釋放空間並防止過時的程序啟動。
# cp /usr/share/efitools/efi/KeyTool.efi esp/EFI/systemd/KeyTool.efi
引導啟動至Preloader,你會看到KeyTool引導項。然後你就可以在MOK數據庫中編輯散列值。
shim[編輯 | 編輯原始碼]
在運行時,shim會嘗試啟動grubx64.efi
。如果MokList沒有包含grubx64.efi
的散列值或它所簽署的密鑰,shim將會啟動MokManager(mmx64.efi
)。你必須在MokManager裡登記你想要啟動的EFI二進制文件(你的引導加載程序grubx64.efi
與內核)的散列值,或者登記你想要簽署的密鑰。
- 如果你使用#shim與散列值,每次你升級任何二進制文件(比如引導加載程序或內核)後都需要重新登記它們的散列值。
- 在版本15.3之後,shim不再啟動沒有合法
.sbat
部分的EFI二進制文件。想要驗證一個EFI二進制文件是否具有這個部分,運行objdump -j .sbat -s /path/to/binary.efi
。詳情看:SBAT文檔。 - 或許值得注意的是,說實話,如果你對安全啟動帶來的安全性並不感興趣,之所以想要開啟它僅僅是為了滿足Windows 11的要求,你或許可以考慮關閉shim的驗證過程:
mokutil --disable-validation
。這種情況下你不必簽署grub(或許sbat仍然需要)以及內核鏡像,且還可以通過chainloader
從grub啟動Windows。
設置shim[編輯 | 編輯原始碼]
安裝shim-signedAUR.
重命名當前的引導加載程序為grubx64.efi
# mv esp/EFI/BOOT/BOOTx64.EFI esp/EFI/BOOT/grubx64.efi
複製shim與MokManager到ESP中的啟動引導文件夾,然後將shimx64.efi
重命名為引導加載程序之前的文件名:
- 確保你不要複製同一目錄下的fbx64.efi,除非你真的有一個有效的bootx64.csv可以使用。否則shim不會執行grubx64.efi,這將無法正常工作,只會重置機器。
# cp /usr/share/shim-signed/shimx64.efi esp/EFI/BOOT/BOOTx64.EFI # cp /usr/share/shim-signed/mmx64.efi esp/EFI/BOOT/
最終,創建一個新的NVRAM引導項來啟動BOOTx64.EFI
:
# efibootmgr --unicode --disk /dev/sdX --part Y --create --label "Shim" --loader /EFI/BOOT/BOOTx64.EFI
shim可以通過機器所有者密鑰或MokList所存儲的散列值驗證二進制文件。
- 機器所有者密鑰 - Machine Owner Key (MOK)
- 一個由用戶生成並被用來簽署EFI二進制文件的密鑰。
- 散列值 - hash
- 一個EFI二進制文件的SHA256散列值。
使用散列值更簡單但是每次升級你的引導加載器或內核後需要添加它們新的散列值進MokManager。而使用MOK你只需要添加密鑰一次,但是每次引導加載器或內核升級後都需要重新簽署。
shim與散列值[編輯 | 編輯原始碼]
如果shim無法找到在MokList中找到grubx64.efi
的SHA256散列值,它將會啟動MokManager(mmx64.efi
)。
在MokManager中選擇Enroll hash from disk,找到grubx64.efi
然後添加它到MokList。重複這一步驟添加你的內核vmlinuz-linux
。完成之後,選擇Continue boot,你的引導加載程序便會啟動並成功加載內核。
shim與密鑰[編輯 | 編輯原始碼]
安裝sbsigntools包。
你需要:
- .key
- PEM格式的私有密鑰,用於簽署EFI二進制文件。
- .crt
- PEM格式的證書,用於sbsign。
- .cer
- DER格式的證書,用於MokManager。
創建一個機器所有者密鑰(MOK):
$ openssl req -newkey rsa:4096 -nodes -keyout MOK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Machine Owner Key/" -out MOK.crt $ openssl x509 -outform DER -in MOK.crt -out MOK.cer
簽署你的引導加載程序(名為grubx64.efi
)以及內核:
# sbsign --key MOK.key --cert MOK.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux # sbsign --key MOK.key --cert MOK.crt --output esp/EFI/BOOT/grubx64.efi esp/EFI/BOOT/grubx64.efi
每次它們更新後你都需要這麼做。你可以使用一個Pacman鈎子自動化簽署過程,比如:
/etc/pacman.d/hooks/999-sign_kernel_for_secureboot.hook
[Trigger] Operation = Install Operation = Upgrade Type = Package Target = linux Target = linux-lts Target = linux-hardened Target = linux-zen [Action] Description = Signing kernel with Machine Owner Key for Secure Boot When = PostTransaction Exec = /usr/bin/find /boot/ -maxdepth 1 -name 'vmlinuz-*' -exec /usr/bin/sh -c 'if ! /usr/bin/sbverify --list {} 2>/dev/null | /usr/bin/grep -q "signature certificates"; then /usr/bin/sbsign --key MOK.key --cert MOK.crt --output {} {}; fi' ; Depends = sbsigntools Depends = findutils Depends = grep
或者使用一個 mkinitcpio鈎子:
/etc/initcpio/post/kernel-sbsign
#!/usr/bin/env bash kernel="$1" [[ -n "$kernel" ]] || exit 0 # use already installed kernel if it exists [[ ! -f "$KERNELDESTINATION" ]] || kernel="$KERNELDESTINATION" keypairs=(/path/to/db.key /path/to/db.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
複製MOK.cer
到一個FAT格式的文件系統(你可以使用EFI 系統分區)。
重新啟動並啟用「安全啟動」。如果shim未能找到在MokList中找到簽署的grubx64.efi
證書,它將啟動MokManager(mmx64.efi
)。
在MokManager中選擇Enroll key from disk,找到MOK.cer
並把它添加到MokList。完成之後,選擇Continue boot,你的引導加載程序便會啟動並成功加載任何由你的機器所有者密鑰簽署的二進制文件。
shim與密鑰和GRUB[編輯 | 編輯原始碼]
只有GRUB的EFI二進制文件包含所有能夠讀取vmlinuz與initramfs鏡像所在文件系統的模塊,才能在安全啟動模式下成功啟動。
從GRUB版本2.06.r261.g2f4430cc0
之後,通過insmod
在安全啟動下讀取模塊不再被允許,因為這將違反不側載任意代碼的構想。如果GRUB模塊沒有被嵌入進EFI二進制文件,GRUB會嘗試通過insmod
側載它們,這會在啟動時報出如下錯誤信息:
error: prohibited by secure boot policy
根據Ubuntu的官方構建腳本,Ubuntu嵌入了下列GRUB模塊在它已簽署的GRUB EFI二進制文件grubx64.efi
中。
- 「base」模塊,為了從CD或者單一分區硬盤中啟動所必需含有:
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
,reboot
,regexp
,search
,search_fs_uuid
,search_fs_file
,search_label
,sleep
,smbios
,squash4
,test
,true
,video
,xfs
,zfs
,zfscrypt
,zfsinfo
- 「platform-specific」模塊,為了x86_64-efi架構所必需含有,例如:
play
:為了在啟動中播放聲音cpuid
:為了啟動中的CPU支持linuxefi
:為了支持UEFI啟動tpm
:為了支持度量啟動(Measured boot)與可信平台模塊
- 「advanced」模塊,包含下列模塊:
cryptodisk
:為了從plain模式加密的硬盤啟動gcry_algorithm
:為了支持特定的散列和加密算法luks
:為了從LUKS加密的硬盤啟動lvm
:為了從LVM邏輯分卷硬盤啟動mdraid09
,mdraid1x
,raid5rec
,raid6rec
:為了從RAID虛擬硬盤中啟動
你必須以一個shell變量的形式構建你的GRUB模塊列表,我們將它表示為GRUB_MODULES
。你可以使用最新的Ubuntu腳本作為一個出發點,並修剪掉你系統中不需要的模塊。修剪模塊將使啟動過程相對更快,並在ESP分區上節省一些空間。
如果GRUB從UEFI shim加載器啟動,為了提高安全性,你還需要在EFI二進制文件中包含一個安全啟動高級目標(SBAT)文件/部分。這個SBAT文件/部分包含了關於GRUB二進制文件的元數據(版本、維護者、開發者、上游URL),並且使shim更容易阻止某些有安全漏洞的GRUB版本被加載[8][9],正如UEFI shim引導加載程序的安全啟動生命流程的提升文檔中所解釋的那樣。
如果grubx64.efi
中的SBAT部分丟失,UEFI shim引導加載程序的第一階段將無法啟動grubx64.efi
。
如果安裝了GRUB,/usr/share/grub/sbat.csv
中提供了一個SBAT的.csv文件樣本。
使用提供的/usr/share/grub/sbat.csv
文件和所有需要的GRUB_MODULES
重新安裝GRUB並簽署:
# grub-install --target=x86_64-efi --efi-directory=esp --modules=${GRUB_MODULES} --sbat /usr/share/grub/sbat.csv # sbsign --key MOK.key --cert MOK.crt --output esp/EFI/GRUB/grubx64.efi esp/EFI/GRUB/grubx64.efi # cp esp/GRUB/grubx64.efi esp/boot/grubx64.efi
重新啟動,在MokManager中選擇密鑰,安全啟動就應該可以工作了。
移除shim[編輯 | 編輯原始碼]
卸載shim-signedAUR,移除被複製的shim與MokManager文件並重命名為你的引導加載程序默認名。
保護安全啟動[編輯 | 編輯原始碼]
防止有物理訪問權限的人禁用安全啟動的唯一方法是用密碼保護固件設置。
大多數UEFI固件提供這樣的功能,通常列在固件設置中的「安全」部分。
提示與技巧[編輯 | 編輯原始碼]
sbctl[編輯 | 編輯原始碼]
sbctl包是一種用戶友好的設置安全啟動和簽署文件方式。
創建並登記密鑰[編輯 | 編輯原始碼]
在開始之前,進入你的固件設置將安全啟動模式設置為設置模式。每個設備都有所不同。
當你重新登錄後,檢查安全啟動的狀態:
$ sbctl status
你應當看到sbctl並未安裝以及安全啟動已關閉。
接下來創建你的自定義安全啟動密鑰:
# sbctl create-keys
將你的密鑰與微軟的密鑰登記入UEFI中:
# sbctl enroll-keys -m
# sbctl enroll-keys
。只有在你清楚了解自己在做什麼的情況下才可以這樣做。再次檢查安全啟動狀態:
$ sbctl status
sbctl現在應該已經安裝好了,但是在用你剛剛創建的密鑰對啟動文件進行簽署之前,安全啟動將無法工作。
簽署[編輯 | 編輯原始碼]
為了安全啟動工作,檢查需要被簽署的文件:
# sbctl verify
現在簽署所有的未被簽署的文件。通常是內核與引導加載程序需要被簽署。比如:
# sbctl sign -s /boot/vmlinuz-linux # sbctl sign -s /boot/EFI/BOOT/BOOTX64.EFI
需要被簽署的文件將取決與你系統的結構,內核以及引導加載程序。
現在萬事大吉!重新啟動你的系統,並在固件設置中重新打開安全啟動。如果引導加載程序與作業系統被加載,安全啟動應當開始工作了。用下列命令確認:
$ sbctl status
使用pacman鈎子自動簽署[編輯 | 編輯原始碼]
sbctl帶有一個Pacman鈎子可以在內核、systemd或者引導加載程序更新後自動地簽署所有的新文件。
systemd-boot-update.service
,那麼引導加載程序只會在重啟後升級,導致sbctl的Pacman鈎子因此而不簽署新的文件。 變通的方法是直接在/usr/lib/
中小心的簽署引導加載程序,這樣bootctl install
與update
將會自動識別並複製.efi.signed(如果有的話)到ESP,而不是普通的.efi文件。詳情見bootctl(1)。
# sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi
用自定義密鑰簽署官方ISO[編輯 | 編輯原始碼]
使用自定義密鑰的安全啟動支持可以添加到官方ISO中,只需提取出引導加載程序(BOOTx64.EFI
與BOOTIA32.EFI
)、內核與UEFI Sheel,並對它們進行簽署,然後用被簽署的文件重新打包ISO。
安裝libisoburn包、mtools包與sbsigntools包。
首先解壓出相關的文件以及El-Torito啟動鏡像:
$ osirrox -indev archlinux-YYYY.MM.DD-x86_64.iso \ -extract_boot_images ./ \ -extract /EFI/BOOT/BOOTx64.EFI BOOTx64.EFI \ -extract /EFI/BOOT/BOOTIA32.EFI BOOTIA32.EFI \ -extract /shellx64.efi shellx64.efi \ -extract /shellia32.efi shellia32.efi \ -extract /arch/boot/x86_64/vmlinuz-linux vmlinuz-linux
mkarchiso所使用的,xorrisofs(1)的-rational-rock
選項,會使ISO 9660中的文件變為只讀,且在解壓後仍然如此。我們需要讓這些文件變為可寫,以便它們可以被修改:
$ chmod +w BOOTx64.EFI BOOTIA32.EFI shellx64.efi shellia32.efi vmlinuz-linux
使用一個已登記的數據庫密鑰和證書籤署這些文件:
$ sbsign --key db.key --cert db.crt --output BOOTx64.EFI BOOTx64.EFI $ sbsign --key db.key --cert db.crt --output BOOTIA32.EFI BOOTIA32.EFI $ sbsign --key db.key --cert db.crt --output shellx64.efi shellx64.efi $ sbsign --key db.key --cert db.crt --output shellia32.efi shellia32.efi $ sbsign --key db.key --cert db.crt --output vmlinuz-linux vmlinuz-linux
將引導加載程序與UEFI Shell複製到eltorito_img2_uefi.img
中。它將被用作為EFI系統分區,並被列為一個El-Torito UEFI啟動鏡像。eltorito_img2_uefi.img
的大小是固定的,但mkarchiso增加了1 MiB的空餘空間,用於對齊以及為後續的扇區保留,所以簽署所帶來的大小增加應該不是問題。
$ mcopy -D oO -i eltorito_img2_uefi.img BOOTx64.EFI BOOTIA32.EFI ::/EFI/BOOT/ $ mcopy -D oO -i eltorito_img2_uefi.img shellx64.efi shellia32.efi ::/
使用修改過的El-Torito UEFI引導映像,重新打包ISO,並在ISO 9660中加入簽署的引導加載程序文件、UEFI Shell和內核。
$ xorriso -indev archlinux-YYYY.MM.DD-x86_64.iso \ -outdev archlinux-YYYY.MM.DD-x86_64-Secure_Boot.iso \ -boot_image any replay \ -append_partition 2 0xef eltorito_img2_uefi.img \ -map BOOTx64.EFI /EFI/BOOT/BOOTx64.EFI \ -map BOOTIA32.EFI /EFI/BOOT/BOOTIA32.EFI \ -map shellx64.efi /shellx64.efi \ -map shellia32.efi /shellia32.efi \ -map vmlinuz-linux /arch/boot/x86_64/vmlinuz-linux
啟動生成的archlinux-YYYY.MM.DD-x86_64-Secure_Boot.iso
文件。
另見[編輯 | 編輯原始碼]
- Understanding the UEFI Secure Boot Chain by tianocore
- Wikipedia:Unified Extensible Firmware Interface#Secure boot
- Dealing with Secure Boot by Rod Smith
- Controlling Secure Boot by Rod Smith
- UEFI secure booting (part 2) by Matthew Garrett
- UEFI Secure Boot by James Bottomley
- efitools README
- Will your computer's "Secure Boot" turn out to be "Restricted Boot"? — Free Software Foundation
- Free Software Foundation recommendations for free operating system distributions considering Secure Boot
- Intel's UEFI Secure Boot Tutorial
- Secure Boot, Signed Modules and Signed ELF Binaries
- National Security Agency docs: UEFI Defensive Practices Guidance and unclassified UEFI Secure Boot customization
- sbkeysync & maintaining uefi key databases by Jeremy Kerr
- Secure your boot process: UEFI + Secureboot + EFISTUB + Luks2 + lvm + Arch Linux (2020-07)
- How is hibernation supported, on machines with UEFI Secure Boot? (Security StackExchange)
- Authenticated Boot and Disk Encryption on Linux by Lennart Poettering (2021-09-23)