Headless setup for Raspberry Pi OS 13 (trixie)

はじめに

久々に RPI OS のインストールをしようとしたところ,Bookworm 以降 wpa_supplicant.conf による wifi 設定ができない仕様になったらしい.これまで rpi-imager を使わずに headless setup していたので困った.

インターネット上にはあまりまとまった情報が見当たらなかったが,rpi-imager を使えば実現できているようだったので,今回は最新の trixie と rpi-imager での実装方法を整理しておく.

rpi-imager での headless setup の実装方法

先にざっくりと概要だけ書いておくと,初期設定スクリプト firstrun.sh を cmdline.txt で呼び出すことで,ユーザやWIFI等の初期設定を実現している.

以下実装の詳細.

cmdline.txt

bootfs$ cat cmdline.txt 
console=serial0,115200 console=tty1 root=PARTUUID=13e740f8-02 rootfstype=ext4 fsck.repair=yes rootwait resize quiet splash plymouth.ignore-serial-consoles cfg80211.ieee80211_regdom=JP systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target
  • cfg80211.ieee80211_regdom=JP
    • WIFIの規制ドメインに JP を指定
  • systemd.run=/boot/firstrun.sh
    • TransientUnit として firstrun.sh を実行
  • systemd.run_success_action=reboot
    • firstrun.sh が成功したら再起動
  • systemd.unit=kernel-command-line.target
    • systemd のターゲットとして kernel-command-line.target を指定

firstrun.sh(全量)

以下は初期化処理の全量.私の環境固有値は変数 ${xxx} として伏せておく.

このスクリプトをベースを参考すればオリジナルの初期化処理も簡単に書けそう.

bootfs$ cat firstrun.sh 
#!/bin/bash

set +e

CURRENT_HOSTNAME=`cat /etc/hostname | tr -d " \t\n\r"`
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_hostname raspberrypi
else
   echo raspberrypi >/etc/hostname
   sed -i "s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\traspberrypi/g" /etc/hosts
fi
FIRSTUSER=`getent passwd 1000 | cut -d: -f1`
FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh
else
   systemctl enable ssh
fi
if [ -f /usr/lib/userconf-pi/userconf ]; then
   /usr/lib/userconf-pi/userconf '${USER}' '${PASSWD}'
else
   echo "$FIRSTUSER:"'${PASSWD}' | chpasswd -e
   if [ "$FIRSTUSER" != "${USER}" ]; then
      usermod -l "${USER}" "$FIRSTUSER"
      usermod -m -d "/home/${USER}" "${USER}"
      groupmod -n "${USER}" "$FIRSTUSER"
      if grep -q "^autologin-user=" /etc/lightdm/lightdm.conf ; then
         sed /etc/lightdm/lightdm.conf -i -e "s/^autologin-user=.*/autologin-user=${USER}/"
      fi
      if [ -f /etc/systemd/system/getty@tty1.service.d/autologin.conf ]; then
         sed /etc/systemd/system/getty@tty1.service.d/autologin.conf -i -e "s/$FIRSTUSER/${USER}/"
      fi
      if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then
         sed -i "s/^$FIRSTUSER /${USER} /" /etc/sudoers.d/010_pi-nopasswd
      fi
   fi
fi
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_wlan '${WIFI_SSID}' '${WIFI_PASSWD}' '${WIFI_COUNTRY}'
else
cat >/etc/wpa_supplicant/wpa_supplicant.conf <<'WPAEOF'
country=${WIFI_COUNTRY}
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
ap_scan=1

update_config=1
network={
        ssid="${WIFI_SSID}"
        psk=${WIFI_PASSWD}
}

