Raspbian イメージの改造

Raspberry Pi の OS といえば、Raspbian が有名です。Raspberry Pi をサーバとして運用するなら、無駄なデスクっトプ環境のない「RASPBIAN STRETCH LITE」がいい感じです。

Raspbian はデフォルトだと ssh リモートログインができません。ssh を有効化するには、イメージ書き込み後に一旦マウントして、boot パーティションssh という空ファイル作る必要があります。でも、面倒です。ラズパイをサーバとして使うなら、ssh をデフォで有効化したオレオレイメージを作りたい!そこで、Raspbian イメージの改造をやってみたのですが、意外とハマりポイントがあったので、覚書を残しておこうと思います。

ちなみに、作業環境は Linux を想定しています。

TL;DR

2017-11-29-raspbian-stretch-lite.img の boot パーティションをマウント:

# losetup /dev/loop0 2017-11-29-raspbian-stretch-lite.img
# kpartx -a /dev/loop0
# mkdir /mnt/raspbian-boot
# mount /dev/mapper/loop0p1 /mnt/raspbian-boot

ssh ログインの有効化:

# touch /mnt/raspbian-boot/ssh

アンマウント:

# umount /mnt/raspbian-boot
# kpartx -d /dev/loop0
# losetup -d /dev/loop0
# rmdir /mnt/raspbian-boot

改造済みの .img を MicroSD に書き込む:

# dd if=2017-11-29-raspbian-stretch-lite.img of=/dev/sdx status=progress

予備知識:ISO ファイルのマウント

OS(ディストリビューション)を入れるときはインストーラの ISO ファイルを落としてきて、CD とか USB に dd コマンドで焼きますよね。例えば、hogehoge-linux.iso/dev/sdx に焼くときはこんな感じ:

# dd if=hogehoge-linux.iso of=/dev/sdx status=progress
1775919616 bytes (1.8 GB, 1.7 GiB) copied, 10.0328 s, 177 MB/s
3629056+0 レコード入力
3629056+0 レコード出力
1858076672 bytes (1.9 GB, 1.7 GiB) copied, 10.9957 s, 169 MB/s

昔は、dd コマンドは進捗表示してくれなかったのに、最近は便利になりましたね。

当然、/dev/sdx は適切なファイルシステムを指定すると、マウントすることができます。

# mkdir /mnt/hogehoge-linux    # マウント先ディレクトリを作る
# mount -t vfat /dev/sdx1 /mnt/hogehoge-linux    # FAT ファイルシステムとしてマウント
# ls /mnt/hogehoge-linux/
COPYING.linux        bcm2708-rpi-b-plus.dtb  bcm2710-rpi-3-b.dtb  config.txt  ...
# umount /mnt/hogehoge-linux    # アンマウント

イメージの書き込みに使った dd コマンドは単純にバイト列をファイルからデバイスに転送しているだけです。なので、ISO ファイル自体を直接マウントすることができます。

# mount -t iso9660 hogehoge-linux.iso /mnt/hogehoge-linux
# ls /mnt/hogehoge-linux/
COPYING.linux        bcm2708-rpi-b-plus.dtb  bcm2710-rpi-3-b.dtb  config.txt  ...

この状態で /mnt/hogehoge-linux/ 以下のファイルを編集すると、hogehoge-linux.iso も同じように変更されます。これで、OS イメージを改造できます。

IMG ファイルのマウント

IMG は ISO ファイルのようにはマウントできない

Raspbian のイメージは上記の方法では巧くいきません。

# mkdir /mnt/raspbian
# mount -t iso9660 /usr/local/src/2017-11-29-raspbian-stretch-lite.img /mnt/raspbian
mount: wrong fs type, bad option, bad superblock on /dev/loop0,
       missing codepage or helper program, or other error

       In some cases useful info is found in syslog - try
       dmesg | tail or so.

ちなみに、 /dev/loop0 はループ・デバイスと言って、通常のファイルをブロック・デバイスように操作するためのものです。

イメージの中身をチラ見してみます。

# file /usr/local/src/2017-11-29-raspbian-stretch-lite.img 
/usr/local/src/2017-11-29-raspbian-stretch-lite.img: DOS/MBR boot sector; partition 1 : ID=0xc, start-CHS (0x0,130,3), end-CHS (0x5,204,60), startsector 8192, 85045 sectors; partition 2 : ID=0x83, start-CHS (0x5,220,24), end-CHS (0xe1,229,4), startsector 94208, 3534848 sectors

