Headless Raspberry Pi Configuration

How to configure Debian/arm using Ubuntu/intel


Configuring a Raspberry Pi for headless operation without having to connect a keyboard and a screen, involves the following step

  • Download the latest Raspbian image
  • copy the Raspbian image to sdcard
  • setup Wifi
  • change pasword for pi
  • enable sshd
  • adduser for me,
  • modify group
  • add me to sudo
  • add athorized ssh keys for me on my laptop so i can login without password
  • set timezone
  • set hostname
  • set keyboard
  • run raspi-config to configure various other stuff

Dependent on what it will be used for,  there is still more to do, quite a few of the above steps needs to be done on the sdcard before you can access the Raspberry Pi remotely.

I have automated this proces by writing a small shell-script rpido

rpido -?
usage: rpido <options> cmd
 -h name   sets /etc/hostname on rpi
 -s	start shell on raspian
 -w	write raspian to sdcard
 cmd    chroot rpi cmd 
installs files from template
configures wifi and sshd, and authorized keys

which takes care of the bulk of this.

To create a configured sdcard for a new RPI, I would typically do

  • install a sd card on my laptop
  • run: rpido -w -h newtoy -s
  • check things are fine, exit
  • move the sdcard to the RPI

This will download and install the latest raspbian to the sdcard, configure all the things mentioned above and set the hostname to newtoy, and finally drop you into a root-shell on the sdcard, where you could run raspi-config.

Yes I can execute arm-binaries on my ubuntu laptop, in a chroot environment on the filesystem on sdcard, check my shell-script below for how it is done.

#!/bin/bash
# 
URL=$(curl -s https://downloads.raspberrypi.org/raspbian_lite_latest | awk -F\" '/http/ { print $2}')
LATEST=$(basename $URL .zip)
zipfile=DIST/$LATEST.zip
RPI_ROOT=sdcard
VERBOSE=1

 vecho() { [ $VERBOSE -lt 1 ] || echo $* >/dev/stderr; }
vvecho() { [ $VERBOSE -lt 2 ] || echo $* >/dev/stderr; }
SUDO() {
        vecho SUDO $*
        sudo $*
}
set_SD() {
        for d in /dev/sd[a-f] ;do
                if sudo gdisk -l $d 2>/dev/null| grep "^Model:.*SD/MMC">/dev/null; then
                        SD=$(basename $d)
                        break;
                fi
        done
}
loopdev_SD() {
        [ -f "$LATEST.img" ] || unzip -x $zipfile $LATEST.img
        LOOP=$(sudo losetup -f)
        SUDO losetup -P $LOOP $LATEST.img
        SD=$(basename ${LOOP}p)
}
is_mounted() {
        mount | grep $(realpath $1) >/dev/null
}
unmount_SD() {
        for i in /sys/block/${SD}/${SD}?;do
                SUDO umount /dev/$(basename $i) 2>/dev/null
        done
}
mount_SD() {
        unmount_SD
        mkdir -p $RPI_ROOT
        SUDO mount /dev/${SD}2 $RPI_ROOT
        SUDO mount /dev/${SD}1 $RPI_ROOT/boot
}
write_SD() {
        unmount_SD
        unzip -p $zipfile $LATEST.img | sudo dd of=/dev/${SD} bs=4M
}
unmount_all() {
        [ ! -z "$RPI_ROOT" ] || return 1
        [ -z "$keep_mount" ] || return 0
        FULLPATH=$(realpath ${RPI_ROOT})
        LOOP=$(mount | grep "/dev/loop[0-9]*p2.*$FULLPATH" | sed 's/p2.*$//')
        # SUDO rm -f ${RPI_ROOT}/usr/bin/qemu-arm-static 
        for p in $(mount | grep $FULLPATH | cut -f3 -d' ' | sort -Vr); do
                SUDO umount $p
        done
        for d in $LOOP; do
                SUDO losetup -d $d
                #SUDO rm -f ${d}*
        done
}
usage() {
        unmount_all
        set +x
        [ $# == 0 ] || echo $*
        echo "usage: rpido <options> cmd"
        echo " -w       write raspian to sdcard"
        echo " -h name  sets /etc/hostname on rpi"
        echo " -s       start shell on raspian"
        echo " cmd      chroot rpi cmd"
        echo "installs files from template"
        echo "configures wifi and sshd, and authorized keys"
        exit
}

while getopts ?h:kqsvw opt;do
        case $opt in
        h) hostname=$OPTARG ;;
        k) keep_mount=y ;;
        q) VERBOSE=0 ;;
        s) rpi_shell=y ;;
        v) VERBOSE=$(($VERBOSE+1)) ;;
        w) write_sd=y ;;
        *) usage ;;
        esac
