はじめに

平素は大変お世話になっております。
クイックガードのパー子です。

日夜 ITシステムの構築・運用に従事していると、稀に Linux の Liveイメージを使いたくなることがあります。

この記事では、Debian Live Project の成果物を用いて、独自にカスタマイズした Liveイメージを作成する手順を解説します。

参考文献

Liveイメージの作成に関する公式マニュアルです。
必要な情報は大体ここに載っています。

作業環境

Intel MacBook上に UTM で立ち上げた Debian 12 (bookworm) でビルドします。

起動システムや格納するメディアを問わない (※) Liveイメージを作成し、同UTM上で起動するまでを目標とします。

※ マシンの起動システムとして UEFI と BIOS の両方をサポートし、CD/DVD と USBメモリのどちらからでも起動できること。

作成手順

事前準備

live-buildパッケージをインストールしておきます。

$ sudo apt-get install live-build

続いて作業ディレクトリを用意します。
以降の作業はこのディレクトリの中で行います。

$ mkdir ./work
$ cd ./work/

初期化

lb configコマンドで作業ディレクトリを初期化します。

$ lb config \
    --distribution 'bookworm' \
    --archive-areas 'main non-free non-free-firmware contrib' \
    --bootappend-live 'boot=live components keyboard-layouts=jp quiet splash'

初期化すると作業ディレクトリ内に以下のツリーが作成されます。
特に ./config/ が重要で、ここに様々なファイルを配置することで Liveイメージをカスタマイズできます。

./
├── auto/
├── config/
│   ├── apt/
│   ├── archives/
│   ├── binary
│   ├── bootloaders/
│   ├── bootstrap
│   ├── chroot
│   ├── common
│   ├── debian-installer/
│   ├── hooks/
│   │   ├── live/
│   │   │   ├── 0010-disable-kexec-tools.hook.chroot -> /usr/share/live/build/hooks/live/0010-disable-kexec-tools.hook.chroot*
│   │   │   └── 0050-disable-sysvinit-tmpfs.hook.chroot -> /usr/share/live/build/hooks/live/0050-disable-sysvinit-tmpfs.hook.chroot*
│   │   └── normal/
│   │       ├── 1000-create-mtab-symlink.hook.chroot -> /usr/share/live/build/hooks/normal/1000-create-mtab-symlink.hook.chroot*
│   │       ├── ...
│   │       └── 9020-remove-man-cache.hook.chroot -> /usr/share/live/build/hooks/normal/9020-remove-man-cache.hook.chroot*
│   ├── includes/
│   ├── includes.binary/
│   ├── includes.bootstrap/
│   ├── includes.chroot_after_packages/
│   ├── includes.chroot_before_packages/
│   ├── includes.installer/
│   ├── includes.source/
│   ├── package-lists/
│   │   └── live.list.chroot
│   ├── packages/
│   ├── packages.binary/
│   ├── packages.chroot/
│   ├── preseed/
│   ├── rootfs/
│   └── source
└── local/
    └── bin/

lb configコマンドのパラメータの意味は次のとおりです。

パラメータ説明
--distributionLiveイメージのバージョン
--archive-areas利用するパッケージの アーカイブ領域
--bootappend-liveKernel の起動パラメータ

–distribution

Liveイメージのバージョンとして bookworm (Debian 12) を指定します。
(未指定の場合は buster (Debian 10) がデフォルトです。)

–archive-areas

近年の Debian では non-free なファームウェアは non-free-firmware で配布される ようになりました。

Wi-Fi を利用する場合などに必要となるので、お馴染みの main non-free contrib に加えてこれを指定します。

–bootappend-live

Liveイメージが起動する際、live-boot および live-config という、システムのブートと初期化を助けるスクリプト群が実行されます。

この挙動を Kernel の起動パラメータで調整することができます。

パラメータ説明
boot=livelive-boot/live-config の有効化
components (または live-config.components)live-config の全コンポーネントを有効化
keyboard-layouts=jp (または live-config.keyboard-layouts=jp)キーボードの配列を指定

まず boot=live を記述することで live-boot と live-config が有効化されます。

live-config では目的とする処理ごとにスクリプト・ファイルが分かれており、それを コンポーネントと呼んでいます。
スクリプト は Liveイメージの /lib/live/config/ に配置され、現時点では以下の順序で適用されます。
(番号の若いものが先に実行されます。)