複数のパーティションが含まれているみたいです。partition 1 (ID=0xc) がブート用パーティションで、partition 2 (ID=0x83) がルートのパーティションでしょう。

どうも、複数パーティションを含むイメージファイル(.iso ではなく .img)の場合は、losetup, kpartx というコマンドでパーティションをバラして、マウントする必要があるみたいです。同じようなことをやっている人がいましたw:Xenのイメージファイルをマウントする

作業としては、

  1. losetup で手動でループデバイスを設定する
  2. kpartx でパーティションマッピングを生成する
  3. パーティションを個別に mount する

という流れになります。

1. ループ・デバイスを設定

/dev/loop0 にイメージを設定します。これで、/dev/loop0 を通じてパーティションが見えるようになります。

# losetup /dev/loop0 /usr/local/src/2017-11-29-raspbian-stretch-lite.img
# losetup -a
/dev/loop0: [2052]:4340343 (/usr/local/src/2017-11-29-raspbian-stretch-lite.img)

ちゃんと見えてるよ、ほら!

# kpartx -l /dev/loop0
loop0p1 : 0 85045 /dev/loop0 8192
loop0p2 : 0 3534848 /dev/loop0 94208

2. パーティションマッピングを作る

# kpartx -a /dev/loop0
# ls /dev/mapper/
control  loop0p1  loop0p2

/dev/mapper/ 以下に loop0p1loop0p2 というデバイス・ファイルが追加されます。

3. mount する

/dev/mapper/ 以下のファイルを普通にマウントします。

# mkdir /mnt/raspbian1 /mnt/raspbian2
# mount /dev/mapper/loop0p1 /mnt/raspbian1
# mount /dev/mapper/loop0p2 /mnt/raspbian2

これで、boot と rootfs の中身が見えるようになりました。

boot:

# ls /mnt/raspbian1/
COPYING.linux        bcm2708-rpi-b-plus.dtb  bcm2710-rpi-3-b.dtb  config.txt    fixup_x.dat  overlays      start_x.elf
LICENCE.broadcom     bcm2708-rpi-b.dtb       bcm2710-rpi-cm3.dtb  fixup.dat     issue.txt    start.elf
LICENSE.oracle       bcm2708-rpi-cm.dtb      bootcode.bin         fixup_cd.dat  kernel.img   start_cd.elf
bcm2708-rpi-0-w.dtb  bcm2709-rpi-2-b.dtb     cmdline.txt          fixup_db.dat  kernel7.img  start_db.elf

rootfs:

# ls /mnt/raspbian2/
bin  boot  dev  etc  home  lib  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

イメージの編集

Raspberry Pi 起動時に ssh ログインを有効化するには、boot に ssh という名前の空ファイルを作るだけです。

# touch /mnt/raspbian1/ssh

他にも色々できるはずですが、それは、また今度にしましょう。

アンマウント

やることはやったので、後はお片付けの時間です。

# umount /mnt/raspbian1 /mnt/raspbian2
# kpartx -d /dev/loop0
# losetup -d /dev/loop0
# rmdir /mnt/raspbian1 /mnt/raspbian2

はい、これで元通りです。

ssh ログインの確認

編集が終わったイメージを MicroSD に書き込みます。

# dd if=/usr/local/src/2017-11-29-raspbian-stretch-lite.img of=/dev/sdx status=progress

この MicroSD をラズパイに入れて起動し、ssh でログインできれば成功です。デフォルトのパスワードは raspberry です。

$ ssh pi@192.168.0.42
pi@192.168.0.4's password: 
Linux raspberrypi 4.9.59-v7+ #1047 SMP Sun Oct 29 12:19:23 GMT 2017 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Jan 20 10:37:59 2018 from 192.168.0.5

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.

pi@raspberrypi:~ $ 

最後に

多分、ssh 鍵ファイルを突っ込んだり、初歩的な設定を事前にやっておく程度のことは、できるんじゃないでしょうか。ただし、apt/deb パッケージを入れたり、コンパイル済みのファイルを事前にインストールとかやるときは、バイナリ互換性を考えないと…。そういうことは、そもそも構成管理ツールでやるべきですかね。