UEFI対応BIOSとSecureBootの訓練をしてみる話
はじめに
昨今UEFI対応BIOS+SecureBootネタに執心のネバー・フレンズ・Tです。
Linuxを相手にしたUEFI対応BIOS+SecureBoot仕様の操作の練習(試行錯誤)を安全にやりたい方向けに手元の仮想環境(kvm)で実際にUEFI対応BIOSを動かし、SecureBootを有効にしてDebianを対象にブートの設定だけ手組してみることを語ってみます。
こちらに慣れておけば、以降はお好きにUEFIの実験ができるようになり、理解が進むのでは?と思ってます。
UEFI対応BIOS+SecureBoot(Debian デフォルト起動編)の概略図とEFI Variable
いままでレガシーBIOS Bootばかり触ってきた人にありがちなのですが、巷のUEFIの解説やLinuxのUEFI Bootの解説、断片的なUEFI関係のblogやwikiを見て、あまりに断片的な内容が多いためか理解するのに心折られた方も多いかと思います。かくいう自分もその一人で、わけがわからないよ!と正直思ってました。UEFI対応BOISとSecureBootの起動についてぜんぜんわからない俺はブートを雰囲気でやっている、ということから少しは抜け出してみます。
まずは、Debianの起動からです。shim-unsignedパッケージのソースコード、及び、手元のDebian unstableの設定ファイルから、概略ですが、図に起こしてみました。
図中、EFI VariableがUEFI対応BOISのブートを制御する変数群となります。図に書ききれないため省略しておりますが、これら変数にはもれなくGUIDというIDが付属しており、このGUIDと変数名のペアで参照することになっています。実際にBootOrder
変数を例にLinuxから見てみます。Linux環境はDebian(unstable)となります。
$ sudo apt install efivar
$ efivar -l | fgrep -i bootorder
8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder
$ efivar -L | fgrep 8be4df61
{8be4df61-93ca-11d2-aa0d-00e098032b8c} {global} efi_guid_global EFI Global Variable
8be4df61-93ca-11d2-aa0d-00e098032b8c-
というのがGUIDであり、分類としては、EFI Global Variableに所属していることを示しています。早速中身を確認してみます。
$ efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder
GUID: 8be4df61-93ca-11d2-aa0d-00e098032b8c
Name: "BootOrder"
Attributes:
Non-Volatile
Boot Service Access
Runtime Service Access
Value:
00000000 07 00 02 00 05 00 01 00 06 00 04 00 03 00 |.............. |
こちらには起動候補(ブートエントリ)の起動優先順位が16bit リトルエンディアンで記録されています。16bitづつ値を読み取ると、7番目、2番目、5番目、1番目、6番目、4番目、最も低い優先順位は3番目のブートエントリということを示しています。
次にこのマシンの一番優先順位が高いブートエントリの中身を見てみます。一番優先順位が高いのは7番目ですから、見るべき変数はBoot0007
を見ます。GUIDはBootOrder
変数についているものと同じものを利用します。
$ efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-Boot0007
GUID: 8be4df61-93ca-11d2-aa0d-00e098032b8c
Name: "Boot0007"
Attributes:
Non-Volatile
Boot Service Access
Runtime Service Access
Value:
00000000 01 00 00 00 74 00 57 00 69 00 6e 00 64 00 6f 00 |....t.W.i.n.d.o.|
00000010 77 00 73 00 20 00 42 00 6f 00 6f 00 74 00 20 00 |w.s. .B.o.o.t. .|
00000020 4d 00 61 00 6e 00 61 00 67 00 65 00 72 00 00 00 |M.a.n.a.g.e.r...|
00000030 04 01 2a 00 01 00 00 00 00 08 00 00 00 00 00 00 |..*.............|
00000040 00 20 03 00 00 00 00 00 d9 5f 1f 95 7a 3a 27 4b |. ......._..z:'K|
00000050 a2 24 95 3f d7 56 77 c4 02 02 04 04 46 00 5c 00 |.$.?.Vw.....F.\.|
00000060 45 00 46 00 49 00 5c 00 4d 00 69 00 63 00 72 00 |E.F.I.\.M.i.c.r.|
00000070 6f 00 73 00 6f 00 66 00 74 00 5c 00 42 00 6f 00 |o.s.o.f.t.\.B.o.|
00000080 6f 00 74 00 5c 00 62 00 6f 00 6f 00 74 00 6d 00 |o.t.\.b.o.o.t.m.|
00000090 67 00 66 00 77 00 2e 00 65 00 66 00 69 00 00 00 |g.f.w...e.f.i...|
000000a0 7f ff 04 00 57 49 4e 44 4f 57 53 00 01 00 00 00 |....WINDOWS.....|
...中略...
16進ダンプのASCII表示の部分からなんとなくわかるとおり、Windowsのブートエントリです。エントリにはラベルがついておりWindows Boot Managerという名前のラベルのようです。ブートローダそのものは、\EFI\Microsoft\Boot\bootmgfw.efi
であることがわかります。ここからこのマシンは電源をONにすると、何もしなければ\EFI\Microsoft\Boot\bootmgfw.efi
によりWindowsが起動するマシンであることがわかります。
今回の目的に関係しそうなEFI Variableを表にすると以下のとおりです。EFI Variableはブート時に活用されるBoot Variableと、OSが走り出したあとで活用されるRuntime Variableに別れますので、ここでは両方記載します。
表の出典:Debian unstableに搭載のmokutil/shim-unsignedパッケージのソースコード、及び、efivarの実行結果を用いて解析。
EFI Boot Variable | EFI Runtime Variable | 説明 |
---|---|---|
BootOrder | BootOrder | ブートエントリの起動優先順位(16Bit リトルエンディアン) |
Boot00000 | Boot00000 | ブートレコード0番目。EFIパーティション上のブートローダへのPATH(MS-DOS形式のPATH)とUEFI対応BOISのブートメニューでのブートエントリの見出しが入っている。 |
Boot00001 | Boot00001 | ブートレコード1番目。同上 |
…中略… | …中略… | ブートレコードn番目。同上 |
SecureBoot | SecureBoot | SecureBoot(SB)モードで起動しているか?を示す。=1 SecureBoot状態、=0 SecureBootではない。 |
MokList | MokListRT | shimx64.efi 用。SecureBoot時のDebian用署名検証用の公開鍵が入っている。カスタムでMachine Owner Key(MOK)の追加に成功するとこちらに追記される。mokutil --list-enroll で中身をデコード可能。 |
MokListX | MokListXRT | shimx64.efi 用。公開鍵のhash値が入っており、hashで指定された公開鍵は署名確認に使えないことを示すMOK blacklist (MOKX)の内容が入っている。mokutil --list-enroll -X で中身をデコード可能。 |
db | db | SecureBoot時にバイナリの署名を確認する用途の公開鍵が入っている。mokutil --db で中身をデコード可能。 |
dbx | dbx | キーのhash値が入っており、hashで指定された公開鍵は署名確認に使えないことを示すblacklistの内容が入っている。mokutil --dbx で中身をデコード可能。 |
PK | PK | SecureBoot時にバイナリの署名を確認する用途のPlatform Key(PK)と言われる公開鍵が入っている。mokutil --pk で中身をデコード可能。 |
KEK | KEK | SecureBoot時にバイナリの署名を確認する用途のKey Exchange Key(KEK)と言われる公開鍵が入っている。mokutil --kek で中身をデコード可能。 |
MokSBState | MokSBStateRT | shimx64.efi / grubx86.efi / kernel用。shimx64.efi にてSecureBoot(SB)がキャンセルされているので以降はSecureBootとして扱わないでほしいことを示す。=1 キャンセル済。変数が存在しないとSecureBoot変数の内容に従う。 mokutil --enable-validation 、mokutil --disable-validation でmmx64.efi (Mok Manager)に変更を指示することで変更可能。 |
MokDBState | MokIgnoreDB | kernelが行う署名検証に上述のdbに格納されている公開鍵をあえて利用しないことを指示する。 |
SbatLevel_DEVEL | SbatLevel_DEVEL | shimx64.efi にてUEFI Secure Boot Advanced Targeting (SBAT)という署名検証時のセキュリティ対策用の変数。 |
MokPWStore | mmx86.efi がメニューを出すまえにパスワードを尋ねるようになる。パスワード文字列はこちらの変数に記載されたhashと照合が行われる。変更はmokutils --passwd 、mokutils --clear-passwd で実施。--root-pw 、--hash-file 、--simple-hash を組み合わせると、linuxのrootのパスワードを利用したり、特定のhashファイルを利用したりできる。 |
|
MokSB | MokSB | mmx86.efi 操作リクエスト用。SecureBootをキャンセルする動作に移ることを要求する。次回起動時に促されるパスワードが平文で入っている。 |
MokNew | MokNew | mmx86.efi 操作リクエスト用。mokutil --list-new に対応。 |
MokPw | MokPw | mmx86.efi 操作リクエスト用。mokutil --passwd 、mokutils --clear-passwd に対応。--root-pw 、--hash-file 、--simple-hash を組み合わせると、linuxのrootのパスワードを利用したり、特定のhashファイルを利用したりできる。 |
MokAuth | MokAuth | mmx86.efi 操作リクエスト用。mokutil --import crt の際に設定するパスワードに関する付加情報を記録。mmx86.efi はcrtをimportする際にパスワードの入力を促し、こちらの付加情報とも照合する。 |
MokDel | MokNew | mmx86.efi 操作リクエスト用。mokutil --list-delete に対応。 |
MokDB | MokDB | mmx86.efi 操作リクエスト用。mokutil --use-db 、mokutils --ignore-db に対応。この変数を変更することにより、mmx86.efi によりMokDBState が変更される。 |
MokXNew | MokXNew | mmx86.efi 操作リクエスト用。mokutil --list-new -X に対応。 |
MokXDel | MokXDel | mmx86.efi 操作リクエスト用。mokutil --list-delete -X に対応。 |
MokXAuth | MokXAuth | mmx86.efi 操作リクエスト用。mokutil --import-hash hash -X 、mokutil --delete hash -X にて署名確認時に参照させたくないMOKのhashを設定する際のパスワードの付加情報。 |
また、表中のPK、KEK、MOKという用語については、SUSEのUEFIドキュメントがとてもわかりやすいのでおすすめしておきます。
なお、EFI Runtime Variablesの欄の変数ですが、EFI Boot Variablesをこの変数にコピーしてkernel側に見せるということをしていたりします。
kvmとUEFI対応BIOSとSecureBootでインストール練習をしてみる!
今回本記事は実際の機材を触る前に、なんとかしてUEFI対応BIOSとSecureBootの練習をしておくという記事なので、まずはUEFI対応BOISとSecureBootのモードでDebianを普通にインストールする事から始めてみます。
なお、環境としては、すでに何らかの方法で用意されたDebian(unstable)の上にkvmにてUEFI対応BIOSを載せて練習することにします。なお、今回Debianそのもののインストール途中のメニューの話はさすがにレガシーBOIS等でなれている人向けの話としたいので、割愛します。
$ lsb_release -dirc
Distributor ID: Debian
Description: Debian GNU/Linux bookworm/sid
Release: unstable
Codename: sid
$ sudo apt install qemu-system-x86 qemu-utils ovmf
$ wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-11.3.0-amd64-netinst.iso
$ qemu-img create -f qcow2 debian-sid-secureboot-uefi.qcow2 10G
$ cp /usr/share/OVMF/OVMF_VARS_4M.ms.fd ovmf_vars_4m.ms.debian-sid.fd
$ kvm -cpu host -machine q35,smm=on -global driver=cfi.pflash01,property=secure,value=on \
-rtc base=utc -smp 2 -m 2G -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.ms.fd \
-drive if=pflash,format=raw,file=$(pwd)/ovmf_vars_4m.ms.debian-sid.fd \
-drive file=$(pwd)/debian-sid-secureboot-uefi.qcow2,if=virtio,index=1,media=disk,format=qcow2 \
-device intel-hda -vga virtio -display gtk -k ja -cdrom $(pwd)/debian-11.3.0-amd64-netinst.iso
kvmを利用するにあたり、qemu-system-x86
、qemu-utils
を導入しておきます。UEFI対応BOISのイメージも必要となりますので、ovmfパッケージでBIOSイメージを/usr/share/OVMF/
以下に導入しておきます。
qemu-img
でインストール先の仮想ディスクを作ってます。/usr/share/OVMF/OVMF_VARS_4M.ms.fd
がEFI VariableのNVRAMの雛形となりますので、ローカルにコピーして保存しておきます。あとでBOIS側から設定をいじったり、ブートエントリ(EFI Boot Variable等)を、自由に書き込んで保存しておく必要があるため、ここでは1つの仮想ディスクに対して1個コピーを取っておきます。
最後のkvm -cpu ...
という長いコマンドラインが直接kvmによる仮想環境の起動となります。今回はネットワークはkvm側がデフォルトで用意するネットワークを利用する前提で立ち上げています。また、コマンドラインにある-vga virtio
はUEFI対応BIOSであるOVMFを使うときは必須となります。これがないと正常な描画ができません。また仮想環境のディスプレイはgtkで直接デスクトップ環境へ描画します。なお、OpenGLのアクセラレーションを期待して-display gtk
にgl=on
を付与すると、インストール中マウスカーソルについて正常なジオメトリ計算ができないようなので今回はOFFしています。
最後のkvmのコマンドが実行されると、Titan Coreのロゴが一瞬見えて、そのあとすぐに見慣れない画面になります。
kvmで起動するとqemuのウィンドウが出てきます。qemuのウィンドウに表示されているコンソール画面が小さいと思った人は、qemuのウィンドウで、CTRL+ALT+ +
(CTRLとALTと+キーを押す)を何回か押すか、メニューからView→Zoom In
をマウスで選択下さい。qemuのウィンドウでマウスカーソルが消えてメニューが選択できない場合は、ALT+v
(ALTを押しながらvキー)を押すとメニューが開きます。そのまま、カーソルキーで選択してReturnキーを押して下さい。また、画面をクリックすると、Allow Inhibit Shortcut
というポップアップが出ますが、許可
を押していただいて問題ありません。マウスが消えてウィンドウフォーカスがqemuのウィンドウから外れないかもしれませんが、タイトルバーにPress Ctrl+Alt+G
と出ているとおり、CTRLとALTとGキーを同時押しするとマウスが出てきてウィンドウフォーカスが外れるようになります。
この画面ですが、EFI Shellというプログラムの画面となります。こちらのコマンドを覚えると、EFI VariableのBoot0000
などのブートエントリをEFI Shellから直接操作できます。ただ、あとで立ち上がるメニュー形式の方が断然使いやすいので、ここではEFI Shellをスキップすることにします。Shell>
と出ているところでEXIT
と打ち込んで下さい。
すると、UEFI対応BIOSのメニュー画面になります。qemuのウィンドウのサイズが戻ってしまった人は、先程の画面サイズ調整(CTRL+ALT+ +
、ALT+v
でメニュー開いてカーソルキーでZoom In
を選択等)を実施ください。
カーソルキーで文字列のハイライトが動きますので、まずはDevice Manager
を選択します。ハイライトを合わせてRetrunキーを押して下さい。
するとDevice Manager
メニューが現れ、Secure Boot Configuration
というのが現れますので、こちらにハイライトを合わせてReturnキーを押下下さい。すると今度はSecure Boot Configuration
メニューになります。
画像の値の通りになっていることを確認下さい。異なっていたら、ハイライトを合わせてRetrunキーで選択することで変更できます。戻りはEscキーを押すと戻れます。変更した場合はF10キーを押してからESCキーを押下下さい。Device Manager
メニューに戻ってきますので、更にESCキーを押して最初のメニュー画面に戻ります。
最初のメニューに戻ってきたら、Boot Manager
を選択してReturnキーを押下します。
ブート可能なブートエントリ一覧が出てきましたので、UEFI QEMU DVD-ROM QM00005
にハイライトを合わせてReturnキーを押すと、やっとisoイメージが実行され、いつものDebianインストーラーのメニューが出てきます。
今回UEFI対応BIOSからのブートですので、画面の上あたりにUEFI Installer menu
と出ています。Graphical install
等選んでインストールをしていきます。
国として日本を選んで、ホスト名、ドメイン、rootユーザ情報、一般ユーザ情報を入力する以外は、ソフトウェアの選択メニューまでひたすら続ける
を押していくだけでインストールが完了します。このあたりは、通常のDebianインストール作業になりますので、ここでは割愛します。
ソフトウェアの選択
メニューになったら、一番基本の
- SSHサーバと、
- 標準システムユーティリティ
のみ選択下さい。
以降手放しで最後までインストールが完了します。あとは、リブート画面になり、Debianのログイン画面が出てきます。
早速インストールで入力した一般ユーザのアカウントでログインして、無事SecureBootの状況かを確認します。
Debian GNU/Linux 11 debian-uefi tty1
debian-uefi login: yours
Passwrod: <一般ユーザのパスワード>
Linux debian-uefi 5.10.0-13-amd64 #1 SMP Debian 5.10.106-1 (2022-03-17) x86_64
...中略...
$ mokutil --sb-state
SecureBoot enabled
無事SecureBootモードで起動しました。
今度は手組でEFI BootセットアップしてSecureBootしてみる!
さて本ブログを読んでいるような方々は、先ほどのぬるいゲームのようなインストールでは物足りないと思います。UEFIとSecureBootの経験値を溜めるため早速レベル上げしていきましょう!
今度は手組したディスクを使ってUEFIとSecureBootを手で操作してブートするまでを練習してみます。もちろん、ご存知の方はUEFI BootでもDebianインストールディスクのレスキューモードで立ち上げてgrub2の再インストールメニューを選べば、すぐにブートするようになることをご存知だと思います。しかしながら、人生では予想もしないタイミングで、もっと変わった構成のディスクを相手にOS復活させてからフォルダにある黒歴史の始末をすぐにしなければならないときが来ます!きっとこれで皆様も動機は十分だと思いますので、早速練習してみましょう。
本章のお題としては、空のディスクを用意して、先程インストールしたDebianの/以下のみ全部コピーし、手組でUEFIブートの設定を行って起動させるということをしてみます。
まずは、空のディスクを用意して、準備を進めます。EFI領域以外は全部適当なサイズで用意してみます。
なお、(Host)
と書いてある部分はqemuを動かすHost OS、(Guest)
とあるのはqemuのウィンドウで操作していることを意味します。
(Host) $ qemu-img create -f qcow2 debian-sid-secureboot-uefi-cpied.qcow2 10G
(Host) $ sudo modprobe nbd
(Host) $ sudo qemu-nbd --connect=/dev/nbd0 (pwd)/debian-efi-copied.qcow2
(Host) $ sudo apt install gdisk
(Host) $ sudo gdisk /dev/nbd0
GPT fdisk (gdisk) version 1.0.8
Partition table scan:
MBR: not present
BSD: not present
APM: not present
GPT: not present
Creating new GPT entries in memory.
Command (? for help): n
Partition number (1-128, default 1):
First sector (34-20971486, default = 2048) or {+-}size{KMGTP}:
Last sector (2048-20971486, default = 20971486) or {+-}size{KMGTP}: +512M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): EF00
Changed type of partition to 'EFI system partition'
Command (? for help): n
Partition number (2-128, default 2):
First sector (34-20971486, default = 1050624) or {+-}size{KMGTP}:
Last sector (1050624-20971486, default = 20971486) or {+-}size{KMGTP}: +8G
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'
Command (? for help): n
Partition number (3-128, default 3):
First sector (34-20971486, default = 17827840) or {+-}size{KMGTP}:
Last sector (17827840-20971486, default = 20971486) or {+-}size{KMGTP}:
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8200
Changed type of partition to 'Linux swap'
Command (? for help): p
Disk /dev/nbd0: 20971520 sectors, 10.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 72270459-AC12-4194-90CB-E0DCED3C8180
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 20971486
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)
Number Start (sector) End (sector) Size Code Name
1 2048 1050623 512.0 MiB EF00 EFI system partition
2 1050624 17827839 8.0 GiB 8300 Linux filesystem
3 17827840 20971486 1.5 GiB 8200 Linux swap
Command (? for help): w
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!
Do you want to proceed? (Y/N): Y
OK; writing new GUID partition table (GPT) to /dev/nbd0.
The operation has completed successfully.
qemu-img
で空のディスクを10GB作り、qemu-nbd
でqcow2形式のディスクイメージをkernelの/dev/nbd0
として認識させたあと、gdisk
でGPTパーティションを作成します。EFI Partitionはディスク先頭からの512MBを割当てるのが流儀となりますのでそうします。さらにEFI領域(0xEF00
)用のパーティションであることを指定します。次に8GBをLinuxの/領域として確保し、Linux領域(0x8300
)とし、残り全部をswap領域(0x8200
)として指定しています。
各パーティションを早速フォーマットします。EFI領域はvfat形式で、/領域はext4形式、swapはswap形式のフォーマットを行います。
(Host) $ sudo mkfs.vfat /dev/nbd0p1
(Host) $ sudo mkfs.ext4 /dev/nbd0p2
(Host) $ sudo mkswap /dev/nbd0p3
さて、先の章で作ったブート可能なdebian-sid-secureboot-uefi.qcow2
をもとに/領域を今回のディスクにコピーします。EFI領域は設定練習ということであとで手組みするのでわざと何もしません。/領域のコピーについてはcp -aT
コマンドが便利で、数々のアトリビュートもコピーしてくれるのでこういう時に便利ですね!
(Host) $ mkdir src dest
(Host) $ sudo qemu-nbd --connect=/dev/nbd1 $(pwd)/debian-sid-secureboot-uefi.qcow2
(Host) $ sudo mount -t ext4 /dev/nbd1p2 src/
(Host) $ sudo mount -t ext4 /dev/nbd0p2 dest/
(Host) $ sudo cp -aT src dest
EFI Variableの値の設定も手組にしますので、先程の章の通り、新規のNVRAMイメージを手元にコピーしておきます。終わりましたら最後に仮想ディスクを外し、Debianインストーラーのisoイメージを起動し、後でレスキューモードを利用します。
(Host) $ cp /usr/share/OVMF/OVMF_VARS_4M.ms.fd ovmf_vars_4m.ms.debian-copied.fd
(Host) $ sudo umount src/
(Host) $ sudo umount dest/
(Host) $ sudo qemu-nbd --disconnect /dev/nbd0
(Host) $ sudo qemu-nbd --disconnect /dev/nbd1
(Host) $ kvm -cpu host -machine q35,smm=on -global driver=cfi.pflash01,property=secure,value=on \
-rtc base=utc -smp 2 -m 2G -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.ms.fd \
-drive if=pflash,format=raw,file=$(pwd)/ovmf_vars_4m.ms.debian-copied.fd \
-drive file=$(pwd)/debian-sid-secureboot-uefi-cpied.qcow2,if=virtio,index=1,media=disk,format=qcow2 \
-device intel-hda -vga virtio -display gtk -k ja -cdrom $(pwd)/debian-11.3.0-amd64-netinst.iso
最初にEFI Shellが立ち上がってきて〜Debianインストーラーメニュー表示までは先の章のとおりの操作となります。Debianインストーラーメニューが出てきたら、今度はAdvanced Options
を選択してReturnキーを押下します。
次に... Rescue mode
を選択してReturnキーを押下します。
すると、Debianインストール時の言語選択等のメニューがTUIで出てきますので、いつもどおりに選択(日本を選択)し続けます。今回ホスト名とかドメインなども作業には影響しないので適当に設定します。しばらく自動でメニューが進み、レスキューモードに移行
の画面になります。
ここで、先程cp -aT
した/領域はkvmからは/dev/vda
上のディスクのパーティション2になるので、/dev/vda2
を選択してReturnキーを押下します。すると/boot/efi
パーティションをマウントしますか?と来ますので、<はい>
を答えます。
するとレスキュー操作メニューになりますので、インストーラー環境内でシェルを実行
を選びます。GRUB ブートローダの再インストール
という、今回のお題をまるごとチートできそうなメニューが出てきますが、今回は趣旨と異なるので無視します。
その後に親切にも/target
に/dev/vda2
をマウントしましたという画面が出てきますが、続ける
を選択すると、レスキューモードでshellが立ち上がります。
早速手組していきましょう!まずは、EFI領域をセットアップします。どういうわけか、先ほどのメニューで/boot/efi
パーティションをマウントしますか?のメニューで<はい>
を答えたにもかかわらずEFI領域がマウントされていないので、EFI領域(/dev/vda1
)をマウントします。
さらに、UEFI対応BOISが起動できるようにするためには、/EFI
というディレクトリから始めるのが習わしのようなので、/EFI
も含め必要なディレクトリを作っていきます。また、必要なefi用イメージ(shimx64.efi
、mmx64.efi
、grubx64.efi
)をコピーしていきます。なお、今回SecureBoot有効に対応しなければならないので、コピーするefi用イメージは全部Debian側で署名されているものが必要です。そのため、末尾に.signed
とついているファイルをコピー下さい。
(Guest)# mount /dev/vda1 /target/boot/efi
(Guest)# cd /target/boot/efi
(Guest)# mkdir EFI
(Guest)# cd EFI
(Guest)# mkdir debian
(Guest)# cd debian
(Guest)# cp /target/usr/lib/shim/shimx64.efi.signed shimx64.efi
(Guest)# cp /target/usr/lib/shim/mmx64.efi.signed mmx64.efi
(Guest)# cp /target/usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed grubx64.efi
では、EFI Variableにレスキューモードのkernelから読み書きできるようにefivarfs
をロードしておきます。
その後、chroot /target /bin/bash
して、/領域にある各種バイナリ(/bin
、/usr/bin
、/sbin
、/usr/sbin
等)を利用できるようにしてしまいます。
(Guest)# cd /
(Guest)# modprobe efivarfs
(Guest)# chroot /target /bin/bash
(Guest) root@debian:/# <-プロンプトが変わる。
早速ブートに必要なファイルを作成、修正していきます。設定ファイルをわかりやすいようにcat
で書いてますが、今ならvi
も普通に動きますので、いつもどおりファイルを修正、新規作成いただければ問題ありません。
(Guest) root@debian:/# cd /boot/efi/EFI/debian
(Guest) root@debian:/boot/efi/EFI/debian# blkid /dev/vda2
/dev/vda2: UUID="2e12dc00-e59c-4d17-95ca-cf5336a4fee8" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="Liux filesystem" PARTUUID="85f775b4-4761-4b48-af6d-443c3b5a2eb7"
(Guest) root@debian:/boot/efi/EFI/debian# cat > grub.cfg
search.fs_uuid 2e12dc00-e59c-4d17-95ca-cf5336a4fee8 root hd0,gpt2
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg
<CTRL+Dを押す>
/boot/efi/EFI/debian/grub.cfg
にて、grub2に、/領域(/dev/vda2
)のUUIDでディスクをブート時に見つけさせ、そこをdisk 0(hd0
)として、gptパーティションの2番目に/領域があることを教えています。こちらの情報をもとに/領域の/boot/grub
ディレクトリ以下のgrub.cfg
を正式なlinuxブート用の設定としてほしいという内容となります。
ちなみにですが、grub2はこれ単体でHostが物理サーバなら、HostのUSBに接続したDISKも扱う能力があります。つまり、grub.cfg
に指定するUUIDは、例えば、USB-Cにつないだ外部ディスクにある/領域を起動したい場合は、同じくblkid
で外部ディスク上の/領域のUUIDを調べて(例: blkid /dev/sda2
等)、grub.cfg
に調べたUUIDを指定すると、grub2がUSBの先につながっている外付けディスクも自動で調べて起動させようとします。grub2の高機能っぷりには毎度助けられますね!
次にkernel側のディスク定義にコピー元のUUID情報が入っているので、修正します。blkid
コマンドで各パーティションのUUIDを求めておき、必要なファイルのUUID情報を変更します。
(Guest) root@debian:/boot/efi/EFI/debian# cd /etc
(Guest) root@debian:/boot/efi/EFI/debian# blkid /dev/vda1
/dev/vda1: UUID="F746-FFE0" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI system partition" PARTUUID="ea4e2394-eae2-4673-8028-c022840d0b6f"
(Guest) root@debian:/boot/efi/EFI/debian# blkid /dev/vda3
/dev/vda3: UUID="13475304-b7df-46d9-bd99-86b13eb5157b" TYPE="swap" PARTLABEL="Linux swap" PARTUUID="b8398490-1291-47d6-83ed-e98f3da0495e"
(Guest) root@debian:/etc# vi fstab #↓ UUID=の部分を上述のblkidで確認したものに各々修正。以下は修正済のUUIDで表示しています。
..中略..
# / was on /dev/vda2 during installation
UUID=2e12dc00-e59c-4d17-95ca-cf5336a4fee8 / ext4 errors=remount-ro 0 1
# /boot/efi was on /dev/vda1 during installation
UUID=F746-FFE0 /boot/efi vfat umask=0077 0 1
# swap was on /dev/vda3 during installation
UUID=13475304-b7df-46d9-bd99-86b13eb5157b none swap sw 0 0
/dev/sr0 /media/cdrom0 udf,iso9660 user,noauto 0 0
(Guest) root@debian:/etc# vi initramfs-tools/conf.d/resume #↓UUID=をswap(/dev/vda3)のUUIDに変更する。以下は修正済のUUIDで表示しています。
RESUME=UUID=13475304-b7df-46d9-bd99-86b13eb5157b
次にkernelからEFI Variableを見えるようにして、ツールを起動します。
(Guest) root@debian:/etc# mount -t efivarfs efivarfs /sys/kernel/firmware/efivars
(Guest) root@debian:/etc# ls /boot/initrd*
/boot/initrd.img-5.10.0-10-amd64 /boot/initrd.img-5.10.0-13-amd64
(Guest) root@debian:/etc# update-initramfs -k 5.10.0-10-amd64 -u
(Guest) root@debian:/etc# update-initramfs -k 5.10.0-13-amd64 -u
(Guest) root@debian:/etc# update-grub2
/boot/initrd
イメージにswapのUUIDが埋め込まれるため、initrd
をアップデートしています。
さらに、/boot/grub/grub.cfg
にも、/領域のUUIDが多数打ち込まれているので、update-grub2
でまとめて更新しています。
最後にブートエントリと、ブート順番を、EFI Variableにefibootmgr
コマンドで書き込みます。
(Guest) root@debian:/etc# efibootmgr -c -d /dev/vda -p 1 -L "debian" -l '\EFI\debian\shimx64.efi'
efibootmgr
はboot loader(今回はshimx64.efi
)が存在するディスクと、パーティション番号を指定する必要があります。今回の場合/dev/vda
のパーティション番号1となります。efibootmgr -c
はデフォルトでEFI VariableのBoot番号の空きを調べてBoot変数を作り、それを起動順番の1番目にします。何らかの理由で1番目にしたくない場合は、efibootmgr
だけ実行すると、現状のBoot変数の中身と、BootOrder変数の中身を表示してくれますので、efibootmgr -o 2,1,3,4,5,7,7,8,9,0
のように起動したい順番に、BootN(Nは数字)のNをカンマで区切って指定下さい。
例:QEMU DVD(Boot0001)を最初に起動するようにしたい。
(Guest) root@debian:/etc# efibootmgr
BootCurrent: 0001
Timeout: 0 seconds
BootOrder: 0009,0000,0002,0001,0003,0004,0005,0006,0007,0008
Boot0000* UiApp
Boot0001* UEFI QEMU DVD-ROM QM00005
Boot0002* EFI Internal Shell
Boot0003* UEFI Misc Device
Boot0004* UEFI PXEv4 (MAC:525400123456)
Boot0005* UEFI PXEv4 (MAC:525400123456) 2
Boot0006* UEFI PXEv6 (MAC:525400123456)
Boot0007* UEFI HTTPv4 (MAC:525400123456)
Boot0008* UEFI HTTPv6 (MAC:525400123456)
Boot0009* debian
(Guest) root@debian:/etc# efibootmgr -o 1,9,0,2,3,4,5,6,7,8
EFI Variableも設定できたので、いよいよリブートします。
(Guest) root@debian:/etc# exit
(Guest)# exit
レスキューモードのメニューが現れますので、システムの再起動
を選択してReturnキーを押下します。すると、無事grub2のメニューが現れ、しばらくするとLinuxがブートし、無事ログインプロンプトが現れます。
Debian GNU/Linux 11 debian-uefi tty1
debian-uefi login: yours
Password: <ログインパスワード>
$ mokutil --sb-state
SecureBoot enabled
無事SecureBootモードで手組したEFI設定で起動できたことがわかります。意外と簡単だったかと思います。
おわりに
EFIブートを手組してSecureBootが簡単にセットアップできることを体験できたと思います。kvmがあればUEFI対応BIOSに関していろいろなことを簡単に試せます。これで修練を積めば、経験値も溜まり、故障などでブートしないディスクをブートさせたりができるようになることでしょう!