A few years ago I wrote a script that would take a standard Raspberry Pi OS image and boot it up in a VM to make all the required modifications so a Pi Zero or Pi 4 would work as a USB Ethernet device connected to a host computer.
One of my uses for these images is an offline Certificate Authority that runs on a Raspberry Pi Zero (note not a Zero W so it’s totally offline), so that it is only powered on and accessible when plugged into one of the USB ports on my laptop. This has worked well, but recently I upgraded my laptop to Ubuntu 22.04 and this upgraded OpenSSL to 3.0.x which also include a bunch of security related changes to do with default cypher sets enabled and minimum key sizes it considers secure.
When I came to issue some new certificates for my VPN setup this cause some problems as the PKCS12 files generated by my offline CA could not be opened on the new laptop without adding a bunch of extra options to the commands to re-enable the obsolete configuration. To remedy this it was time to upgrade the CA image to the latest version of Raspberry Pi OS.
The script worked pretty well until Raspberry Pi OS stopped enabling a default user called pi
with the password raspberry
. This was a sensible change as many Raspberry Pi machines had ended up connected directly to the Internet without the password being changed.
Since there is no default user anymore you have supply a userconf.txt
file in the /boot
partition on the SDCard that is used to create the user on first boot. The problem is that the /boot
partition is bundled into the image so not easily accessible until the image is written to a card.
We can fix this using a loopback mount on Linux, we just need to know the offset into the image file that the partition starts at. We can find that by running fdisk -l
against the image:
$ fdisk -l 2022-09-22-raspios-bullseye-armhf-lite.img
Disk 2022-09-22-raspios-bullseye-armhf-lite.img: 1.75 GiB, 1874853888 bytes, 3661824 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xb1214a26
Device Boot Start End Sectors Size Id Type
2022-09-22-raspios-bullseye-armhf-lite.img1 8192 532479 524288 256M c W95 FAT32 (LBA)
2022-09-22-raspios-bullseye-armhf-lite.img2 532480 3661823 3129344 1.5G 83 Linux
We can see that the first partition is the one we want and the off set it at 8192
sectors (each sector is 512 bytes as shown in the header). Using awk
we can then extract and calculate the right value to pass to mount
$ OFFSET=$(fdisk -l $1 | awk '/^[^ ]*1/{ print $2*512 }')
$ sudo mount -o loop,offset=$OFFSET 2022-09-22-raspios-bullseye-armhf-lite.img boot
Now we have the partition mounted we can go about generating the userconf.txt
file. This is done using openssl
as described in the Raspberry Pi Org docs
$ openssl passwd -6
Password:
Verifying - Password:
$6$k6CgL8YerZDjNAeD$N0HnHZGUPufxjewErapVEalmZll/1KgPlD0ybBXubaAvp7CZEOZBw8FDFIQ2jIyUevanKvGHmmc33YyXaZnf./
The output is appended to username:
and placed in the /boot/userconf.txt
file, then we can unmount the partition and boot the system to install the required parts for the USB gadget.
I’ve put the whole thing in a shell script which also exports the password as an environment variable so it can be picked up by the expect
script that actually does the install.
#!/bin/bash
OFFSET=$(fdisk -l $1 | awk '/^[^ ]*1/{ print $2*512 }')
mkdir boot
sudo mount -o loop,offset=$OFFSET $1 boot
read -rsp "Please enter password for pi user: " PASSWORD
echo
PASS=$(echo $PASSWORD | openssl passwd -6 -stdin)
echo "pi:$PASS" > userconf.txt
sudo cp userconf.txt boot/userconf.txt
sudo sync
sudo umount boot
rmdir boot
export PASSWORD
./create-image $1
I’ve updated the git repo with the new script.