Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nmcclain committed Aug 27, 2019
1 parent e23c7b5 commit 793817d
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 0 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# raspberian-firstboot
A lightly modified Raspbian-light image with first boot customization.

Our goal is **easy** boot time customization of **the** standard Raspberry Pi OS - just edit a simple bash script.

**Download the image from the [Releases tab](../../releases).**

# Why would I use this image?
The standard [Raspbian-lite](https://www.raspberrypi.org/downloads/raspbian/)
image allows you to customize the wireless settings and enable SSHd before flashing it to an SD card. Unfortunately, there is no way to further customize the OS during the first boot, nothing like cloud-init or userdata. Without a display and keyboard, complex "headless" deployments are impossible.

With this image, you can run a custom script on first boot and:
* Set a unique hostname. ([simple example](examples/simple_hostname/))
* Configure HDMI, audio, and network settings. ([example](examples/audio/))
* Install apt software packages. ([example](examples/apt_packages/))
* Setup an automatic reverse SSH tunnel for remote management. ([example](examples/reverse_ssh_tunnel/))
* Bootstrap a configuration management tool like Ansible/Chef/Puppet to prevent configuration drift.
* Deploy your IoT fleet with custom UUIDs and configurations.

# Quick Start
1. Download the latest image from the [Releases tab](../../releases). Note we currenlty only support the raspbian-lite image.
1. Mount the `/boot` volume on Windows, MacOS, or Linux (usually this happens automatically if you "open" the image).
1. NOTE: references below to /mnt will be /Volumes on macos and under "My Computer" on Windows.
1. Create a `/mnt/boot/firstboot.sh` script with your custom contents [examples here](examples/).
1. Optionally add additional custom configuration files or small binaries to /mnt/boot (the /boot partiton is small - keep your total additions under ~160MB).
1. Remember you can also add a [/mnt/boot/wpa_supplicant.conf](https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md) file for wifi configuration.
1. Unmount the `/boot` volume: `umount /mnt` on linux, `diskutil unmount /Volumes/boot` on macos, right-click for Windows.
1. Flash the customized image to your SD card.
1. Boot your Pi... `/boot/firstboot.sh` will be executed and renamed to `/boot/firstboot.sh.done`.

# What changes were made to the standard Raspbian-lite image?
1. [firstboot.service](linux_mount_pi_img/firstboot.service) is installed in `/lib/systemd/system/firstboot.service`
1. firstboot.service is enabled: `cd /etc/systemd/system/multi-user.target.wants && ln -s /lib/systemd/system/firstboot.service .`
1. Nothing else!

# How to reproduce this image yourself
1. This requires modifying the second partition of the Raspbian image, which requires Linux for ext4 support.
1. Source image is obtained from the official Raspberry Pi [download page](https://www.raspberrypi.org/downloads/raspbian/).
1. Be sure to verify the SHA hash!
1. Mount the second partition of the source image - the `mount` command will require an `--offset` flag, [as described here](https://raspberrypi.stackexchange.com/questions/13137/how-can-i-mount-a-raspberry-pi-linux-distro-image).
1. Install [firstboot.service](linux_mount_pi_img/firstboot.service) in `/mnt/lib/systemd/system/firstboot.service`
1. Enable firstboot.service for systemd: `cd /mnt/etc/systemd/system/multi-user.target.wants && ln -s /lib/systemd/system/firstboot.service .`
1. Unmount the second partition of the source image.
1. Carefully test & validate the image before distributing!

7 changes: 7 additions & 0 deletions examples/apt_packages/firstboot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# update apt cache
apt-get update

# install git client and streamer, a camera capture tool
apt-get install -y git streamer
13 changes: 13 additions & 0 deletions examples/audio/firstboot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

# set audio output to "auto"
#amixer cset numid=3 0
# force audio output to headphone jack
#amixer cset numid=3 1
# force audio output to hdmi
amixer cset numid=3 2

play -n synth .1 sin 1880
play -n synth 1.5 sin 880
play -n synth .5 sin 1280

26 changes: 26 additions & 0 deletions examples/reverse_ssh_tunnel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# raspberian-firstboot

## Setup an automatic reverse SSH tunnel for remote management
1. Edit `firstboot/sshtun.service`:
1. Confirm/update port for management server local port (example uses port `2223`).
1. Update `USER@MANAGEMENTHOST` for your internet-accessible management server.
1. Copy USER's private key to `firstboot` directory.
1. Mount your image (works in Windows, MacOS, and Linux).
1. Copy `firstboot.sh` and `firstboot` directory to `/boot` partition (`/mnt/boot`, `/Volumes/boot`, etc.).
1. Unmount your image, burn it to SD, and test:
1. On your management server, run `netstat -nl | grep 2223`
1. It may take a few minutes for your tunnel to come up the first time.
1. Once the tunnel is listening, connect to your Pi: `ssh pi@localhost -p2223`

## Troubleshoot
1. Connect a display & keyboard to your Pi.
1. Confirm/debug wifi connectivity.
1. Ensure it's possible to SSH to your mangement host using the same user/key set in `sshtun.service`
1. Confirm `firstboot.service` was successful:
1. `/boot/firstboot.sh` was renamed to `/boot/firstboot.sh.done`
1. Look for issues in `/var/log/`
1. Confirm `sshtun.service` was setup successfully:
1. Confirm `sshtun.service` was installed in `/etc/systemd/system/sshtun.service`
1. Confirm `sshtun.service` is running.
1. Look for an `ssh` process that matches your `sshtun.service` config with `ps -elf | grep ssh`
1. Look for issues in `/var/log/`
21 changes: 21 additions & 0 deletions examples/reverse_ssh_tunnel/firstboot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

# disable ipv6
echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
sysctl -p

# enable/start ssh
systemctl enable ssh
systemctl start ssh

# install private ssh key
mkdir /root/.ssh/
chmod 700 /root/.ssh/
cp /boot/firstboot/id_rsa /root/.ssh/id_rsa
chmod 600 /root/.ssh/id_rsa

# install/enable/start sshtun service
cp /boot/firstboot/sshtun.service /etc/systemd/system/sshtun.service
systemctl enable sshtun
systemctl start sshtun

11 changes: 11 additions & 0 deletions examples/reverse_ssh_tunnel/firstboot/sshtun.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Unit]
Description=sshtun
After=network.target

[Service]
ExecStart=/usr/bin/ssh -NTC -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no -i /root/.ssh/id_rsa -R 2223:localhost:22 USER@MANAGEMENTHOST
RestartSec=5
Restart=always

[Install]
WantedBy=multi-user.target
6 changes: 6 additions & 0 deletions examples/simple_hostname/firstboot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

NEW_NAME="mypi"
echo $NEW_NAME > /etc/hostname
sed -i "s/raspberrypi/$NEW_NAME/g" /etc/hosts
hostname $NEW_NAME
14 changes: 14 additions & 0 deletions firstboot.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=FirstBoot
After=network.target
Before=rc-local.service
ConditionFileNotEmpty=/boot/firstboot.sh

[Service]
ExecStart=/boot/firstboot.sh
ExecStartPost=/bin/mv /boot/firstboot.sh /boot/firstboot.sh.done
Type=oneshot
RemainAfterExit=no

[Install]
WantedBy=multi-user.target

0 comments on commit 793817d

Please sign in to comment.