Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save MrAndrewMal/416e0f203fa5d8db9bda7a676bd6895a to your computer and use it in GitHub Desktop.
Save MrAndrewMal/416e0f203fa5d8db9bda7a676bd6895a to your computer and use it in GitHub Desktop.
Scripts to setup a Raspberry Pi OS image **before** flashing to SD card

Usage

  1. Download a Raspberry Pi OS image and unzip it:
    wget https://downloads.raspberrypi.org/raspios_lite_armhf_latest --trust-server-names --timestamping --quiet
    unzip 2020-05-27-raspios-buster-lite-armhf.zip
  2. Download gist files:
    wget https://gist.githubusercontent.com/cinderblock/20952a653989e55f8a7770a0ca2348a8/raw/{prepare,chroot,grow,setup}.sh --timestamping --quiet
    chmod +x {prepare,chroot,grow}.sh
  3. Edit setup.sh as desired. default adds my keys!
  4. Run scripts.

Available scripts

sudo ./prepare.sh 2020-05-27-raspios-buster-lite-armhf.img [setup.sh] [mount point]
sudo ./chroot.sh 2020-05-27-raspios-buster-lite-armhf.img [mount point]
sudo ./grow.sh 2020-05-27-raspios-buster-lite-armhf.img [megabytes]

The default setup.sh script will fail unless you grow the standard .img first (for 2020-02-13).

Prepare

Runs the setup.sh (or other) script in a chroot in specified image.

Chroot

Gives you a chroot in the image to change whatever manually.

Planned changed: Use systemd-nspawn instead of chroot.

Note: Bash history will be saved too. Subject to change.

Grow

Grow the image (and main partition) by some number of megabytes.

This only affects the local .img file. On first boot, Raspbian will automatically grow the parition to fill the full card.

Adding 100MB adds aproximately 10 seconds to the write time when transfering to an SD card.

See also

