第743回の
前回と同じく
update-grubコマンドがやっていること
改めて第743回の簡単なおさらいです。GRUBにはおおよそ次の4種類の設定方法が存在します。
/boot/
:GRUBが起動時に参照する設定ファイル。他のファイルはこれを生成するために存在するgrub/ grub. cfg /boot/
:grub/ grubenv grub.
の挙動を一時的・cfg 恒久的に変更できる環境変数ブロックで、 grub-editenv
等で設定する/etc/
:default/ grub update-grub
実行時に生成するgrub.
の内容をコントロールするための環境変数セットcfg /etc/
:grub. d/ update-grub
実行時に生成するgrub.
の断片を出力するスクリプト群cfg
第743回では、/etc/
を使った設定方法grub-editenv
の簡単な使い方も説明しています。
今回は残りのgrub.
と/etc/
の内容についての話になります。まずはgrub.
についてです。Ubuntuの/boot/
」
#
# DO NOT EDIT THIS FILE
#
# It is automatically generated by grub-mkconfig using templates
# from /etc/grub.d and settings from /etc/default/grub
#
Ubuntuを使う限りにおいて、grub.
を直接編集することは推奨されません。これはカーネルやGRUBのパッケージが更新されたときに、update-grub
コマンドによって上書き再生成されることがあるからです。grub.
の中身を変更したい場合は、/etc/
以下にある生成用の断片ファイルを編集・update-grub
を実行することが正しい手順となります。
ところで上記のメッセージにはgrub-mkconfig
」update-grub
」grub-mkconfig
」
$ cat $(command -v update-grub) #!/bin/sh set -e exec grub-mkconfig -o /boot/grub/grub.cfg "$@"
要するに出力ファイルを指定しなくても良いようにしただけですね。言い換えると、試しにシステムのgrub.
以外に出力したいならgrub-mkconfig
を直接実行して、/boot/
と差分をとって確認すれば良いということになります。
grub-mkconfig
コマンド自体は、いくつかの事前設定はありますが、基本的に/etc/
ディレクトリにある個々の実行可能なプログラムを、ファイル名の順番に実行し、その標準出力を指定したgrub.
ファイルに保存しているだけです。Ubuntuなら、次のようなプログラムがあります。いずれもシェルスクリプトですが、シェルスクリプトである必要はありません。
$ ls /etc/grub.d/ 00_header 10_linux_zfs 30_os-prober 40_custom 05_debian_theme 20_linux_xen 30_uefi-firmware 41_custom 10_linux 20_memtest86+ 35_fwupd README
READMEと、あとチルダ付きのバックアップファイル、拡張子にdpkとかrpmが付くパッケージマネージャーが退避したファイルなどは無視されます。実行した結果はgrub.
の中に次のような感じで記録されていきます。
### BEGIN (ファイル名) ###
出力結果
### END (ファイル名) ###
軽く内容を確認していきましょう。
00_
:header /etc/
の内容に基づいて、全体の設定や関数を生成する部分default/ grub 05_
:GRUBのテーマ設定。現在のUbuntuはほぼ何も設定していないdebian_ theme 10_
:Linux起動用のメニューエントリー。リカバリーモードもここに入るlinux 10_
:ZFS環境向けのLinux起動用のメニューエントリーlinux_ zfs 20_
:Xen環境向けのLinux起動用のメニューエントリーlinux_ xen 20_
:memtest86+を起動するメニューエントリー。EFI環境だと何も出力しないmemtest86+ 30_
:Windowsなど他のOSを検索し、メニューに追加する。Ubuntu 22.os-prober 04 LTSからは無効化されている 30_
:次回起動時にUEFIの設定画面を表示し再起動するfwsetupコマンドを実行するだけのメニューエントリーuefi-firmware 35_
:UEFI BIOSを更新するメニューエントリー。ただしサポートしている環境のみfwupd 40_
:カスタマイズ用のサンプルスクリプト。単にスクリプトの3行目以降をそのまま表示するだけcustom 41_
:同上。こちらはcustom grub.
と同じ場所にcfg custom.
があれば、それを読み込んで利用するcfg
ちなみにGRUBは国際化に対応しています。これはgettextを利用して、update-grub
実行時にgrub.
に出力する文字列を、実行時のロケールに合わせて翻訳するというものです。この場合、GRUBで表示されたメニューは翻訳された言語の文字列が使われるわけですが、正しく表示されるためにはフォントが対応している必要があります。
GRUBの場合は、PFF2フォーマットのunicode.grub-mkfont
で任意のフォントを変換し/boot/
以下に配置しgrub.
の中でloadfont フォントファイル名
」
以降は、既存の設定をベースに、Ubuntuではサポートしていない機能を追加してみましょう。
Windowsを検索対象にする
UbuntuはもともとWindowsとのデュアルブートをサポートしていました。初期のUbuntuだとインストール時にWindowsパーティションが見つかれば、そこからブックマークや連絡先を抽出して、UbuntuのFirefoxやThunderbird等に反映する仕組みが存在しましたし、WubiというWindowsのパーティションに直接Ubuntu用のイメージファイルを作成し、そこからデュアルブートする仕組みもありました。もちろんWindowsマシンにUbuntuをインストールした時は、GRUBのメニューにWindowsが表示されるような調整もされていました。
このあたりの設定は時代とともに徐々に廃止されていきます。セキュリティ的な都合やメンテナンスの難しさ、Windowsの高速ブートとの兼ね合いなど理由はさまざまです。そしてUbuntu 22.
Ubuntu 22./etc/
」
if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then
grub_warn "$(gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.")"
exit 0
elif [ "x${GRUB_DISABLE_OS_PROBER}" = "xauto" ]; then
# UBUNTU: We do not want to disable os-prober on upgrades if we found items before.
if test -e /boot/grub/grub.cfg && ! grep -q osprober /boot/grub/grub.cfg; then
grub_warn "$(gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.")"
exit 0
fi
fi
まとめると、次のような挙動になっています。
GRUB_
なら、Windows等のOS検知は行わないDISABLE_ OS_ PROBER="true" (GRUB 2. 06で追加された仕組み) GRUB_
なら、DISABLE_ OS_ PROBER="auto" grub.
で過去に検知した結果が残っていたら検知する、なければ何もしないcfg GRUB_
が上記以外の値なら、Windows等のOS検知を行うDISABLE_ OS_ PROBER
一般的なGRUBはgrub-mkconfig
の中でtrue
」auto
」/etc/
を変更しておくと良いでしょう。
$ echo 'GRUB_DISABLE_OS_PROBER="false"' | sudo tee -a /etc/default/grub $ sudo update-grub
もし自動検知は不要で、固定的なメニューエントリーがあれば良いということであれば、検知した結果のgrub.
の中身を抽出し、/etc/
などを作って次のようにしてしまうという手もあります。
#!/bin/sh
cat <<EOF
(grub.cfgの中身をここに記述する)
EOF
ファイル名のxx_
」25_
」update-grub
を実行するだけです。
$ sudo chmod +x /etc/grub.d/xx_windows $ sudo update-grub
実際に再起動する前にgrub.
の中身を見て、正しい結果になるか確認しておきましょう。
古き良きmemtestを復活させる
/etc/
はmemtest86+を起動するメニューエントリーです。memtest86+はメモリテストを行うオープンソースのプログラムで、新しいPCやメモリモジュールを購入した時に初期不良が存在しないかや、PCでよくわからない不具合が発生した時にメモリに問題がないかなどを調べるのに便利なツールです。
しかしながらUEFI環境だと次のように無効化されています。
# We need 16-bit boot, which isn't available on EFI.
if [ -d /sys/firmware/efi ]; then
echo "Memtest86+ needs a 16-bit boot, that is not available on EFI, exiting" >&2
exit 0
fi
これはオープンソース版のmemtest86+がLegacy BIOSのみサポートしており、UEFI BIOSでは動かなかったことによるものです。
しかしながら昨年リリースされたMemtest86+ V6では、念願のUEF BIOS対応が行われました。これはmemtest86+ V5からフォークしたPCMemTestが先祖返りしたものです。その際、従来のいくつかの機能が消えてしまっているようですが、単にテストするだけなら特に問題ないでしょう。また、セキュアブート対応については未対応で、将来リリースされる6.
memtest86+のバイナリは、ブータブルISOイメージとして配布されています。よってまずはここからmemtest86+の本体となるバイナリだけを取り出してしまいましょう。
$ unzip ../mt86plus_6.01_64.iso.zip $ mkdir temp $ sudo mount -o loop,ro mt86plus64.iso temp $ sudo cp temp/EFI/BOOT/bootx64.efi /boot/memtest.efi $ sudo umount temp $ file /boot/memtest.efi /boot/memtest.efi: Linux kernel x86 boot executable bzImage, version \353fHdrS\014\002, RW-rootFS,
file
コマンドでは
$ cat <<'EOD' | sudo tee /etc/grub.d/30_memtest #!/bin/sh cat <<EOF menuentry "Memory test" { linux /boot/memtest.efi } EOF EOD $ sudo chmod +x /etc/grub.d/30_memtest $ sudo update-grub
最後に次回起動時に
$ sudo grub-reboot "Memory test"
この状態で再起動します。ただしUEFIセキュアブートは一旦オフにしておいてください。もしオフでないなら、上記コマンドは先にsudo grub-reboot "UEFI Firmware Settings"
」
これでメモリテスト用のエントリーが追加されました。いつなんどき
GRUBにパスワードを設定する
Ubuntuは、基本的に
たとえばUbuntuのrootアカウントはロックされていてログインできませんし、sudoグループに所属したユーザーしかsudo
コマンドを実行できません。
しかしながら、マシンの前にいる人であれば誰でも、そのマシンの電源を切って無理やり再起動し、キーボードでGRUBメニューを表示させ、そこから
とは言え、ちょっとした手間をかけることで
GRUBのアクセス制限を有効化すると、次のような制約がかけられます。
- GRUBのCLIへのアクセスを管理者のみに限定する
- GRUBのメニューエントリーの編集を管理者のみに限定する
- GRUBの特定のメニューエントリーを、管理者ないし指定したユーザーのみがアクセスできるようにする
実際の手順を見ていきましょう。まずはアカウントとパスワードの記述方法ですが、password
コマンドとpassword_
コマンドの2種類が存在します。password
はgrub.
にアカウント名とパスワードをそのまま書く方法です。シンプルですがセキュアではありません。それに対してpassword_
のほうは、パスワードを別途生成したハッシュ値のみ記述する形になります。今回はpassword_
を使うことにします。password_
用のハッシュ値の生成は、grub-mkpasswd-pbkdf2
コマンドを利用します。
$ grub-mkpasswd-pbkdf2 パスワードを入力してください: Reenter password: PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.(長いハッシュ値)
上記のgrub.
」grub.
に記述するので控えておいてください。
次に/etc/
を次のように編集します。
#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.10000.(長いハッシュ値)
追加したのは最後の2行だけです。もちろん/etc/
以外を使ってもかまいません。superusers
は管理者アカウントとして指定する任意のアカウント名前を設定します。カンマなどで区切ると複数の指定が可能です。password_
に指定するのはアカウントの名前とそれに対応するパスワードハッシュです。password
ないしpassword_
で作ったアカウントで、superusers
にないアカウントは通常のアカウント扱いとなります。
ただしこの設定だけだと、
- 無制限に許可したい:
--unrestricted
オプションを付ける - 特定のユーザーにのみ許可したい:
--users
オプションを付ける - それ以外は管理者のみが選択可能
--users
オプションもカンマ等の区切りで複数の指定が可能になっています。
Ubuntuの場合はメニューエントリーが/etc/
で自動生成しているため、メニューエントリーにオプションを追加するためには、このスクリプトの修正が必要です。具体的には次のように変更します。
echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
else
echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' --unrestricted {" | sed "s/^/$submenu_indentation/"
「menuentry
」--unrestricted
」
通常は--unrestrited
」superusers
にないユーザーは、メニューエントリーの選択は可能なものの編集等は不可になります。また、--users ユーザー名
」
起動が失敗した時のリカバリー機能
Ubuntuのgrub.
には、起動が失敗したことを検知する2種類の仕組みが実装されています。
ひとつめはrecordfail
」/boot/
)recordfail=1
」/lib/
が起動したら)、grub-editenv /boot/
」recordfail
変数を削除します。その上で、次回起動時に次のように判断します。
recordfail
に1が設定されている:前回は何らかの理由で起動途中で止まっていたと疑われるので、GRBUのメニューを表示し、10秒間のタイムアウトを設定するrecordfail
変数が存在しないか1以外:前回は無事に起動したので、通常の起動シーケンスに入る
これにより起動失敗したときはGRUBメニューから別のカーネルを選択したり、起動オプションを変更しやすくなっています。ただし、自動的に別のカーネルを選ぶようにはなっていないので、手動での対応が必要です。
もうひとつはinitrdfail
」recordfail
と似たような感じで、GRUB側で変数をセットし、起動後に変数を削除、変数の内容によって次回起動時に挙動を変更することになります。
今回はシンプルに、recordfaile
が1だったら=前回の起動途中で何か問題が発生したら、Advanced optionの1個前のカーネルを選択する」recordfail
を判定しているところに処理を追加します。具体的には/etc/
の次の部分です。
make_timeout ()
{
cat << EOF
if [ "\${recordfail}" = 1 ] ; then
set timeout=${GRUB_RECORDFAIL_TIMEOUT:-30}
default="1>2"
fallback=0
else
追加したのはdefault="1>2"
」fallback
」default
の指定方法は、第743回のGRUB_
」
fallback
はrecordfail
が1に設定されるのでロード失敗すると次は失敗扱いになる)
これまでと同様にsudo update-grub
して反映します。動作確認にはrecordfail=1
を設定してしまいましょう。
$ sudo grub-editenv - set recordfail=1
これで再起動したら、自動的にリカバリー側に遷移します。GRUBのメニューエントリーが表示されて、30秒のタイムアウト設定になっていて、さらにuname -a
」
ちなみにgrub-common.
は、あまり厳密な依存関係を設定していません。よって
GRUBには他にもロードするイメージのハッシュ値をチェックする機能や、機能が制限されたロックダウンモード、TPMを利用したMeasuring Bootなどにも対応しています。実際に公式のマニュアルを参照すると、思いの外いろいろなことができることに気づくでしょう。ぜひ、自分なりのGRUBメニューを模索してみてください。
ただし大事なことなので、最後にもう一度伝えておきます。基本的には