ArchLinux でセキュアブートする

ThinkCentre M715q でセキュアブートしつつ Arch Linux を動かす。

動機

会社で使ってた PC は、ほぼ 10 年ものだったこともあって新しい PC を購入してもらいました。
しかし、Linux をセットアップして起動すると妙に通信速度が遅く 15KB/s 程度しか出ない……。

これでは仕事にならないから旧 PC を使い続けようかとも思ったのですが、
せっかく買ってもらったのに使わないと申し訳ねぇなぁと思いながら色々調べたところ以下が原因であることがわかりました。

  • M715q に搭載されている NIC(Realtek 8111/8168/8411)は IOMMU が有効でないと動作が不安定になる

  • この PC で IOMMU を有効化するには Device Guard を有効化する必要がある

  • Device Guard を有効にするとセキュアブートが強制的に有効になる

  • 基本 Linux の bootloader は Microsoft の秘密鍵で署名されてないのでセキュアブートが有効になると起動できない

「せっかく購入してもらったのにこれは不味い」と思っていたのですが、
自分で作った秘密鍵で署名して、その鍵を信用させることでクリアできそうなのでやってみました。

なお、有効な鍵で署名されたブートローダーに PreLoader と shim があり、
これらのブートローダーを起動してから GRUB を起動することでセキュアブート下でも起動できるのですが、
何故かどちらも上手いこと起動できなかったので今回は自分で用意した鍵で署名した GRUB で起動します。

また、GRUB が Linux を起動するために Linux カーネルにも署名が必要になります。

セキュアブートに必要なもの

セキュアブートする際、最低限以下を用意する必要があります。

  • PK (Platform Key)

  • KEK (Key Exchange Key)

  • db (Signature Database)

PK はトップレベルの鍵で、所謂ルート認証局の鍵です。
KEK は db 署名用の鍵です。PK で署名されている必要があります。
db は GRUB のような EFI バイナリを署名するための鍵です。KEK で署名されている必要があります。

つまり、 PK -> KEK -> db -> GRUB & Linux のような流れで署名します。

1. 前準備

※以降は、セキュアブートを無効化して起動した Linux 上で作業しています。 そのため、Linux のセットアップなどは省きます。

鍵の所有者を識別するのに GUID が使われるため、生成してファイルに保存しておきます。

sudo su -
sudo mkdir /etc/secureboot && cd $_
sudo chmod 700 .
uuidgen --random | sudo tee GUID.txt

2. PK 作成

openssl req -newkey rsa:2048 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN=Platform Key/" -out PK.crt
openssl x509 -outform DER -in PK.crt -out PK.cer
cert-to-efi-sig-list -g "$(cat GUID.txt)" PK.crt PK.esl
sign-efi-sig-list -g "$(cat GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth

3. KEK 作成

sudo openssl req -newkey rsa:2048 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=Key Exchange Key/" -out KEK.crt
sudo openssl x509 -outform DER -in KEK.crt -out KEK.cer
sudo cert-to-efi-sig-list -g "$(cat GUID.txt)" KEK.crt KEK.esl
sudo sign-efi-sig-list -g "$(cat GUID.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth

4. db 作成

sudo openssl req -newkey rsa:2048 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=Signature Database key/" -out db.crt
sudo openssl x509 -outform DER -in db.crt -out db.cer
sudo cert-to-efi-sig-list -g "$(cat GUID.txt)" db.crt db.esl
sudo sign-efi-sig-list -g "$(cat GUID.txt)" -k KEK.key -c KEK.crt db db.esl db.auth

5. GRUB と Linux カーネルを署名

GRUB はインストール先が --bootloader-id オプションなどで微妙に変わりますが、
初期値でインストールされているものとして記述します。

sudo sbsign --key db.key --cert db.crt --output /boot/efi/EFI/grub/grubx64.efi /boot/efi/EFI/grub/grubx64.efi
sudo sbsign --key db.key --cert db.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux

ついでに、ArchLinux のパッケージマネージャである pacman にフックスクリプトを追加して、 カーネルがアップデートされる度に自動で署名するようにします。

sudo mkdir -p /etc/pacman.d/hooks/
cat <<'EOF' | sudo tee /etc/pacman.d/hooks/99-secureboot.hook
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = linux

[Action]
Description = Signing Kernel for SecureBoot
When = PostTransaction
Exec = /usr/bin/sbsign --key /etc/secureboot/db.key --cert /etc/secureboot/db.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux
Depends = sbsigntools
EOF

6. EFI パーティションに配置

鍵を登録するためには、FAT32 でフォーマットされたパーティションに鍵を置く必要があります。 UEFI 環境で Linux をセットアップする場合、FAT32 フォーマットされた EFI パーティションが必須なためここに設置します。

# EFI パーティションが /boot/efi にマウントされているものとして
sudo mkdir /boot/efi/certs
sudo cp *.auth /boot/efi/certs

7. KeyTool.efi に署名して配置

鍵を登録するために KeyTool.efi を使用します。
セキュアブートを有効にしたあとも起動できるように KeyTool.efi を作った鍵で署名したあと、
efibootmgr を使って起動できるように設定します。

# ArchLinux では efitools パッケージに含まれていて、/usr/share/efitools/efi にあります
sudo sbsign --key db.key --cert db.crt --output /boot/efi/EFI/KeyTool-Signed.efi /usr/share/efitools/efi/KeyTool.efi
# /dev/sda1 が EFI パーティションの場合
sudo efibootmgr -c -d /dev/sda -p 1 -l 'KeyTool' -L '\EFI\KEYTOOL-SIGNED.EFI'

8. 現在の鍵をバックアップ

鍵を登録すると既存の鍵で署名されている Windows などは起動できなくなるので、
工場出荷時の鍵を復元できる機能もついているはずですが、念の為にバックアップを保存しておきます。

Windows Bootloader に署名してしまえば起動できるかもしれないけれど、Windows を起動するつもりがないので試していない。

再起動して Boot Menu を開き、KeyTool を起動したあと、
Save うんたらみたいな項目があるので EFI パーティションを選び保存する。

9. 鍵を登録

もう一度再起動して今度は BIOS 画面に入りセキュアブートを Setup Mode にするための項目を探して有効化します。
Setup Mode に移行することで、既存の鍵が消去されます。

BIOS の設定を保存したあと、KeyTool を起動し、 KEK -> db -> PK の順で登録します。
KEK と db は逆でもいいはずですが、PK は登録すると自動で User Mode に設定されるため最後に登録する必要があります。

ここまできたら再起動後にセキュアブートを有効化し、今回の目的である Device Guard を有効化します。
これで M715q でも Linux が満足に使えるようになりました。