コンポーネント
0005nss-systemd
0010debconf
0020hostname
0030live-debconfig_passwd
0030user-setup
0040sudo
0050locales
0070tzdata
0080gdm3
0085sddm
0090kdm
0100lightdm
0110lxdm
0120nodm
0130slim
0140xinit
0150keyboard-configuration
1020gnome-panel-data
1030gnome-power-manager
1040gnome-screensaver
1050kaboom
1060kde-services
1080policykit
1090ssl-cert
1110anacron
1120util-linux
1130login
1140xserver-xorg
1160openssh-server
1170xfce4-panel
1180xscreensaver
1190broadcom-sta
9990hooks
9995nss-systemd

キーボード配列として指定できる値は /usr/share/X11/xkb/rules/base.lst に列挙されています。
日本語キーボードの場合は jp です。

/usr/share/X11/xkb/rules/base.lst
...

! layout
  us              English (US)
  ...
  jp              Japanese
  ...

他にも多数の起動パラメータが存在します。
詳細は各パッケージの man (live-boot / live-config) を参照してください。

ブートローダ

ブートローダに関する設定を変更します。

  • タイムアウト
  • スプラッシュ画像

ブートローダの設定ファイルを作業ディレクトリ内の ./config/bootloaders/配下に配置することで変更できます。

なお、設定ファイルの雛形は /usr/share/live/build/bootloaders/<ブートローダの種類>/配下に格納されているので、これを加工して使います。

タイムアウト

Liveイメージを起動すると、デフォルトでは起動メニューが表示され、選択されるまで待ち合わせます。
これを変更し、無限に待ち合わせるのではなく、指定の秒数経過 (今回は 5秒) で自動的にデフォルトのエントリーが選択されるようにします。

補足

環境によっては 起動メニューが表示されない ことがあります。
後述するように、ディスプレイ設定を調整することで解消する可能性があるようです。

まずは UEFI用のブートローダである GRUB の設定ファイルを配置します。

$ mkdir ./config/bootloaders/grub-pc

$ {
    cat /usr/share/live/build/bootloaders/grub-pc/config.cfg

    echo 'set timeout_style=menu'
    echo 'set timeout=5'
  } > ./config/bootloaders/grub-pc/config.cfg

同様に、BIOS のために ISOLINUX の設定ファイルを配置します。

$ mkdir ./config/bootloaders/isolinux

$ cat /usr/share/live/build/bootloaders/isolinux/isolinux.cfg \
    | sed -E 's/^timeout 0/timeout 50/' \
    > ./config/bootloaders/isolinux/isolinux.cfg

補足

ISOLINUX のタイムアウト値は 0.1秒刻みのため、5秒の場合は 50 を指定します。

メニュー画面が表示されない場合:

今回の実行環境では、BIOS で起動した場合、デフォルトの設定のままではメニュー画面がうまく表示されませんでした。
(代わりに “Guest has not initialized the display (yet).” というメッセージが出続けます。)

解決方法としては、UTM の仮想マシン設定画面の “ディスプレイ” » “仮想ディスプレイカード” を VGA あたりに変更することで表示されるようになります。

スプラッシュ画像

メニュー画面に表示されるスプラッシュ画像を好みのものに差し替えます。

./config/bootloaders/syslinux_common/splash.svg として配置すれば GRUB と ISOLINUX の両方に反映されます。

$ mkdir ./config/bootloaders/syslinux_common

$ cp /PATH/TO/splash.svg ./config/bootloaders/syslinux_common/

PNG でもよいようですが、その場合は 640x480ピクセル にするとよさそうです。

When modifying one of the default themes, if you want to use a personalized background image that will be displayed together with the boot menu, add a splash.png picture of 640x480 pixels.

なお、SVGファイルには @DISTRIBUTION@@DATE@ などのプレースホルダを含めることができるようです。
(詳細は /usr/lib/live/build/binary_syslinux の処理内容を確認してください。)

パッケージ

Liveイメージにプレインストールしておくパッケージを指定します。

./config/package-lists/<任意の名称>.list.chroot というファイルを作成し、1行1パッケージで列挙します。

今回は OpenSSHサーバを入れて、SSH でログインできるようにしてみます。

$ echo 'openssh-server' > ./config/package-lists/default.list.chroot

