From 793817d531933594c5601289bd2ca497de8fb2de Mon Sep 17 00:00:00 2001 From: ned Date: Mon, 26 Aug 2019 19:26:24 -0600 Subject: [PATCH] Initial commit --- README.md | 45 +++++++++++++++++++ examples/apt_packages/firstboot.sh | 7 +++ examples/audio/firstboot.sh | 13 ++++++ examples/reverse_ssh_tunnel/README.md | 26 +++++++++++ examples/reverse_ssh_tunnel/firstboot.sh | 21 +++++++++ .../firstboot/sshtun.service | 11 +++++ examples/simple_hostname/firstboot.sh | 6 +++ firstboot.service | 14 ++++++ 8 files changed, 143 insertions(+) create mode 100644 README.md create mode 100755 examples/apt_packages/firstboot.sh create mode 100755 examples/audio/firstboot.sh create mode 100644 examples/reverse_ssh_tunnel/README.md create mode 100755 examples/reverse_ssh_tunnel/firstboot.sh create mode 100644 examples/reverse_ssh_tunnel/firstboot/sshtun.service create mode 100755 examples/simple_hostname/firstboot.sh create mode 100644 firstboot.service diff --git a/README.md b/README.md new file mode 100644 index 0000000..174ec94 --- /dev/null +++ b/README.md @@ -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! + diff --git a/examples/apt_packages/firstboot.sh b/examples/apt_packages/firstboot.sh new file mode 100755 index 0000000..fe006f5 --- /dev/null +++ b/examples/apt_packages/firstboot.sh @@ -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 diff --git a/examples/audio/firstboot.sh b/examples/audio/firstboot.sh new file mode 100755 index 0000000..5cd535e --- /dev/null +++ b/examples/audio/firstboot.sh @@ -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 + diff --git a/examples/reverse_ssh_tunnel/README.md b/examples/reverse_ssh_tunnel/README.md new file mode 100644 index 0000000..27169ff --- /dev/null +++ b/examples/reverse_ssh_tunnel/README.md @@ -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/` diff --git a/examples/reverse_ssh_tunnel/firstboot.sh b/examples/reverse_ssh_tunnel/firstboot.sh new file mode 100755 index 0000000..5b9b377 --- /dev/null +++ b/examples/reverse_ssh_tunnel/firstboot.sh @@ -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 + diff --git a/examples/reverse_ssh_tunnel/firstboot/sshtun.service b/examples/reverse_ssh_tunnel/firstboot/sshtun.service new file mode 100644 index 0000000..a27d207 --- /dev/null +++ b/examples/reverse_ssh_tunnel/firstboot/sshtun.service @@ -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 diff --git a/examples/simple_hostname/firstboot.sh b/examples/simple_hostname/firstboot.sh new file mode 100755 index 0000000..21610f4 --- /dev/null +++ b/examples/simple_hostname/firstboot.sh @@ -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 diff --git a/firstboot.service b/firstboot.service new file mode 100644 index 0000000..551b12c --- /dev/null +++ b/firstboot.service @@ -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