WPAEOF
   chmod 600 /etc/wpa_supplicant/wpa_supplicant.conf
   rfkill unblock wifi
   for filename in /var/lib/systemd/rfkill/*:wlan ; do
       echo 0 > $filename
   done
fi
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_keymap 'us'
   /usr/lib/raspberrypi-sys-mods/imager_custom set_timezone 'Asia/Tokyo'
else
   rm -f /etc/localtime
   echo "Asia/Tokyo" >/etc/timezone
   dpkg-reconfigure -f noninteractive tzdata
cat >/etc/default/keyboard <<'KBEOF'
XKBMODEL="pc105"
XKBLAYOUT="us"
XKBVARIANT=""
XKBOPTIONS=""

KBEOF
   dpkg-reconfigure -f noninteractive keyboard-configuration
fi
rm -f /boot/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt
exit 0

ホスト名設定

CURRENT_HOSTNAME=`cat /etc/hostname | tr -d " \t\n\r"`
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_hostname raspberrypi
else
   echo raspberrypi >/etc/hostname
   sed -i "s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\traspberrypi/g" /etc/hosts
fi

SSH有効化

if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh
else
   systemctl enable ssh
fi

ユーザ&GUログインマネージャ(lightdm)&コンソール(getty)の設定

FIRSTUSER=`getent passwd 1000 | cut -d: -f1`
FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`
if [ -f /usr/lib/userconf-pi/userconf ]; then
   /usr/lib/userconf-pi/userconf '${USER}' '${PASSWD}'
else
   echo "$FIRSTUSER:"'${PASSWD}' | chpasswd -e
   if [ "$FIRSTUSER" != "${USER}" ]; then
      usermod -l "${USER}" "$FIRSTUSER"
      usermod -m -d "/home/${USER}" "${USER}"
      groupmod -n "${USER}" "$FIRSTUSER"
      if grep -q "^autologin-user=" /etc/lightdm/lightdm.conf ; then
         sed /etc/lightdm/lightdm.conf -i -e "s/^autologin-user=.*/autologin-user=${USER}/"
      fi
      if [ -f /etc/systemd/system/getty@tty1.service.d/autologin.conf ]; then
         sed /etc/systemd/system/getty@tty1.service.d/autologin.conf -i -e "s/$FIRSTUSER/${USER}/"
      fi
      if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then
         sed -i "s/^$FIRSTUSER /${USER} /" /etc/sudoers.d/010_pi-nopasswd
      fi
   fi
fi

Wi-Fi設定

if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_wlan '${WIFI_SSID}' '${WIFI_PASSWD}' '${WIFI_COUNTRY}'
else
cat >/etc/wpa_supplicant/wpa_supplicant.conf <<'WPAEOF'
country=${WIFI_COUNTRY}
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
ap_scan=1

update_config=1
network={
        ssid="${WIFI_SSID}"
        psk=${WIFI_PASSWD}
}

WPAEOF
   chmod 600 /etc/wpa_supplicant/wpa_supplicant.conf
   rfkill unblock wifi
   for filename in /var/lib/systemd/rfkill/*:wlan ; do
       echo 0 > $filename
   done
fi

キーボードレイアウト&タイムゾーン設定

if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_keymap 'us'
   /usr/lib/raspberrypi-sys-mods/imager_custom set_timezone 'Asia/Tokyo'
else
   rm -f /etc/localtime
   echo "Asia/Tokyo" >/etc/timezone
   dpkg-reconfigure -f noninteractive tzdata
cat >/etc/default/keyboard <<'KBEOF'
XKBMODEL="pc105"
XKBLAYOUT="us"
XKBVARIANT=""
XKBOPTIONS=""

KBEOF
   dpkg-reconfigure -f noninteractive keyboard-configuration
fi

終了処理

rm -f /boot/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt
exit 0

firstrun.sh 自身の削除,cmdline.txt からの呼び出し部分 system.run を削除して,初回起動時のみこの処理が行われるようにする.

exit 0 で処理が正常終了すると cmdline.txt の systemd.run_success_action=reboot により再起動が実行される.

おわりに

更に詳しい実装を知りたい人は rpi-imager や imager_custom のソースを読んでどうぞ.

GitHub - raspberrypi/rpi-imager: The home of Raspberry Pi Imager, a user-friendly tool for creating bootable media for Raspberry Pi devices.
The home of Raspberry Pi Imager, a user-friendly tool for creating bootable media for Raspberry Pi devices. - raspberryp...

コメント

タイトルとURLをコピーしました