ブート・フック

先に説明した ように、システムが起動すると /lib/live/config/配下のスクリプトが順番に実行されます。

OpenSSHサーバをインストールしても、標準コンポーネントの 1つである 1160-openssh-server により パスワード認証が無効化されてしまう ため、認証用の設定を施さないと SSH でログインできません。

セキュリティ的には鍵や証明書を用いるのが望ましいのですが、今回は簡便のためにパスワード認証を復活させることにします。
以下のスクリプトを追加の live-configコンポーネントとして配置することで実現します。

1161-allow-password-ssh
#!/bin/sh

echo -n ' allow-password-ssh'

sed -i -E 's/^PasswordAuthentication no$/PasswordAuthentication yes/' /etc/ssh/sshd_config

Liveイメージにファイルを追加するには、目的のファイルを ./config/includes.chroot_after_packages/配下にツリー構造を保って格納するだけで OK です。
./config/includes.chroot_after_packages/ が Liveシステムのルート・ディレクトリ / に相当するため、上記スクリプトの配置パスは ./config/includes.chroot_after_packages/lib/live/config/1161-allow-password-ssh となります。

$ mkdir -p ./config/includes.chroot_after_packages/lib/live/config

$ install -m 0755 /PATH/TO/1161-allow-password-ssh ./config/includes.chroot_after_packages/lib/live/config/1161-allow-password-ssh

ビルド

カスタマイズが完了したら、lb buildコマンドで ISOファイルを作成します。

$ sudo lb build

補足

chroot などを実行するので root権限が必要です。

作成には 15分ほどかかります。

完了すると ./live-image-amd64.hybrid.iso というファイルが生成されます。

起動する

出来上がった Liveイメージを UTM で起動してみましょう。

起動パターンは以下の 4つです。

起動システムメディア
UEFICD/DVD
UEFIUSBメモリ
BIOSCD/DVD
BIOSUSBメモリ

起動システムは設定画面の “QEMU” » “UEFI起動” で切り替えます。

起動メディアは、同じく “ドライブ” で変更できます。

“イメージの種類” と “インターフェイス” は以下の組み合わせにします。

メディアイメージの種類インターフェイス
CD/DVDCD/DVD (ISO) イメージIDE
USBメモリディスクイメージUSB

UEFI & CD/DVD

正常に起動しました。

これは GRUB のメニュー画面ですね。
スプラッシュ画像も意図どおりのものです。

何もキーを押さないと 5秒でタイムアウトして自動的にデフォルト (= 一番上) のエントリーで OS が起動します。

そのまま OS を立ち上げて、SSH でログインすることもできました。

補足

ユーザ名とパスワードは user / live です。

UEFI & USBメモリ

USBメモリからでも問題なく起動できました。

CD/DVD との差異は特に見られません。

BIOS & CD/DVD

今となっては UEFI に取って代わられてあまり見かけなくなりましたが、レガシーな BIOS でも起動できることを確認しました。

GRUB ではなく、ISOLINUX によるメニュー画面です。

BIOS & USBメモリ

こちらも問題なしです。

CD/DVD との違いはありません。

もっとカスタマイズしたい

今回の要件には含まれていませんでしたが、以下のようなカスタマイズも検討されることが多いかと思います。

ユーザ名を変更する

Kernel の起動パラメータで変更できます。

$ lb config --bootappend-live 'boot=live components username=<ユーザ名>'

パスワードはブート・フックで変更します。

/usr/share/doc/live-config/examples/hooks/passwd というスクリプト があるので、これを ./config/includes.chroot_after_packages/lib/live/config/2000-passwd などと配置することで起動時にパスワードの変更プロンプトが表示されるようになります。

…と、上記のように 公式マニュアル には書かれているのですが、試してみたところプロンプトがうまく表示されずに起動途中で止まってしまいました。

インタラクティブにパスワードを指定する必要がなければ chpasswdコマンドで固定値に変更してしまってもよいかもしれません。
ユーザ名は Kernel の起動パラメータから拾います。
(/proc/cmdline から直接取得してもよいですし、環境変数 LIVE_CONFIG_CMDLINE にも同じものが格納されています。)

2000-chpasswd
#!/bin/sh

echo -n ' 2000-chpasswd'

