version 1.2

New feature: auto-update apkovl file with latest master
gadget: improved setup if several ports available
wlan: only start wpa_supplicant if needed
updated doc
This commit is contained in:
macmpi 2023-11-20 09:00:41 +01:00
parent 4026274721
commit ad47f73731
7 changed files with 114 additions and 45 deletions

View File

@ -25,15 +25,18 @@ Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapp
- `interfaces`[^3] (*optional*): define network interfaces at will, if defaults DCHP-based are not suitable.
- `authorized_keys` (*optional*): provide client's public SSH key to secure `root` ssh login.
- `ssh_host_*_key*` (*optional*): provide server's custom ssh keys to be injected (may be stored), instead of using bundled ones[^2] (not stored). Providing an empty key file will trigger new keys generation (ssh server may take longer to start).
**Goody:** seamless USB-serial & USB-ethernet gadget mode (PiZero for instance):\
On supporting Pi devices, just add `dtoverlay=dwc2,dr_mode=peripheral` in `usercfg.txt` (or `config.txt`), and plug USB cable into host Computer port.\
Serial terminal can then be connected-to from host Computer (e.g. `cu -l ttyACM0` on Linux. xon/xoff flow control).\
Alternatively, with host Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from host with: `ssh root@10.42.0.2`.
- `auto-updt` (*optional*): allow apkovl file automatic update with latest from master branch: if contains *reboot* keyword all in one line, system will reboot after succesful update (unless ssh session is active or `unattended.sh` script is available).
Main execution steps are logged: `cat /var/log/messages | grep headless`.
## Goody:
Seamless USB-serial & USB-ethernet gadget mode (*e.g. PiZero*):
- Make sure dwc2/dwc3 driver is loaded acordingly, and device configuration is set to `peripheral` mode: this may be hardware (including cable) and/or software driven.\
(on supporting Pi devices, just add `dtoverlay=dwc2,dr_mode=peripheral` in `usercfg.txt` (or `config.txt`) to force by software)
- Plug USB cable into host Computer port before boot.\
Serial terminal can then be connected-to from host Computer (e.g. `cu -l ttyACM0` on Linux. xon/xoff flow control).\
Alternatively, with host Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from host with: `ssh root@10.42.0.2`.
[^1]: Initial boot fully preserves system's original state (config files & installed packages): a fresh system will therefore come-up as unconfigured.
[^2]: About bundled ssh keys: this overlay is meant to **quickly bootstrap** system in order to then proceed with proper install; therefore it purposely embeds [some ssh keys](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/overlay/tmp/.trash) so that bootstrapping is as fast as possible. Those temporary keys are moved in RAM /tmp: they will **not be stored/reused** once actual system install is performed (whether or not ssh server is installed in final setup).

Binary file not shown.

View File

@ -0,0 +1 @@
2dde95eb40fd626a7908ac33f41d47dbd46b0029d110ae8daaaed3575d1ada20e0b4f0268c040311159375b11eac48ce812c8f66993cb5e47c3078a6eb95a7b8 headless.apkovl.tar.gz

View File

@ -30,7 +30,8 @@ if [ -n "$build_path" ]; then
doas tar cv -C "$build_path"/overlay --no-recursion \
$(doas find "$build_path"/overlay/ | sed "s|$build_path/overlay/||" | sort | xargs ) | \
gzip -c9n > headless.apkovl.tar.gz
TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz
sha512sum headless.apkovl.tar.gz > headless.apkovl.tar.gz.sha512
TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz*
doas rm -rf "$build_path"
fi

View File