#!/bin/bash
set -e
if [[ "$(dpkg --print-architecture)" != "armhf" ]]; then
[ -x /usr/bin/qemu-arm-static ] || apt-get install qemu qemu-user-static binfmt-support
fi
trap 'echo Cleaning up...; cleanup' EXIT
IMG=$1
# Link a loopback device to the img file and get which one was used
LOOP=$(losetup -Pf ${IMG} --show)
function cleanup {
losetup -d ${LOOP}
}
# Allow using second argument to override mount point
MNT=${2:-/tmp/mount${LOOP}}
# Create the mount point and mount the image
mkdir -p ${MNT}
mount ${LOOP}p2 ${MNT}
mount ${LOOP}p1 ${MNT}/boot
function cleanup {
umount ${MNT}{/boot,}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
# Setup mounts needed for proper chroot
mount -o bind,ro {,${MNT}}/etc/resolv.conf
mount --bind {,${MNT}}/dev
mount --bind {,${MNT}}/dev/pts
mount --bind {,${MNT}}/sys
mount --bind {,${MNT}}/proc
function cleanup {
umount ${MNT}{/{boot,etc/resolv.conf,dev{/pts,},sys,proc},}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
if [[ ! -z "${QEMU}" ]]; then
# Make QEMU binary available in chroot
cp {,${MNT}}${QEMU}
function cleanup {
rm ${MNT}${QEMU}
umount ${MNT}{/{boot,etc/resolv.conf,dev{/pts,},sys,proc},}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
fi
# Prepare ld.preload for chroot
sed -i 's/^/#CHROOT /g' ${MNT}/etc/ld.so.preload
function cleanup {
sed -i 's/^#CHROOT //g' ${MNT}/etc/ld.so.preload
[[ ! -z "${QEMU}" ]] && rm ${MNT}${QEMU}
umount ${MNT}{/{boot,etc/resolv.conf,dev{/pts,},sys,proc},}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
echo Chrooting...
# Run the script in Chroot
chroot ${MNT} /bin/bash
#!/bin/bash
set -e
IMG=$1
# Default to 100MB
MEGA=${2:-100}
# Create a dummy partition to help defaults later
fdisk ${IMG} <<EOF > /dev/null
n
w
EOF
# Add zeros to the end of the img file
dd if=/dev/zero bs=1M count=${MEGA} >> ${IMG}
# Grow by deleting existing partition and creating a new one in the same place, but bigger.
fdisk ${IMG} <<EOF > /dev/null
d
2
n
p
d
w
EOF
# Setup loop device for image
LOOP=$(losetup -Pf ${IMG} --show)
trap 'echo Cleaning up...; cleanup' EXIT
function cleanup {
losetup -d ${LOOP}
}
# Check fs because we can
e2fsck -f ${LOOP}p2
# Grow the ext partition
resize2fs ${LOOP}p2
# Check fs again because we can
e2fsck -f ${LOOP}p2
#!/bin/bash
SETUP=${2:-setup.sh}
set -e
if [[ "$(dpkg --print-architecture)" != "armhf" ]]; then
QEMU=/usr/bin/qemu-arm-static
[ -x ${QEMU} ] || apt-get install qemu qemu-user-static binfmt-support
fi
trap 'echo Cleaning up...; cleanup' EXIT
IMG=$1
# Link a loopback device to the img file and get which one was used
LOOP=$(losetup -Pf ${IMG} --show)
function cleanup {
losetup -d ${LOOP}
}
# Allow using second argument to override mount point
MNT=${3:-/tmp/mount${LOOP}}
# Create the mount point and mount the image
mkdir -p ${MNT}
mount ${LOOP}p2 ${MNT}
mount ${LOOP}p1 ${MNT}/boot
function cleanup {
umount ${MNT}{/boot,}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
# Setup mounts needed for proper chroot
mount -o bind,ro {,${MNT}}/etc/resolv.conf
mount --bind {,${MNT}}/dev
mount --bind {,${MNT}}/dev/pts
mount --bind {,${MNT}}/sys
mount --bind {,${MNT}}/proc
function cleanup {
umount ${MNT}{/{boot,etc/resolv.conf,dev{/pts,},sys,proc},}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
if [[ ! -z "${QEMU}" ]]; then
# Make QEMU binary available in chroot
cp {,${MNT}}${QEMU}
function cleanup {
rm ${MNT}${QEMU}
umount ${MNT}{/{boot,etc/resolv.conf,dev{/pts,},sys,proc},}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
fi
# Prepare ld.preload for chroot
sed -i 's/^/#CHROOT /g' ${MNT}/etc/ld.so.preload
function cleanup {
sed -i 's/^#CHROOT //g' ${MNT}/etc/ld.so.preload
[[ ! -z "${QEMU}" ]] && rm ${MNT}${QEMU}
umount ${MNT}{/{boot,etc/resolv.conf,dev{/pts,},sys,proc},}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
# Copy setup script to image
cp ${SETUP} ${MNT}/setup.sh
chmod +x ${MNT}/setup.sh
function cleanup {
rm ${MNT}/setup.sh
sed -i 's/^#CHROOT //g' ${MNT}/etc/ld.so.preload
[[ ! -z "${QEMU}" ]] && rm ${MNT}${QEMU}
umount ${MNT}{/{boot,etc/resolv.conf,dev{/pts,},sys,proc},}
# rmdir -p ${MNT}
losetup -d ${LOOP}
}
echo Setting up...
# Run the script in Chroot
chroot ${MNT} /setup.sh

Setup Raspberry Pi OS Image Script

Scripts for modifying Raspberry Pi OS images before writing to SD card.

  • QEMU - setup on x86 (faster)
  • chroot
  • Setup script. Set:
    • hostname
    • WiFi
    • ssh keys
    • Localization
    • sshd
    • programs
  • Grow img
#!/bin/bash
# This is meant to be run on Raspberry Pi OS to setup the system the way you want.
# No variables to change. Just change what you want to change.
# For instance, the default gives *me* access to your system!
set -e
# Set locale to US
echo en_US.UTF-8 UTF-8 > /etc/locale.gen
locale-gen
update-locale LANG=en_US.UTF-8
sudo -u pi bash <<EOF_PI
set -e
# Initialize Authorized Keys
mkdir -p /home/pi/.ssh
curl -q https://github.com/cinderblock.keys > /home/pi/.ssh/authorized_keys
# Run other things as "pi" user
EOF_PI
# Enable SSHD, without passwords
systemctl enable ssh
echo PasswordAuthentication no >> /etc/ssh/sshd_config
# Add WiFi config
cat << EOF_WPA > /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US
network={
ssid="My SSID"
psk="My PSK"
}
EOF_WPA
# Set Hostname
sed -e s/raspberrypi/myHostname/ -i /etc/host{s,name}
# Set Timezone
ln -snf /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
# Update
apt-get update
apt-get upgrade -y
# Install Essentials
apt-get install vim screen git -y
# This is a nice colorful "status" line that shows which tab you're on in screen. You're welcome ;)
echo "caption always '%{= dg} %H %{G}| %{B}%l %{G}|%=%?%{d}%-w%?%{r}(%{d}%n %t%? {%u} %?%{r})%{d}%?%+w%?%=%{G}| %{B}%M %d %c:%s '" >> /etc/screenrc
# Install Node.js (needs and extra 50MB of space on the 2020-02-13 image first! Use "grow.sh")
curl -sL https://deb.nodesource.com/setup_12.x | bash -
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
apt-get install -y nodejs yarn
# Optional set passwd for user `pi`. This will prompt you!
# passwd pi
# Add other things you want...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment