Image script: trixie arm64, NetworkManager wifi, any free loop device

- Download the 2026-09-15 Raspberry Pi OS trixie arm64 lite image.
- Trixie (and NetworkManager-based bookworm) ignore wpa_supplicant.conf on the boot
  partition: write a NetworkManager profile there instead, and set the regulatory
  domain on the kernel command line. The SSID is now asked for too.
- Use whichever loop device is free rather than a hard-coded /dev/loop42, mount into a
  temporary directory, and unmount and detach on any exit. Resolve paths from the
  script's own location, and refuse to overwrite an already customised image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 23:14:18 +02:00
parent f2f50e98e1
commit 6423c134be

View File

@@ -2,10 +2,12 @@
set -e # exit on error set -e # exit on error
TARGET_FOLDER="./rpi_image" SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
IMG_FILE_BASENAME="2023-12-11-raspios-bookworm-armhf-lite" TARGET_FOLDER="$SCRIPT_DIR/rpi_image"
#DOWNLOAD_URL="https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-11-08/${IMG_FILE_BASENAME}.zip" #IMG_FILE_BASENAME="2023-12-11-raspios-bookworm-armhf-lite"
DOWNLOAD_URL="https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2023-12-11/${IMG_FILE_BASENAME}.img.xz" #DOWNLOAD_URL="https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2023-12-11/${IMG_FILE_BASENAME}.img.xz"
IMG_FILE_BASENAME="2026-09-15-raspios-trixie-arm64-lite"
DOWNLOAD_URL="https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2026-09-15/${IMG_FILE_BASENAME}.img.xz"
echo "This script downloads raspian lite, and modifies the image to enable SSH and set hostname" echo "This script downloads raspian lite, and modifies the image to enable SSH and set hostname"
@@ -14,15 +16,20 @@ if [[ $EUID -ne 0 ]]; then
exit 1 exit 1
fi fi
echo -n Hostname of new pi
read -p "Hostname of new pi [newrpi]: " RPI_HOSTNAME read -p "Hostname of new pi [newrpi]: " RPI_HOSTNAME
read -p "WIFI SSID (only used if a password is given) [WLAN]: " WIFI_SSID
read -p "WIFI password (leave empty for non-wifi setup) [] " WIFI_PASSWORD read -p "WIFI password (leave empty for non-wifi setup) [] " WIFI_PASSWORD
RPI_HOSTNAME=${RPI_HOSTNAME:-newrpi} RPI_HOSTNAME=${RPI_HOSTNAME:-newrpi}
WIFI_SSID=${WIFI_SSID:-WLAN}
SCRIPT_DIR=`pwd` mkdir -p "$TARGET_FOLDER"
mkdir -p $TARGET_FOLDER cd "$TARGET_FOLDER"
cd $TARGET_FOLDER
if [ -e "${IMG_FILE_BASENAME}.img" ]; then
echo "${TARGET_FOLDER}/${IMG_FILE_BASENAME}.img already exists (already customised?)."
echo "Remove it first if you want to start over."
exit 1
fi
echo "Downloading image" echo "Downloading image"
wget ${DOWNLOAD_URL} wget ${DOWNLOAD_URL}
@@ -36,36 +43,74 @@ unxz ${IMG_FILE_BASENAME}.img.xz
echo "Mounting image" echo "Mounting image"
mkdir mounted_image MOUNT_DIR=$(mktemp -d "$TARGET_FOLDER/mounted_image.XXXXXX")
mkdir mounted_image/boot LOOP_DEV=""
mkdir mounted_image/system cleanup() {
losetup -P /dev/loop42 ${IMG_FILE_BASENAME}.img set +e
mount /dev/loop42p1 mounted_image/boot umount "$MOUNT_DIR/boot" "$MOUNT_DIR/system" 2>/dev/null
mount /dev/loop42p2 mounted_image/system [ -n "$LOOP_DEV" ] && losetup -d "$LOOP_DEV"
rmdir "$MOUNT_DIR/boot" "$MOUNT_DIR/system" "$MOUNT_DIR" 2>/dev/null
}
trap cleanup EXIT
mkdir "$MOUNT_DIR/boot" "$MOUNT_DIR/system"
LOOP_DEV=$(losetup -f --show -P ${IMG_FILE_BASENAME}.img)
mount ${LOOP_DEV}p1 "$MOUNT_DIR/boot" # firmware partition, /boot/firmware on the Pi
mount ${LOOP_DEV}p2 "$MOUNT_DIR/system"
echo "Enabling SSH and writing hostname" echo "Enabling SSH and writing hostname"
echo $RPI_HOSTNAME > mounted_image/system/etc/hostname echo $RPI_HOSTNAME > "$MOUNT_DIR/system/etc/hostname"
touch mounted_image/boot/ssh # startup ssh touch "$MOUNT_DIR/boot/ssh" # startup ssh
sed -i "/^#PermitRootLogin/ cPermitRootLogin prohibit-password" mounted_image/system/etc/ssh/sshd_config sed -i "/^#PermitRootLogin/ cPermitRootLogin prohibit-password" "$MOUNT_DIR/system/etc/ssh/sshd_config"
mkdir -p mounted_image/system/root/.ssh mkdir -p "$MOUNT_DIR/system/root/.ssh"
cat $SCRIPT_DIR/*.pub > mounted_image/system/root/.ssh/authorized_keys cat $SCRIPT_DIR/*.pub > "$MOUNT_DIR/system/root/.ssh/authorized_keys"
chmod 700 mounted_image/system/root/.ssh chmod 700 "$MOUNT_DIR/system/root/.ssh"
chmod 600 mounted_image/system/root/.ssh/authorized_keys chmod 600 "$MOUNT_DIR/system/root/.ssh/authorized_keys"
if [ -z "$WIFI_PASSWORD" ] if [ -z "$WIFI_PASSWORD" ]
then then
echo "Skipping WIFI setup" echo "Skipping WIFI setup"
elif [ -d "$MOUNT_DIR/system/etc/NetworkManager" ] && [ -e "$MOUNT_DIR/boot/user-data" ]
then
# Trixie (and bookworm images from 2023-10 on, when they use NetworkManager) ignore
# wpa_supplicant.conf on the boot partition; give NetworkManager a connection profile.
echo "Setting up initial wifi config (NetworkManager)"
NM_DIR="$MOUNT_DIR/system/etc/NetworkManager/system-connections"
mkdir -p "$NM_DIR"
/bin/cat <<EOM >"$NM_DIR/wifi.nmconnection"
[connection]
id=wifi
type=wifi
autoconnect=true
[wifi]
mode=infrastructure
ssid=${WIFI_SSID}
[wifi-security]
key-mgmt=wpa-psk
psk=${WIFI_PASSWORD}
[ipv4]
method=auto
[ipv6]
method=auto
EOM
chmod 600 "$NM_DIR/wifi.nmconnection" # NetworkManager ignores profiles readable by others
# regulatory domain, so the 5GHz channels and the right power limits are usable at boot
sed -i '1 s/$/ cfg80211.ieee80211_regdom=DE/' "$MOUNT_DIR/boot/cmdline.txt"
else else
echo "Setting up initial wifi config" echo "Setting up initial wifi config (wpa_supplicant)"
/bin/cat <<EOM >mounted_image/boot/wpa_supplicant.conf /bin/cat <<EOM >"$MOUNT_DIR/boot/wpa_supplicant.conf"
country=DE country=DE
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1 update_config=1
network={ network={
ssid="WLAN" ssid="${WIFI_SSID}"
psk="${WIFI_PASSWORD}" psk="${WIFI_PASSWORD}"
} }
EOM EOM
@@ -75,12 +120,8 @@ fi
echo "Unmounting image and other cleanup" echo "Unmounting image and other cleanup"
sleep 3 # to prevent "device is busy" sleep 3 # to prevent "device is busy"
umount /dev/loop42p1 cleanup
umount /dev/loop42p2 trap - EXIT
losetup -d /dev/loop42
rmdir mounted_image/boot
rmdir mounted_image/system
rmdir mounted_image
rm ${IMG_FILE_BASENAME}.img.xz.sha256 rm ${IMG_FILE_BASENAME}.img.xz.sha256
chmod a+rw ${IMG_FILE_BASENAME}.img . chmod a+rw ${IMG_FILE_BASENAME}.img .