UEFIのSecure boot + kdump・kexec

UEFIのSecure boot + kdump・kexecの動作確認

uefiのSecure bootが有効な環境でkdump・kexecは動作するのか?ってところの確認です。

Secure bootとは?というところはこちらのドキュメントを参照してください。 access.redhat.com

テスト環境

テスト環境にはlibvirtを使います。ホストはFedora 30 x86_64、ゲストもFedora 30 x86_64です。

テスト環境セットアップ

まずはSecure bootを有効にした環境を作ります。まずは普通にlibvirtを使ってOSをインストールします。 このときに、UEFIを使うように設定します。そのため、edk2-ovmfパッケージをインストールしておく必要があります。

f:id:masami256:20190830235150p:plain

インストール段階だとセキュアブート用のファームウェアは指定できないのでとりあえずはセキュアブートなしでインストールします。

kdumpの設定

インストールが終わったら普通に設定しましょう。

access.redhat.com

この時点で動作確認もしてkdumpが正常動作することを確認しておきます。

Secure bootの設定

virsh editでxmlファイルを編集します。VMは一旦shutdownしておきましょう。

まず、OVMF_VARS.secboot.fdをコピーします。コピー先の名前はなんでも良いです。ここではvm名と合わせてます。

$ sudo cp /usr/share/edk2/ovmf/OVMF_VARS.secboot.fd /var/lib/libvirt/qemu/nvram/secureboot-test_VARS_secboot.fd

そしたらvirsh editで編集します。以下の部分を探します。

  <os>
    <type arch='x86_64' machine='pc-q35-3.1'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/edk2/ovmf/OVMF_CODE.fd</loader>
    <nvram>/var/lib/libvirt/qemu/nvram/secureboot-test_VARS.fd</nvram>
    <boot dev='hd'/>
  </os>

そして、以下のように編集します。変更したのはloaderのところでOVMF_CODE.secboot.fdを指定したのと、nvramには先ほどコピーしたファイルを指定しました。

  <os>
    <type arch='x86_64' machine='pc-q35-3.1'>hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</loader>
    <nvram>/var/lib/libvirt/qemu/nvram/secureboot-test_VARS_secboot.fd</nvram>
    <boot dev='hd'/>
  </os>

セキュアブート有効で起動

普通にvmを起動すればセキュアブートが有効な状態で立ち上がります。 dmesgで確認するとこんなふうになります。

[masami@secureboot-test ~]$ dmesg | grep secureboot
[    0.000000] secureboot: Secure boot enabled
[    0.928493] systemd[1]: Set hostname to <secureboot-test>.
[    3.770079] systemd[1]: Set hostname to <secureboot-test>.
[masami@secureboot-test ~]$ 

これでvmのセキュアブートが有効になりました。

kdumpのテスト

セキュアブートが有効な状態でkdumpをスタートすると失敗します。エラーメッセージはこのようになってます。

[masami@secureboot-test ~]$ sudo systemctl status kdump
● kdump.service - Crash recovery kernel arming
   Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Fri 2019-08-30 22:29:52 JST; 12s ago
  Process: 1316 ExecStart=/usr/bin/kdumpctl start (code=exited, status=1/FAILURE)
 Main PID: 1316 (code=exited, status=1/FAILURE)

Aug 30 22:29:51 secureboot-test systemd[1]: Starting Crash recovery kernel arming...
Aug 30 22:29:52 secureboot-test kdumpctl[1316]: Secure Boot is enabled. Using kexec file based syscall.
Aug 30 22:29:52 secureboot-test kdumpctl[1316]: kexec_file_load failed: Operation not permitted
Aug 30 22:29:52 secureboot-test kdumpctl[1316]: kexec: failed to load kdump kernel
Aug 30 22:29:52 secureboot-test kdumpctl[1316]: Starting kdump: [FAILED]
Aug 30 22:29:52 secureboot-test systemd[1]: kdump.service: Main process exited, code=exited, status=1/FAILURE
Aug 30 22:29:52 secureboot-test systemd[1]: kdump.service: Failed with result 'exit-code'.
Aug 30 22:29:52 secureboot-test systemd[1]: Failed to start Crash recovery kernel arming.

dmesgでカーネルのエラーを確認するとこのようなメッセージを発見できます。

[  173.215036] Lockdown: kexec: /proc/kcore is restricted; see man kernel_lockdown.7

カーネルのコンフィグを確認するとLockdown機能が有効になってますね。

[masami@secureboot-test ~]$ grep LOCK_DOWN /boot/config-5.2.9-200.fc30.x86_64 
CONFIG_LOCK_DOWN_KERNEL=y
# CONFIG_LOCK_DOWN_KERNEL_FORCE is not set
CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT=y

Lockdownの解除

手元にmanがなかったので検索するとこちらが見つかりました。

lwn.net

manによると物理的に接続されたキーボードからのsysrq + xでlockdownが解除できるとあります。というわけで、virsh send-keyでsysrq + xを送ります。

$ sudo virsh send-key secureboot-test KEY_LEFTALT KEY_SYSRQ KEY_X

そうすると以下のようにlockdownを解除したよってメッセージがでます。

[masami@secureboot-test ~]$ dmesg
[ 2236.328389] sysrq: Disabling Secure Boot restrictions
[ 2236.328934] Lifting lockdown

kdumpのテスト

まずセキュアブートが有効か確認します。

[masami@secureboot-test ~]$ sudo mokutil --sb-state
SecureBoot enabled
[masami@secureboot-test ~]$ 

で、kdumpを起動

[masami@secureboot-test ~]$ sudo systemctl start kdump

エラーが出なければOKなので、クラッシュさせてみると /var/crash/に該当時刻のディレクトリが出来ていてコアダンプが取得できてるのが確認できます😃

[masami@secureboot-test ~]$ sudo sh -c "echo c > /proc/sysrq-trigger"
client_loop: send disconnect: Broken pipe
masami@moon:~$ ssh secureboot-fedora 
Web console: https://secureboot-test:9090/ or https://192.168.122.191:9090/

Last login: Fri Aug 30 22:58:25 2019 from 192.168.122.1
[masami@secureboot-test ~]$ ls /var/crash/
127.0.0.1-2019-08-30-01:05:24  127.0.0.1-2019-08-30-23:38:24
[masami@secureboot-test ~]$ date
Fri 30 Aug 2019 11:38:53 PM JST
[masami@secureboot-test ~]$ 

まとめ

セキュアブートが有効な環境でもkdump・kexecは使えますが、カーネルのLockdown機能が有効な場合は使えません。もしkdump・kexecを使用したい場合はLockdownを止める必要があるということでした。

追記1 2019/08/31

kexecとセキュアブート

セキュアブートが有効な場合と無効な場合ではkexec実行時のオプションがちょっと違います。では具体的に何が違うのか?というとセキュアブートが有効な場合は-sオプションを使います。 Fedoraの場合はkdumpctlコマンドがこの辺の処理をしていて、以下のようなコードとなってます。

    
    # For secureboot enabled machines, use new kexec file based syscall.
    # Old syscall will always fail as it does not have capability to
    # to kernel signature verification.
    if is_secure_boot_enforced; then
        echo "Secure Boot is enabled. Using kexec file based syscall."
        KEXEC_ARGS="$KEXEC_ARGS -s"
    fi
 
    $KEXEC $KEXEC_ARGS $standard_kexec_args \
        --command-line="$KDUMP_COMMANDLINE" \
        --initrd=$TARGET_INITRD $kdump_kernel
    if [ $? == 0 ]; then
        echo "kexec: loaded kdump kernel"
        return 0
    else
        echo "kexec: failed to load kdump kernel" >&2
        return 1
    fi

セキュアブートが有効な場合は-sオプション付けて使用するシステムコールを変える必要があります。

f:id:masami256:20190831104347p:plain

超例解Linuxカーネルプログラミング~最先端Linuxカーネルの修正コードから学ぶソフトウェア品質~

超例解Linuxカーネルプログラミング~最先端Linuxカーネルの修正コードから学ぶソフトウェア品質~