done
shift $(($OPTIND-1))
[ $VERBOSE -lt 2 ] || set -x
if [ "$rpi_shell" = y ]; then
        CMD="bash -i"
else
        CMD="$*"
fi
[ -f $zipfile ] || curl --create-dirs -o $zipfile $URL
set_SD
if [ -z "$SD" ];then
        loopdev_SD
else
        [ -z "$write_sd" ] || write_SD
fi
mount_SD
if [ -z "${RPI_ROOT}" -o ! -f "$RPI_ROOT/etc/rpi-issue" -o ! -f "$RPI_ROOT/boot/issue.txt" ]; then
        usage raspbian root not as expected
fi
SUDO rsync -a template/. $RPI_ROOT
[ -z "$hostname" ] || echo $hostname | sudo tee $RPI_ROOT/etc/hostname >/dev/null

if [ ! -z "$CMD" ]; then
        SUDO rsync /usr/bin/qemu-arm-static ${RPI_ROOT}/usr/bin/
        for f in proc dev sys;do
                is_mounted $RPI_ROOT/$f || SUDO mount --bind /$f $RPI_ROOT/$f
        done
        SUDO chroot ${RPI_ROOT} $CMD
fi

unmount_all
sync

The current content of template is:

$ find template/
template/
template/etc
template/etc/default
template/etc/default/keyboard
template/etc/shadow
template/etc/wpa_supplicant
template/etc/wpa_supplicant/wpa_supplicant.conf
template/etc/passwd
template/etc/timezone
template/etc/gshadow
template/etc/group
template/etc/sudoers.d
template/etc/sudoers.d/010_peter-nopasswd
template/home
template/home/peter
template/home/peter/.bashrc
template/home/peter/.ssh
template/home/peter/.ssh/authorized_keys
template/boot
template/boot/ssh

You probably have to install some tools on your ubuntu first, I would suggest:

sudo apt-get install gdisk realpath qemu-user-static zip rsync xz-utils file curl

Features:

  • rpido always checks for the latest version of Raspian lite. It is much more effcient to download a new up-to-date version than to try to upgrade an earlier version at hand.
  • if there is no sd-card installed, rpido will use a file-based loop mounted filesystem, much faster for debugging and trying out stuff,
  • -k is a debugging option wich will leave the filesystem mounted, hence you can easily dive into it again with a simple chroot
  • Even though the script is not running as root, there is a lot of sudo which can really do a lot of harm, so be sure to do a backup before, for me it only takes one command
    sudo zfs snapshot -r t470@20181027
    
    # and to restore f.ex. the root filesystem to this snapshot
    
    sudo zfs rollback t470/ROOT/ubuntu-18.04-t470@20181027

If you are still wondering how I manage to run arm-binaries on an intel laptop the secret-sauce is line 108-112 in the above script

And finally I would recommend that the bash prompt shows whether you are root by color, like this little example of rpido use demonstrates:

peter@t470:~> ./rpido -s
SUDO losetup -P /dev/loop14 sdcard
SUDO mount /dev/loop14p2 sdcard
SUDO mount /dev/loop14p1 sdcard/boot
SUDO rsync -a template/. adcard
SUDO rsync /usr/bin/qemu-arm-static sdcard/usr/bin/
SUDO mount –bind /proc sdcard/proc
SUDO mount –bind /dev sdcard/dev
SUDO mount –bind /sys sdcard/sys
SUDO chroot sdcard bash -i
root@raspberrypi:/# cat ~peter/.bashrc
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~peter/bin
RED=$(tput setaf 1)
BLUE=$(tput setaf 4)
NORMAL=$(tput sgr0)
HOSTNAME=$(cat /etc/hostname)
if [ $(id -u) -eq 0 ]; then # you are root, make the prompt red
+++ PS1=”\[${RED}\]\u@${HOSTNAME}:\W#\[${NORMAL}\] ”
else
+++ PS1=”\[${BLUE}\]\u@${HOSTNAME}:\W>\[${NORMAL}\] ”
fi
root@raspberrypi:/# exit
SUDO umount sdcard/sys
SUDO umount adcard/proc
SUDO umount adcard/dev
SUDO umount sdcard/boot
SUDO umount sdcard
SUDO losetup -d /dev/loop14
peter@t470:~>

This entry was posted in Linux, on Hackaday, Raspberry Pi. Bookmark the permalink.