@ -3,7 +3,7 @@
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
# SPDX-License-Identifier: MIT
HDLSBSTRP_VERSION="1.1"
HDLSBSTRP_VERSION="1.2"
_apk() {
local cmd="$1"
@ -82,6 +82,7 @@ cat <<-EOF >> /tmp/.trash/headless_cleanup
grep -q "ttyGS0" /etc/securetty || echo "ttyGS0" >> /etc/securetty
/sbin/getty -L 115200 ttyGS0 vt100 &
fi
exit 0
EOF
chmod +x /tmp/.trash/headless_cleanup
}
@ -140,39 +141,78 @@ echo "$keygen_stance" >> /etc/conf.d/sshd
rc-service sshd restart
}
_updt_apkovl() {
## update apkovl overlay file & eventually reboot
# URL redirects to apkovl file on github master: is.gd shortener provides basic analytics.
# Analytics are public and can be checked at https://is.gd/stats.php?url=apkovl_master
# Privacy policy: https://is.gd/privacy.php
local file_url="https://is.gd/apkovl_master"
local sha_url="https://github.com/macmpi/alpine-linux-headless-bootstrap/raw/main/headless.apkovl.tar.gz.sha512"
local updt_status="failed, keeping original version"
_logger "Updating overlay file..."
# wget -q -O /tmp/sha -T 10 "$sha_url" > /dev/null 2>&1 && \
if wget -q -O /tmp/apkovl -T 10 "$file_url" > /dev/null 2>&1 && \
echo "36243ca58766232a1ff996de611724cee295109f981f3eb5d4b6df916c856036c002abd0cd58266602f3fcbf22777c1bfd1fb891888e864294b86dba366f6f2e toto" > /tmp/sha && \
[ "$( sha512sum /tmp/apkovl | awk '{print $1}' )" = "$( awk '{print $1}' /tmp/sha )" ]; then
_is_ro && mount -o remount,rw "${ovlpath}"
cp /tmp/apkovl "${ovl}"
_is_ro && mount -o remount,ro "${ovlpath}"
! [ "$( sha512sum "${ovl}" | awk '{print $1}' )" = "$( awk '{print $1}' /tmp/sha )" ] && \
_logger "Bad update: original apkovl file may be altered, please check!..." && return 1
updt_status="successful"
fi
rm -f /tmp/apkovl /tmp/sha
_logger "Update $updt_status"
[ "$updt_status" = "successful" ] || return 1
# reboot if specified in auto-updt file (and no ssh session ongoing nor unattended.sh script available)
! pgrep -a -P "$( cat /run/sshd.pid 2>/dev/null )" 2>/dev/null | grep -q "sshd: root@pts" && \
! [ -f "${ovlpath}"/unattended.sh ] && \
grep -q "^reboot$" "${ovlpath}"/auto-updt && \
_logger "Will reboot in 3sec..." && sleep 3 && reboot
exit 0
}
_tst_version() {
# Tested URL redirects to github project page: is.gd shortener provides basic analytics.
## Compare current version with latest online, notify & eventally calls for update
# URL redirects to github project page: is.gd shortener provides basic analytics.
# Analytics are public and can be checked at https://is.gd/stats.php?url=apkovl_run
# Privacy policy: https://is.gd/privacy.php
local new_vers=""
local status="failed"
local ref="/macmpi/alpine-linux-headless-bootstrap/releases/tag/v"
if wget -q -O /tmp/homepg -T 10 https://is.gd/apkovl_run > /dev/null 2>&1; then
status="success"
local url="https://is.gd/apkovl_run"
if wget -q -O /tmp/homepg -T 10 "$url" > /dev/null 2>&1; then
_logger "Internet access: success"
ver="$( grep -o "$ref.*\"" /tmp/homepg | grep -Eo '[0-9]+[\.[0-9]+]*' )"
rm -f /tmp/homepg
[ -n "$ver" ] && ! [ "$ver" = "$HDLSBSTRP_VERSION" ] && \
new_vers="!! Version $ver is available on Github project page !!" && \
_logger "$new_vers" && \
if [ -n "$ver" ] && ! [ "$ver" = "$HDLSBSTRP_VERSION" ]; then
new_vers="!! Version $ver is available on Github project page !!"
_logger "$new_vers"
printf '%s\n\n' "$new_vers" >> /tmp/.trash/banner
# Optionally update apkovl if key-file allows it
[ -f "${ovlpath}"/auto-updt ] && _updt_apkovl &
fi
else
_logger "Internet access: failed"
fi
_logger "Internet access: $status"
}
_setup_networking() {
## Setup Network interfaces
local has_wifi
local has_wifi wlan_lst
_has_wifi() { return "$has_wifi"; }
find /sys/class/ieee80211/*/device/net/* -maxdepth 0 -type d -exec basename {} \; > /tmp/.wlan_list 2>/dev/null
[ -s /tmp/.wlan_list ] && [ -f "${ovlpath}"/wpa_supplicant.conf ]
wlan_lst="$( find /sys/class/ieee80211/*/device/net/* -maxdepth 0 -type d -exec sh -c 'printf %s\| "${0##*/}"' {} \; 2>/dev/null )"
wlan_lst="${wlan_lst%\|}"
[ -n "$wlan_lst" ] && [ -f "${ovlpath}"/wpa_supplicant.conf ]
has_wifi=$?
if _has_wifi; then
_logger "Configuring wifi..."
_apk add wpa_supplicant
_preserve "/etc/wpa_supplicant/wpa_supplicant.conf"
install -m600 "${ovlpath}"/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf
rc-service wpa_supplicant restart
else
_logger "No wifi interface or SSID/pass file supplied"
fi
@ -202,24 +242,31 @@ if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces > /dev/null 2
EOF
;;
*)
_has_wifi && grep -q "$INTERFACE" /tmp/.wlan_list && \
# According to below we could rely on DEVTYPE for wlan devices
# https://lists.freedesktop.org/archives/systemd-devel/2014-January/015999.html
# but...some wlan might still be ill-behaved: use wlan_lst
# shellcheck disable=SC2169 # ash does support string replacement
_has_wifi && ! [ "${wlan_lst/$INTERFACE/}" = "$wlan_lst" ] && \
cat <<-EOF >> /etc/network/interfaces
auto $INTERFACE
iface $INTERFACE inet dhcp
EOF
# ensure considered gadget interface is actually connected over USB (may have several)
[ "$DEVTYPE" = "gadget" ] && \
cat <<-EOF >> /etc/network/interfaces && cat <<-EOF > /etc/resolv.conf
auto $INTERFACE
iface $INTERFACE inet static
address 10.42.0.2/24
gateway 10.42.0.1
! [ "$( find -L /sys/class/udc/*/device/gadget*/net/"$INTERFACE" -maxdepth 0 -exec \
sh -c 'cat "${0%/device*}"/current_speed' {} \; )" = "UNKNOWN" ] && \
cat <<-EOF >> /etc/network/interfaces && cat <<-EOF > /etc/resolv.conf
auto $INTERFACE
iface $INTERFACE inet static
address 10.42.0.2/24
gateway 10.42.0.1
EOF
nameserver 208.67.222.222
nameserver 208.67.220.220
EOF
nameserver 208.67.222.222
nameserver 208.67.220.220
EOF
EOF
;;
esac
done
@ -234,20 +281,22 @@ _preserve "/etc/hostname"
echo "alpine-headless" > /etc/hostname
hostname -F /etc/hostname
_has_wifi && grep -qE "$wlan_lst" /etc/network/interfaces && \
rc-service wpa_supplicant restart
rc-service networking restart
rm -f /tmp/.wlan_list
}
_setup_gadget() {
## load composite USB Serial/USB Ethernel driver & setup terminal
_logger "Enabling USB-gadget Serial and Ethernet ports"
lsmod | grep -q "dwc2" || modprobe -qs dwc2
# remove conflicting modules in case they were initially loaded (cmdline.txt)
modprobe -rq g_serial g_ether g_cdc
modprobe -q g_cdc && sleep 1
# once driver has settled check if cable is connected: unload if not
[ "$( cat "$udc_gadget"/current_speed )" = "UNKNOWN" ] && \
_logger "USB cable not connected !!" && modprobe -rq g_cdc && return 1
# once driver has settled check if any gadget port is connected over USB: unload driver if none
[ "$( find -L /sys/class/udc/*/current_speed -print0 | \
xargs -0 cat | \
grep -cv "UNKNOWN" )" = "0" ] && \
_logger "no USB cable connected !!" && modprobe -rq g_cdc && return 1
# default serial config: xon/xoff flow control
stty -g -F /dev/ttyGS0 >/dev/null 2>&1
@ -272,10 +321,12 @@ _logger "Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi"
# help randomness for wpa_supplicant and sshd (urandom until 3.16)
rc-service seedrng restart || rc-service urandom restart
# setup USB gadget mode if such device mode is enabled
udc_gadget="$( dirname "$( find -L /sys/class/udc/* -maxdepth 2 -type f -name "is_a_peripheral" 2>/dev/null)" )"
[ "$( cat "$udc_gadget"/is_a_peripheral 2>/dev/null )" = "0" ] && \
_setup_gadget
# setup USB gadget ports if some ports are enabled in peripheral mode
# Note: we assume dwc2/dwc3 is pre-loaded, we just check mode
[ "$( find -L /sys/class/udc/*/is_a_peripheral -print0 2>/dev/null | \
xargs -0 cat 2>/dev/null | \
grep -c "0" )" -ge "1" ] && \
_setup_gadget
# Determine ovl file location
# grab used ovl filename from dmesg
@ -291,8 +342,10 @@ fi
# Create banner file
warn=""
grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$?
[ "$RO" -eq "0" ] && warn="(remount partition rw!)"
grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$?
_is_ro() { return "$is_ro"; }
_is_ro && warn="(remount partition rw!)"
cat <<-EOF > /tmp/.trash/banner
Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi

10
sample_auto-updt Normal file
View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
# SPDX-License-Identifier: MIT
# Automated reboot after update is cancelled if:
# - there is an opened ssh session
# - unattended.sh script is provided
# Uncomment line below to enable reboot after update
#reboot

View File

@ -37,10 +37,11 @@ else
fi
# also works in case volume is mounted read-only
grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$?
[ "$RO" -eq "0" ] && mount -o remount,rw "${ovlpath}"
grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$?
_is_ro() { return "$is_ro"; }
_is_ro && mount -o remount,rw "${ovlpath}"
rm -f "${ovl}"
[ "$RO" -eq "0" ] && mount -o remount,ro "${ovlpath}"
_is_ro && mount -o remount,ro "${ovlpath}"
########################################################