for _PARAMETER in ${LIVE_CONFIG_CMDLINE}; do
  case "${_PARAMETER}" in
    live-config.username=*|username=*)
      LIVE_USERNAME="${_PARAMETER#*username=}"
      ;;
  esac
done

echo "${LIVE_USERNAME:-user}"':Pa$$w0rd' | chpasswd

ロケール/タイムゾーンを変更する

ユーザ名と同じく Kernel の起動パラメータで変更できます。

$ lb config --bootappend-live 'boot=live components locales=<ロケール> timezone=<タイムゾーン>'

デフォルトはそれぞれ en_US.UTF-8 / UTC です。

書き込みを永続化する

Liveイメージのデータは読み取り専用のファイル・システムに格納されており、稼働している間に加えた変更はメモリ上に保持されます。
そのため、再起動すると変更内容が失われてしまうのですが、焼き込んだメディア (= USBメモリなど) の空き領域を活用することで永続的に保持できるようになります。

ここでは仮想的な USBメモリとしてファイルに Liveイメージを焼き込み、同ファイル上に永続化領域をセットアップしてみます。

まず、永続化機能を使用するためには Kernel起動パラメータに persistence を含める必要があるため、Liveイメージを作り直します。

$ lb config \
    --distribution 'bookworm' \
    --archive-areas 'main non-free non-free-firmware contrib' \
    --bootappend-live 'boot=live components persistence keyboard-layouts=jp quiet splash'

このイメージを焼き込んだ仮想USBメモリ live-image-amd64.hybrid.img を生成します。
サイズは、Liveイメージが 630MiB程度なので、1GiB とします。

$ cp ./live-image-amd64.hybrid.iso ./live-image-amd64.hybrid.img

$ truncate -s '1G' ./live-image-amd64.hybrid.img

$ ls -gGh ./live-image-amd64.hybrid.*
-rw-r--r-- 1 1.0G Feb 20 09:43 ./live-image-amd64.hybrid.img
-rw-r--r-- 1 628M Feb 20 09:43 ./live-image-amd64.hybrid.iso

続いて当該ファイルを loopデバイスに紐付けます。

$ sudo losetup -f
/dev/loop0

$ sudo losetup /dev/loop0 ./live-image-amd64.hybrid.img

永続化用のパーティションを作成します。
Liveイメージの正確なサイズは 658,505,728 Bytes で、それ以降の領域をすべて永続化用パーティションにします。

$ ls -gG ./live-image-amd64.hybrid.iso
-rw-r--r-- 1 658505728 Feb 20 09:43 ./live-image-amd64.hybrid.iso

$ sudo parted /dev/loop0
GNU Parted 3.5
Using /dev/loop0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Warning: The driver descriptor says the physical block size is 2048 bytes, but Linux says it is 512 bytes.
Ignore/Cancel? ignore
Model: Loopback device (loopback)
Disk /dev/loop0: 4295MB
Sector size (logical/physical): 2048B/512B
Partition Table: mac
Disk Flags:

Number  Start  End     Size    File system  Name   Flags
 1      2048B  6143B   4096B                Apple
 2      379kB  5589kB  5210kB               EFI

(parted) mkpart
Partition name?  []?
File system type?  [ext2]? ext4
Start? 658505728B
End? 100%
Error: Too many primary partitions.

セクター・サイズが一致しない、総容量がおかしい、パーティション・テーブルの形式は mac でいいのかなど、いろいろ気になる点はありますが、結局 “Too many primary partitions.” と怒られてパーティションの作成に失敗してしまいました。
う〜ん、公式マニュアル では軽いノリでパーティションを作成しているのですが…。

後述しますが、今回の Liveイメージは isohybrid というテクニックを用いてビルドされており、パーティション・テーブルの構造がどうにも胡散くさいように思います。
そこで、安定していそうな hdd形式 で試してみることにします。

$ lb config \
    --distribution 'bookworm' \
    --binary-image 'hdd' \
    --archive-areas 'main non-free non-free-firmware contrib' \
    --bootappend-live 'boot=live components persistence keyboard-layouts=jp quiet splash'

この設定で作成した Liveイメージを用いて改めてトライしてみます。

$ sudo parted /dev/loop0
GNU Parted 3.5
Using /dev/loop0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: Loopback device (loopback)
Disk /dev/loop0: 1074MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End    Size   Type     File system  Flags
 1      1049kB  724MB  722MB  primary  fat32        boot

