Raspbian 初回起動時の init_resize.sh と regenerate_ssh_host_keys.servce

Facebooktwittergoogle_plustumblrmail

Taipei Hackerspace で友人が、いろいろセットアップが完了した Raspbian のシステムを複数 (十数台分) の SD カードに焼きたい、ということを言ってきたので、その治具製作を手伝うことにした。この時に調べた内容のメモをここに残す。

Raspbian はイメージを焼いた直後の起動時にだけ特別な処理がある。主なものは 2 つで、ひとつはパーティションサイズの拡張、もうひとつは ssh ホスト用の鍵ファイル生成だ。

元になる Raspbian は大本営 https://www.raspberrypi.org/downloads/raspbian/ からダウンロードしたものを使う。今回この記事を書くときに使ったテスト用環境では手元にあった 2017-03-02-raspbian-jessie-lite.zip を利用したが、日付が違ってもバージョンが Jessie なら多分作業内容は同じはず。


パーティションサイズの拡張

最初のパーティションサイズの拡張処理は、 SD カードの上の /boot/cmdline.txt の内容が関係している。初回起動時だけ、 init として /usr/lib/raspi-config/init_resize.sh というスクリプトが走るのだ。この内容を以下に引用する。

#!/bin/sh

reboot_pi () {
umount /boot
sync
echo b > /proc/sysrq-trigger
sleep 5
exit 0
}

check_commands () {
if ! command -v whiptail > /dev/null; then
echo ="whiptail not found"
sleep 5
return 1
fi
for COMMAND in grep cut sed parted fdisk; do
if ! command -v $COMMAND > /dev/null; then
FAIL_REASON="$COMMAND not found"
return 1
fi
done
return 0
}

check_noobs () {
if [ "$BOOT_PART_DEV" = "/dev/mmcblk0p1" ]; then
NOOBS=0
else
NOOBS=1
fi
}

get_variables () {
ROOT_PART_DEV=`grep -Eo 'root=[[:graph:]]+' /proc/cmdline | cut -d "=" -f 2-`
ROOT_PART_NAME=`echo $ROOT_PART_DEV | cut -d "/" -f 3`
ROOT_DEV_NAME=`echo /sys/block/*/${ROOT_PART_NAME} | cut -d "/" -f 4`
ROOT_DEV="/dev/${ROOT_DEV_NAME}"
ROOT_PART_NUM=`cat /sys/block/${ROOT_DEV_NAME}/${ROOT_PART_NAME}/partition`

BOOT_PART_DEV=`cat /proc/mounts | grep " /boot " | cut -d " " -f 1`
BOOT_PART_NAME=`echo $BOOT_PART_DEV | cut -d "/" -f 3`
BOOT_DEV_NAME=`echo /sys/block/*/${BOOT_PART_NAME} | cut -d "/" -f 4`
BOOT_PART_NUM=`cat /sys/block/${BOOT_DEV_NAME}/${BOOT_PART_NAME}/partition`

check_noobs

ROOT_DEV_SIZE=`cat /sys/block/${ROOT_DEV_NAME}/size`
TARGET_END=`expr $ROOT_DEV_SIZE - 1`

PARTITION_TABLE=`parted -m $ROOT_DEV unit s print | tr -d 's'`

LAST_PART_NUM=`echo "$PARTITION_TABLE" | tail -n 1 | cut -d ":" -f 1`

ROOT_PART_LINE=`echo "$PARTITION_TABLE" | grep -e "^${ROOT_PART_NUM}:"`
ROOT_PART_START=`echo $ROOT_PART_LINE | cut -d ":" -f 2`
ROOT_PART_END=`echo $ROOT_PART_LINE | cut -d ":" -f 3`

if [ "$NOOBS" = "1" ]; then
EXT_PART_LINE=`echo "$PARTITION_TABLE" | grep ":::;" | head -n 1`
EXT_PART_NUM=`echo $EXT_PART_LINE | cut -d ":" -f 1`
EXT_PART_START=`echo $EXT_PART_LINE | cut -d ":" -f 2`
EXT_PART_END=`echo $EXT_PART_LINE | cut -d ":" -f 3`
fi
}

