🔐

Setup Modeを使わずにセキュアブートでArch Linuxを起動する (shim-signed)

に公開

Microsoft社によって署名されているshim(shim-signed)を用いて、セキュアブート下でArch Linuxを起動する流れを記します。
この方法は、自分の鍵をファームウェアに登録する必要がない(Setup Modeにする必要がない)点でメリットがあります。

ArchWikiを基に、既存には影響を与えない方針で一連の流れを整理した記事です。

前提

grub、efibootmgr、sbsigntools、shim-signedなどはインストールされている前提とします。また、grub-mkconfigでgrubの設定ファイルも生成されているものとします。

EFI システムパーティション(ESP)

参考: EFI システムパーティション - ArchWiki

EFI システムパーティション(以下ESPと称する)は、fdisk -lでタイプが「EFI システム」であるパーティションです。下の場合は、/dev/nvme0n1p1です。

# fdisk -l 
...
/dev/nvme0n1p1       2048     411647     409600   200M EFI システム
...

元々OSがインストールされているUEFI搭載PCであれば、おそらくESPは既に存在します。ESPにはWindowsやリカバリのEFIファイルがあるため、そこにshimとGRUBのEFIファイルを追加します。

追加により増加する容量は、マウントポイントが/boot以外であれば10MiB程度です。したがって、ESPの空き容量が50MiB〜100MiBあれば既存の流用で良いと思います。

ESPのマウントポイント

/boot、/efi、非推奨とされている/boot/efiなどがありますが、私はArchWikiやarchinstallの実装、Gentoo Wiki等からマウントポイントを/efiにしています。

/bootは、必ずしもESP上に必要としないファイル(マイクロコード、mkinitcpioによるvmlinuzやinitramfsなど)が配置されるため、ESPの容量が不足する場合があります。
ESPの容量変更は次の点で面倒なため、必要なファイル以外はESPに配置しないことで容量を節約する方針にします。

  • ESPが最初のセクタにあることが多く、パディングが無い場合は次のパーティションのセクタ移動が必要になる
  • ESPはLVMが適用できない
mount --mkdir /dev/nvme0n1p1 /efi

署名鍵の作成

参考: Unified Extensible Firmware Interface/セキュアブート - ArchWiki

openssl req -newkey rsa:2048 -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

3つのファイルが生成されます。MOK.keyが秘密鍵、MOK.crtとMOK.cerが証明書です。MOK.cerはUEFIから認識できるようにする必要があります。ここでは、ESP上にMOK.cerを配置します。

cp MOK.cer /efi/MOK.cer

GRUBのインストール

参考: GRUB#UEFI システム - ArchWiki

セキュアブートにおけるGRUBは、insmodによる動的なモジュールロードができません。grub-install時にモジュール指定が必要です。

注意点は次のとおりです。

  • モジュールはスペースで複数指定するため、${GRUB_MODULES}はダブルクォーテーションで囲むこと
    • 囲まない場合、モジュール名がインストールデバイスと認識されるため、grub-install: error: More than one install device?.のエラーが出力されます。
  • --efi-directory=にESPのパスを指定すること
GRUB_MODULES="all_video bli ext2 gfxterm gzio part_gpt part_msdos tpm"
grub-install --bootloader-id=grub --target=x86_64-efi --efi-directory=/efi --modules="${GRUB_MODULES}" --sbat /usr/share/grub/sbat.csv

bootloader-idにgrubを指定したため、/efi/EFI/grubにGRUBのEFIファイルが配置されます。

GRUB#デフォルト/フォールバックのブートパス - ArchWikiによれば、一部のファームウェアは/efi/EFI/BOOT/bootx64.efiのみロードするそうです。ただし、Windowsとのデュアルブート環境では、おそらくWindowsのEFIファイルが既に配置されています。今回は、既存の環境に影響を与えない方針で進めるため、/efi/EFI/grub/で進めます[1]。

署名

参考: セキュアブート#shim で鍵を使う - ArchWiki

これを前提として、配置されたGRUBのEFIファイルを署名します。カレントディレクトリにMOK.keyとMOK.crtがあるものとします。

sbsign --key MOK.key --cert MOK.crt --output /efi/EFI/grub/grubx64.efi /efi/EFI/grub/grubx64.efi