(parted) mkpart primary ext4 723517440B 100%
(parted) print
Model: Loopback device (loopback)
Disk /dev/loop0: 1074MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size   Type     File system  Flags
 1      1049kB  724MB   722MB  primary  fat32        boot
 2      724MB   1074MB  350MB  primary  ext4         lba

(parted) quit
Information: You may need to update /etc/fstab.

いけました。

作成したパーティションを ext4 でフォーマットします。
ボリューム・ラベルは persistence とする必要があります。

$ sudo mkfs.ext4 -L 'persistence' /dev/loop0p2
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 342016 1k blocks and 85344 inodes
Filesystem UUID: 463b74a6-691a-46c9-9b75-0778ca5e55ba
Superblock backups stored on blocks:
        8193, 24577, 40961, 57345, 73729, 204801, 221185

Allocating group tables: done
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

最後に永続化用の設定ファイルである persistence.conf をルートに配置します。

ここでは /home/以下を永続化するようにしてみます。

$ sudo mount /dev/loop0p2 /mnt/

$ echo '/home' | sudo tee /mnt/persistence.conf > /dev/null

$ sudo umount /mnt/

$ sudo losetup -d /dev/loop0

以上で設定完了です。

UTM の起動モードが BIOS となっていること、および、ドライブ設定の “読み出しのみ” のチェックが外れていることを確認したうえで起動してみましょう。
/home/配下への変更が、再起動した後も保持されているはずです。

補足

hdd形式の Liveイメージは、パーティション形式からもわかるとおり、UEFI では起動できません。
あくまで isohybrid を認識できない (レガシーな?) マシン用のイメージということでしょうか。

デフォルトの挙動では、初回起動時に指定したツリーが丸ごと永続化領域へコピーされるようです。
/usr/ などへの変更は Liveイメージとの差分のみ保持したいはずなので、そのようなときは /usr union などとすればよいです。
(詳細は man を確認してください。)

また、暗号化 することもできるようです。

Apendix

isohybrid

Debian の Liveイメージは、デフォルトでは isohybrid というテクニックを用いて作成されます。

光学メディアとストレージ・デバイスでは起動の仕組みが異なるため、通常はそれぞれの起動メディア専用のイメージを別々に用意する必要があります。
この手間を解消して、単一のバイナリで両方のメディアに対応するのが isohybrid です。

isohybrid の肝は、ISO 9660ファイル・システムの先頭にある 32KiB の未使用領域 です。
この領域は光学ドライブから読み込む場合は無視されるため、自由なデータを書き込むことができます。
光学メディアとして標準的な ISO 9660形式のファイル・システムを維持しながら、先頭の未使用領域にストレージ・デバイス用のブートローダやパーティション・テーブルを書き込むことでハイブリッドなイメージを実現しているのです。

UEFI

UEFIシステムにおいて、ブートローダは ESP (= EFI System Partition) と呼ばれるパーティションに格納されている必要があります。

そこで、ISOファイル・システム上に UEFI用のブートローダを含んだイメージ (= ブート・イメージ) を配置しておき、そのイメージが占有する領域を仮想的な ESPパーティションとしてパーティション・テーブルに登録します。
USBメモリなどのストレージ・デバイスから起動すると、UEFI はパーティション・テーブルの記述に従ってこのブート・イメージを ESP と認識して起動処理を進めるのです。

BIOS

BIOS の場合は MBR (= Master Boot Record) に格納されたブートローダが使用されます。

UEFI で使用されるパーティション・テーブル (= GPT; GUID Partition Table) の先頭には Protective MBR という従来の MBR と同サイズの領域が確保されているため、UEFI用の設定を破壊することなく共存できます。

ちなみに、光学メディアから起動する場合でも UEFI と BIOS で異なるブートローダが使用されますが、これは Bootable CD のための規格である El Torito に基づいて複数のブートローダを設置しているようです。

まとめ

以上、Debian の Liveイメージを自作する手順をご紹介しました。

オリジナルの Liveイメージを作成し、起動システム (UEFI or BIOS) やメディア (CD/DVD or USBメモリ) を問わず起動できることを確認しました。

isohybridイメージだと変更の永続化がうまくいきませんでしたが、もし成功したという方がいらっしゃいましたらぜひ教えてください。

今後ともよろしくお願い申し上げます。