From e8a2c6624ec1983a06efb01ac10b7973a12b8de2 Mon Sep 17 00:00:00 2001 From: Damian Mee Date: Mon, 25 May 2020 18:15:05 +0800 Subject: [PATCH] Added ability to run record firstboot scripts with script(1) --- Dockerfile | 4 +-- README.md | 14 ++++++-- examples/firstboot-script.tor.sh | 55 ++++++++++++++++++++++++++++++++ firstboot-script.service | 14 ++++++++ firstboot.service | 2 +- modify-image.sh | 36 +++++++++++---------- 6 files changed, 104 insertions(+), 21 deletions(-) create mode 100755 examples/firstboot-script.tor.sh create mode 100644 firstboot-script.service diff --git a/Dockerfile b/Dockerfile index b57d700..4229150 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,8 +12,8 @@ RUN apt-get update && \ ADD modify-image.sh /usr/local/bin/modify-image RUN chmod +x /usr/local/bin/modify-image -RUN mkdir -p /data/ /mnt/raspbian/ -ADD firstboot.service /data/ +RUN mkdir -p /mnt/raspbian/ /data/ +ADD firstboot.service firstboot-script.service /data/ VOLUME /raspbian/ WORKDIR /raspbian/ diff --git a/README.md b/README.md index d0c07e7..85a95de 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ Raspbian ========= -Literally just pure Raspbian Lite, but with added the ability to run a script on the first boot by putting it onto `/boot/` as `firstboot.sh`. +Literally just pure Raspbian Lite, but with added the ability to run a script on the first boot by putting it onto `/boot/` as either: + +* `/boot/firstboot.sh` - Run provided script directly +* `/boot/firstboot-script.sh` - Run via [`script(1)`][script] for complete session recording, that can be later played back using [`scriptreplay(1)`][replay] + +[script]: http://man7.org/linux/man-pages/man1/script.1.html +[replay]: http://man7.org/linux/man-pages/man1/scriptreplay.1.html Repo is inspired by https://github.com/nmcclain/raspberian-firstboot, but has been automated, Dockerized, and fully scripted. @@ -49,6 +55,10 @@ If you're on a Linux box, you can (after cloning this repo) run: You can also completely ignore all contents of this repo, download Raspbian Lite, and (assuming you have the ability to mount `ext4` on your OS): +> **NOTE: For `firstboot-script.service` see [here].** + +[here]: /firstboot-script.service + 1. Mount second partition 1. Install the service, by creating `$MOUNT_PATH/etc/systemd/system/firstboot.service` file, with the following contents: ```unit file (systemd) @@ -59,9 +69,9 @@ You can also completely ignore all contents of this repo, download Raspbian Lite ConditionFileNotEmpty=/boot/firstboot.sh [Service] + Type=oneshot ExecStart=/boot/firstboot.sh ExecStartPost=/bin/mv /boot/firstboot.sh /boot/firstboot.sh.done - Type=oneshot RemainAfterExit=no [Install] diff --git a/examples/firstboot-script.tor.sh b/examples/firstboot-script.tor.sh new file mode 100755 index 0000000..2f16c9c --- /dev/null +++ b/examples/firstboot-script.tor.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env sh + +## This is an example script that: +# 1. Installs Tor +# 2. Sets up a stealth Tor Hidden Service for ssh access +# 3. Copies `hostname` to `/boot/` (necessary to access) +# 4. Halts RBP when done (unless `/boot/nohalt` exists) + +set -e + +RET_COUNT=20 + +retry() { + delay=${RET_DELAY:-10} # seconds + count=${RET_COUNT:-3} + + _s=s + until $*; do + >&2 printf "'%s' failed (exit=%s)" "$1" "$?" + if [ $((count-=1)) = 0 ]; then + >&2 printf "\n" + return 1 + fi + + [ "$count" = 1 ] && _s= + >&2 printf ", retry in %ss (%s more time%s)…\n\n" "$delay" "$count" "$_s" + sleep "$delay" + done +} + +do_tor() { + retry apt-get install -y tor + + retry test -f /etc/tor/torrc || exit 1 + + cat << EOF >> /etc/tor/torrc +HiddenServiceDir /var/lib/tor/ssh/ +HiddenServiceVersion 2 +HiddenServicePort 22 127.0.0.1:22 +HiddenServiceAuthorizeClient stealth ssh +EOF + + retry systemctl restart tor + retry systemctl restart tor@default + + RET_COUNT=10 retry cp /var/lib/tor/ssh/hostname /boot/ +} + +retry apt-get update + +do_tor + +RET_COUNT=100 RET_DELAY=5 retry test -f /boot/nohalt || halt + +exit 0 diff --git a/firstboot-script.service b/firstboot-script.service new file mode 100644 index 0000000..8685cba --- /dev/null +++ b/firstboot-script.service @@ -0,0 +1,14 @@ +[Unit] +Description=FirstBoot +After=network.target +Before=rc-local.service +ConditionFileNotEmpty=/boot/firstboot-script.sh + +[Service] +Type=oneshot +ExecStart=/usr/bin/script --command=/boot/firstboot-script.sh --return --flush --timing=/boot/firstboot-script-log.tm /boot/firstboot-script-log.out +ExecStartPost=/bin/mv /boot/firstboot-script.sh /boot/firstboot-script.sh.done +RemainAfterExit=no + +[Install] +WantedBy=multi-user.target diff --git a/firstboot.service b/firstboot.service index 551b12c..01e813a 100644 --- a/firstboot.service +++ b/firstboot.service @@ -5,9 +5,9 @@ Before=rc-local.service ConditionFileNotEmpty=/boot/firstboot.sh [Service] +Type=oneshot ExecStart=/boot/firstboot.sh ExecStartPost=/bin/mv /boot/firstboot.sh /boot/firstboot.sh.done -Type=oneshot RemainAfterExit=no [Install] diff --git a/modify-image.sh b/modify-image.sh index 71b0ed9..5aaea65 100755 --- a/modify-image.sh +++ b/modify-image.sh @@ -198,22 +198,26 @@ log_ok os_path=etc/systemd/system - service_file=firstboot.service - service_src="$(pwd)/$service_file" - - # Change $service_src, when running in Docker - [ -f "/data/$service_file" ] && service_src="/data/$service_file" - - log "Installing service" "$service_file" - cp "$service_src" "$mount_dir/$os_path/" - log_ok "Installed at /$os_path/$service_file" - - # Another subshell to avoid cd (we (need (to (go (deeper(!)))))) - ( - cd "$mount_dir/$os_path/multi-user.target.wants/" - ln -s "/$os_path/$service_file" . - log_ok "Enabled as /$os_path/multi-user.target.wants/$service_file" - ) + + log "Installing services" + for service in firstboot firstboot-script; do + service_file="$service.service" + service_src="$(pwd)/$service_file" + + # Change $service_src, when running in Docker + [ -f "/data/$service_file" ] && service_src="/data/$service_file" + + cp "$service_src" "$mount_dir/$os_path/" + log_ok "$service installed at /$os_path/$service_file" + + # Another subshell to avoid cd (we (need (to (go (deeper(!)))))) + ( + cd "$mount_dir/$os_path/multi-user.target.wants/" + ln -s "/$os_path/$service_file" . + log_ok "$service enabled as /$os_path/multi-user.target.wants/$service_file" + ) + + done log "Unmounting" "$mount_dir"