次に、vmlinuzにも署名をします。

sbsign --key MOK.key --cert MOK.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux

カーネルをアップデートする度に上記のコマンドが必要なため、mkinitcpioにフックすることで自動化します[2]。
/etc/initcpio/post/kernel-sbsignの中身は、セキュアブート#shim で鍵を使う - ArchWikiを参照してください。コード中の/path/to/は、MOK.keyとMOK.crtがある絶対パスに書き換えます。

# nvim /etc/initcpio/post/kernel-sbsign
...
# chmod +x /etc/initcpio/post/kernel-sbsign

shim-signedのインストール

参考: セキュアブート#shim をセットアップする - ArchWiki

grub-installにより配置されたgrubx64.efiと同じディレクトリにshimx64.efiとmmx64.efiをコピーします。今回の場合、/efi/EFI/grub/です。

cp /usr/share/shim-signed/shimx64.efi /efi/EFI/grub/
cp /usr/share/shim-signed/mmx64.efi /efi/EFI/grub/

ブートエントリの作成

ESPが含まれるディスク名とパーティション番号を指定します。

注意点は、EFIがマッピングしたパーティション番号を指定することです。
例えば、ESPがLinux上で/dev/nvme0n1p1の場合、ディスク名は/dev/nvme0n1ですが、パーティション番号は1とは限りません。/dev/nvme0n1p1は、Linuxがマッピングした番号だからです。したがって、UEFIシェルからmapコマンドで確認する方が無難ですが、多くの場合は一致しています。

既にブートエントリが設定されている場合は、efibootmgrコマンドでパーティション番号を確認できます。各ブートエントリに、HD(1,などが記されているはずです。このエントリがブート可能であれば、おそらく括弧の最初にある番号がパーティション番号でしょう。下の場合は1となります。

# efibootmgr
...
Boot0001* GRUB  HD(1,GPT,
...

ラベル名はUEFIでブート順番を明示的に指定したい場合などに表示される名前です。今回はShimにします。--loaderには、ESPから見たshimx64.efiのパスを指定します。
上記のsbsignコマンドは、(ESPを/efiにマウントした)Linux上から見たパスなので、/efi/EFI/grubでした。ここではESPを読み込んだ後のパスなので、/efiは不要で/EFI/grubです。

なお、ローダのパスはバックスラッシュで指定する必要があるのですが、スラッシュで入力しても内部でバックスラッシュに変換されるようです。

efibootmgr --unicode --disk /dev/nvme0n1 --part 1 --create --label "Shim" --loader /EFI/grub/shimx64.efi

これでArch Linux上からの設定は完了です。再起動してUEFIのブートメニューを表示しましょう。

shim-signedの設定

UEFIのブートメニューにShimが現れます。これでブートすると次のメッセージが表示されます。

Verification failed: (0x1A) Security Violation

OKを押すと、次のメッセージが表示されます。

Press any key to perform MOK management

適当なキーを押すとMokManagerが開きます。Enroll key from diskを選択してEnterを押すと、フォルダビューが表示されます。

ESP上にMOK.cerを配置していれば、SYSTEMフォルダ配下にあるはずです。それを選択し、確認画面でYes、Rebootを押します。

これにより、セキュアブートでもGRUBが起動するようになります。

カーネルをアップデートすると自動的に署名するかどうかの確認もこの時点でした方が良いと思います。もし動作していない場合、カーネルをアップデートすると起動しなくなるためです。
linux-ltsあたりをインストールして、grub-mkconfigした上でGRUBから該当カーネルが動作するかどうか確認すると良いと思います。動作しない場合は、kernel-sbsignフック周辺を確認します[3]。

脚注
  1. セキュアブート#shim をセットアップする - ArchWikiなどのように、ArchWikiは/efi/EFI/BOOT/の方針で書かれています。 ↩︎

  2. 2023年11月まではpacmanのフック(999-sign_kernel_for_secureboot.hook)を用いる方法がArchWikiに記されていました。私の環境ではこの方法でも動作していました。 ↩︎

  3. pacmanからカーネルインストール時にkernel-sbsignログが出るかどうかでも確認できます。 ↩︎

Discussion