Raspberry Pi USB Gadget Creator Update

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.