check_variables () {
if [ "$NOOBS" = "1" ]; then
if [ $EXT_PART_NUM -gt 4 ] || \
[ $EXT_PART_START -gt $ROOT_PART_START ] || \
[ $EXT_PART_END -lt $ROOT_PART_END ]; then
FAIL_REASON="Unsupported extended partition"
return 1
fi
fi

if [ $ROOT_PART_NUM -ne $LAST_PART_NUM ]; then
FAIL_REASON="Root partition should be last partition"
return 1
fi

if [ $ROOT_PART_END -gt $TARGET_END ]; then
FAIL_REASON="Root partition runs past the end of device"
return 1
fi

if [ ! -b $ROOT_DEV ] || [ ! -b $ROOT_PART_DEV ] || [ ! -b $BOOT_PART_DEV ] ; then
FAIL_REASON="Could not determine partitions"
return 1
fi
}

main () {
get_variables

if ! check_variables; then
return 1
fi

if [ "$NOOBS" = "1" ]; then
BCM_MODULE=`cat /proc/cpuinfo | grep -e "^Hardware" | cut -d ":" -f 2 | tr -d " " | tr '[:upper:]' '[:lower:]'`
if ! modprobe $BCM_MODULE; then
FAIL_REASON="Couldn't load BCM module $BCM_MODULE"
return 1
fi
echo $BOOT_PART_NUM > /sys/module/${BCM_MODULE}/parameters/reboot_part
fi

if [ $ROOT_PART_END -eq $TARGET_END ]; then
reboot_pi
fi

if [ "$NOOBS" = "1" ]; then
if ! parted -m $ROOT_DEV u s resizepart $EXT_PART_NUM yes $TARGET_END; then
FAIL_REASON="Extended partition resize failed"
return 1
fi
fi

if ! parted -m $ROOT_DEV u s resizepart $ROOT_PART_NUM $TARGET_END; then
FAIL_REASON="Root partition resize failed"
return 1
fi

return 0
}

mount -t proc proc /proc
mount -t sysfs sys /sys

mount /boot
sed -i 's| quiet init=/usr/lib/raspi-config/init_resize.sh||' /boot/cmdline.txt
mount /boot -o remount,ro
sync

echo 1 > /proc/sys/kernel/sysrq

if ! check_commands; then
reboot_pi
fi

if main; then
whiptail --infobox "Resized root filesystem. Rebooting in 5 seconds..." 20 60
sleep 5
else
sleep 5
whiptail --msgbox "Could not expand filesystem, please try raspi-config or rc_gui.\n${FAIL_REASON}" 20 60
fi

reboot_pi

この中で、 /boot/cmdline.txt から init_resize.sh の呼び出しを削除しているので、次回以降の起動では init_resize.sh が走らず、通常の systemd での起動となる。

--- /a/boot/cmdline.txt
+++ /b/boot/cmdline.txt
@@ -1 +1 @@
-dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh
+dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

init_resize.sh は最後に自動でリブートする。


ssh ホスト用の鍵ファイル生成

もうひとつの ssh ホスト用の鍵ファイル生成処理は、 init_resize.sh がリブートをトリガしたあと、つまり正確に言えば 2 回めの起動時に走る /lib/systemd/system/regenerate_ssh_host_keys.service が処理している。この内容を以下に引用する。

[Unit]
Description=Regenerate SSH host keys

[Service]
Type=oneshot
ExecStartPre=/bin/sh -c "if [ -e /dev/hwrng ]; then dd if=/dev/hwrng of=/dev/urandom count=1 bs=4096; fi"
ExecStart=/usr/bin/ssh-keygen -A
ExecStartPost=/bin/rm /lib/systemd/system/regenerate_ssh_host_keys.service ; /usr/sbin/update-rc.d regenerate_ssh_host_keys remove

[Install]
WantedBy=multi-user.target

核心は ExecStart 行で、「 ssh-keygen -A 」コマンドを使って /etc/ssh/host_* を生成している。この実行後に regenerate_ssh_host_keys.service 自身を削除し且つ update-rc.d を使って登録解除しているので、それ以降のブートから自動では鍵が生成されなくなる。

余談: elinux.org にあった (RPi Remote Access – Initial Setup) のだが、もし後日手動で再実施するなら、以下のようにすればよい (私はまだやったことない) 。

sudo rm /etc/ssh/ssh_host_* && sudo dpkg-reconfigure openssh-server

参考

 

Facebooktwittergoogle_plustumblrmail
Yusuke Dada K.
Yusuke Dada K.
台湾の現地企業で主に組み込みソフトウエアの研究開発をしている日本人です。我人是個日本人,負責軟體的研究開發。在臺灣的科技公司工作。

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です