The Raspberry PI OS Lite ISO needs to be configured to add a username and password, as well as activate ssh to allow a headless PI to be setup over a network.
Is there a better way to do this?
This script does the following:
- Download a version of Raspberry PI OS Lite 64-bit
- Download the ISO’s sha256
- Verify the ISO
- Mount the ISO inside the qube
- Get a username and password from the user and create a config file with this info
- Enable ssh on first boot
- Dismount ISO
- Create’s a script to pull the iso from the qube to dom0 and write it to the sdcard
- Gives the user instructions on what to type in dom0’s terminal to pull the script over
- The pulled script pulls the iso from the qube to dom0
- The iso is written to the sdcard
- Cleanup
#!/bin/bash
set -e
RASPBIAN_OS_ISO_URL="https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-05-03/2023-05-03-raspios-bullseye-arm64-lite.img.xz"
RASPBIAN_OS_ISO_SHA256_URL="https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-05-03/2023-05-03-raspios-bullseye-arm64-lite.img.xz.sha256"
ISO_NAME="$(basename "$RASPBIAN_OS_ISO_URL")"
SHA_NAME="$(basename "$RASPBIAN_OS_ISO_SHA256_URL")"
ISO_UNCOMPRESSED_NAME="${ISO_NAME%%.*}.img"
BASE_DIR=$(pwd)
UTILS_DIR="myUtils"
REMOTE_SCRIPT_NAME="myTransfer.sh"
SCRIPT_SYMLINK="/tmp/z"
ISO_SYMLINK="/tmp/iso.iso"
SOURCE_QUBE="$(xenstore-read name)"
if [ "$EUID" -ne 0 ]
then echo "Please run using sudo"
exit
fi
function section_break() {
local title=$1
local line='================================================================================'
local sqiggle='<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>'
local line_length=${#line}
printf '\n'
printf '\n'
printf '%s\n' "$sqiggle"
if [[ -n $title ]]; then
local padding=$(( (line_length - ${#title}) / 2 ))
for ((i=1; i<=padding; i++)); do
printf ' '
done
printf '%s\n' "$title"
fi
printf '%s\n' "$line"
printf '\n'
}
function status() {
echo -e "-> $1"
}
function highlight() {
echo -e "\e[30;47m$1\e[0m"
}
section_break "Fetching the compressed ISO if need be, verifying and decompressing"
status "Downloading Raspbian OS Lite 64 bit if the file does not exist locally:"
aria2c --quiet=true --show-console-readout=true -x4 -s4 -c "$RASPBIAN_OS_ISO_URL"
status "Downloading SHA256 signature for Raspbian OS Lite 64 bit, regardless"
rm -f "$SHA_NAME"
aria2c --quiet=true --show-console-readout=true "$RASPBIAN_OS_ISO_SHA256_URL"
status "Check SHA256 sum of the compressed ISO file to its downloaded SHA256 signature"
sha256sum --check "$SHA_NAME"
# TODO check that the script fails if the ISO is altered.
status "Deleting the previous payload directory"
rm -rf "./$UTILS_DIR"
status "Decompressing compressed ISO file"
unxz --keep "./$ISO_NAME"
status "Creating and moving into a new directory named 'myUtils' to work in."
mkdir -p "./$UTILS_DIR"
cd "$UTILS_DIR"
UTILS_DIR_GLOBAL=$(pwd)
ISO_FULL_PATH="$UTILS_DIR_GLOBAL/$ISO_UNCOMPRESSED_NAME"
status "Moving iso to transfer directory"
mv "../$ISO_UNCOMPRESSED_NAME" .
section_break "Mounting ISO as part of the filesystem to allow modifications"
status "Creating loop device"
LOOP_DEVICE="$(losetup --show -f -P "$ISO_FULL_PATH")"
BOOT_PARTITION="${LOOP_DEVICE}p1"
status "Get the first partition (assumed to be the boot partition) of the loop device: $BOOT_PARTITION"
status "Wait for the loop device to be ready"
while ! [ -b "$BOOT_PARTITION" ]; do sleep 1; done
status "Mounting the boot partition"
mkdir -p /mnt/boot
mount "$BOOT_PARTITION" /mnt/boot
status "Get user information to prepare the Pi Files"
# Asking for username and password from user
highlight "Please enter a username for the Raspberry Pi:"
read USERNAME
highlight "Please enter a password for the Raspberry Pi:"
read -s PASSWORD
ENCRYPTED_PASSWORD=$(echo "$PASSWORD" | openssl passwd -6 -stdin)
status "Creating user configuration and SSH files in the boot partition"
echo "$USERNAME:$ENCRYPTED_PASSWORD" > /mnt/boot/userconf
touch /mnt/boot/ssh
status "Flushing file system buffers"
sync
status "Unmount the boot partition and delete the loop device"
umount /mnt/boot
UMOUNT_EXIT_STATUS=$?
# Give system some time to release resources
status "Waiting for kernel to dismount iso"
while fuser -m /mnt/boot > /dev/null 2>&1; do
sleep 1
done
# Diagnostic steps before trying to delete /mnt/boot
status "Trying to remove /mnt/boot..."
rm -rf /mnt/boot
RM_EXIT_STATUS=$?
if [[ $RM_EXIT_STATUS -ne 0 ]]; then
status "Removal operation failed. Running diagnostic commands..."
status "List all mount to /mnt/boot:"
mount | grep /mnt/boot
fi
status "Finished setup for headless boot with user '$USERNAME'and SSH enabled."
section_break "Transfer scripts and ISO's to dom0 to write the ISO to the sd card"
status "The ISO will now allow the Pi to be accessible via ssh on first boot using the username and password provided."
status "Creating a symlink to the ISO file that will be easier to get to later."
# Delete the symlink if it already exists
rm -f "$ISO_SYMLINK"
rm -f "$SCRIPT_SYMLINK"
SCRIPT_LOCAL_ABSOLUTE="$(pwd)/$REMOTE_SCRIPT_NAME"
status "Creating file '$REMOTE_SCRIPT_NAME' in $UTILS_DIR directory."
cat << 'SCRIPT' > "$REMOTE_SCRIPT_NAME"
#!/bin/bash
REMOTE_ISO_FILE="$ISO_SYMLINK"
LOCAL_ISO_FILE="payload.iso"
LOCAL_SCRIPT_FILE="iso_transfer.sh"
SOURCE_QUBE="$SOURCE_QUBE_PLACEHOLDER"
if [ "$EUID" -ne 0 ]
then echo "Please run using sudo"
exit
fi
function status() {
echo -e "-> $1"
}
function highlight() {
echo -e "\e[30;47m$1\e[0m"
}
status "Fetching ISO file from Qube $SOURCE_QUBE:"
qvm-run --pass-io $SOURCE_QUBE cat $REMOTE_ISO_FILE > $LOCAL_ISO_FILE
DEVICE_NAME=$(dmesg | awk '/mmcblk[0-9]/{gsub(/:$/, "", $2); print $2}'| tail -1 )
status "The device detected is: /dev/$DEVICE_NAME"
status "Is this the correct device?"
highlight "Enter 'y'/'n', or 'e' to edit the device name: "
read response
echo
if [[ "$response" == "n" ]]; then
echo "Please confirm the device and rerun the script."
exit 1
elif [[ "$response" == "e" ]]; then
echo -n "Enter the device name (e.g., mmcblk0): "
read DEVICE_NAME
fi
status "Writing the ISO to SD card using:"
status "dd bs=4M if=$LOCAL_ISO_FILE of=/dev/$DEVICE_NAME status=progress"
dd bs=4M if=$LOCAL_ISO_FILE of=/dev/$DEVICE_NAME status=progress
status "Flushing buffers"
sync
status "Image has been written to the SD card."
status "Deleting this file and the ISO file"
rm -f payload.iso
rm -f myTransfer.sh
highlight "Please remove your SD card. It is ready to boot your PI from"
status "This setup assumes you have a DHCP server running."
status "The easiest way to find the IP address of the PI is to check what IP your router assigned to it"
SCRIPT
chmod u+x "$REMOTE_SCRIPT_NAME"
status "Creating symlinks in /tmp to reduce typeing when transferring to dom0"
ln -s "$ISO_FULL_PATH" "$ISO_SYMLINK"
ln -s "$SCRIPT_LOCAL_ABSOLUTE" "$SCRIPT_SYMLINK"
status "Setting directories in remote script"
sed -i "s|\$ISO_SYMLINK|$ISO_SYMLINK|g" $REMOTE_SCRIPT_NAME
sed -i "s|\$SCRIPT_SYMLINK|$SCRIPT_SYMLINK|g" $REMOTE_SCRIPT_NAME
status "Setting source qube name in remote script"
sed -i "s|\$SOURCE_QUBE_PLACEHOLDER|$SOURCE_QUBE|g" $REMOTE_SCRIPT_NAME
section_break "Transfer the script and iso to dom0 with the user's assistance"
echo "In dom0 terminal, type:"
highlight "> qvm-run --pass-io $(xenstore-read name) 'cat $SCRIPT_SYMLINK' > $REMOTE_SCRIPT_NAME"
echo
highlight "Running this script blindly is a major security vulnerability if you do not understand every line in it."
echo "Open the file with an editor to check the contents. Make sure you understand what everyline does."
highlight "> vim $REMOTE_SCRIPT_NAME #vim basics: ':q' to quit ':q!' to quit without saving, 'i' to edit, '<esc>' to stop editing, ':w' to save"
echo "Make the script executable:"
highlight "> chmod u+x $REMOTE_SCRIPT_NAME"
echo "Then you can run the script:"
highlight "> sudo ./$REMOTE_SCRIPT_NAME"
echo
echo
echo "The script will copy the required ISO image automatically."
highlight "Press enter to continue when you've finished copying..."
read
status "Deleting the payload directory $BASE_DIR/$UTILS_DIR "
rm -rf "$BASE_DIR/$UTILS_DIR"