Compare commits

..

No commits in common. "main" and "v1.0" have entirely different histories.
main ... v1.0

7 changed files with 144 additions and 253 deletions

View File

@ -12,7 +12,7 @@ Please follow [Alpine Linux Wiki](https://wiki.alpinelinux.org/wiki/Installation
Tools provided here can be used on any plaform for any install modes (diskless, data disk, system disk). Tools provided here can be used on any plaform for any install modes (diskless, data disk, system disk).
Just add [**headless.apkovl.tar.gz**](https://is.gd/apkovl_master)[^2] overlay file *as-is* at the root of Alpine Linux boot media (or onto any custom side-media) and boot-up the system.\ Just add [**headless.apkovl.tar.gz**](https://is.gd/apkovl_master)[^2] overlay file *as-is* at the root of Alpine Linux boot media (or onto any custom side-media) and boot-up the system.\
With default DCHP-based network interface definitions (and [SSID/pass](#extra-configuration) file if using wifi), system can then be remotely accessed with: `ssh root@<IP>`\ With default DCHP-based network interface definitions (and [SSID/pass file](#extra-configuration) if using wifi), system can then be remotely accessed with: `ssh root@<IP>`\
(system IP address may be determined with any IP scanning tools such as `nmap`). (system IP address may be determined with any IP scanning tools such as `nmap`).
As with Alpine Linux initial bring-up, `root` account has no password initially (change that during target setup!).\ As with Alpine Linux initial bring-up, `root` account has no password initially (change that during target setup!).\
@ -25,19 +25,15 @@ 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. - `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. - `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). - `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).
- `opt-out` (*optional*): dummy file to opt-out internet features (connection status, version check, auto-update) and related links usage anonymous [telemetry](https://is.gd/privacy.php).
- `auto-updt` (*optional*): allow apkovl file automatic update with latest from master branch. If it contains *reboot* keyword all in one line, system will reboot after succesful update (unless ssh session is active or `unattended.sh` script is available).
**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 (xon/xoff flow control: e.g. on Linux with `cu -l ttyACM0`).\
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`.
Main execution steps are logged: `cat /var/log/messages | grep headless`. 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 accordingly, 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. [^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). [^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).
@ -47,9 +43,8 @@ Alternatively, with host Computer set-up to share networking with USB interface
## Want to tweak more ? ## Want to tweak more ?
This repository may be forked/cloned/downloaded.\ This repository may be forked/cloned/downloaded.\
Main script file is [`headless_bootstrap`](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/overlay/usr/local/bin/headless_bootstrap).\ Main script file is [`headless.start`](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/overlay/usr/local/bin/headless_bootstrap).\
Execute `./make.sh` to rebuild `headless.apkovl.tar.gz` after changes.\ Execute `./make.sh` to rebuild `headless.apkovl.tar.gz` after changes.
(requires `busybox`; check `busybox` build options if not running from Alpine or Ubuntu)
## Credits ## Credits

Binary file not shown.

View File

@ -1 +0,0 @@
36892e9aa76807941602160ff62948c8953eefa36f9b386a0c7302fc46710b473a3e14b7eaf223a1ae6ac6454a839c78049c311fdbd50a204b2a011cb8faa474 headless.apkovl.tar.gz

35
make.sh
View File

@ -3,35 +3,28 @@
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi # SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# script meant to be run on Alpine (busybox) or on Ubuntu
# verify busybox build options if eventually using other platforms
command -v doas > /dev/null || alias doas="/usr/bin/sudo" command -v doas > /dev/null || alias doas="/usr/bin/sudo"
build_path="$(mktemp -d)" build_path="$(mktemp -d)"
if [ -n "$build_path" ]; then if [ -n "$build_path" ]; then
# prefer timestamp option for touch as it works on directories too cp -r overlay "$build_path"/.
t_stamp="$( TZ=UTC date +%Y%m%d0000.00 )" find "$build_path"/overlay/ -exec touch -md "$(date '+%F 00:00:00')" {} \;
cp -a overlay "$build_path"/.
find "$build_path"/overlay/ -exec sh -c 'TZ=UTC touch -chm -t "$0" "$1"' "$t_stamp" {} \; # setting owner/groups for runtime (won't affect mtime)
# setting modes and owner/groups for runtime (won't affect mtime)
find "$build_path"/overlay/etc -type d -exec chmod 755 {} \; find "$build_path"/overlay/etc -type d -exec chmod 755 {} \;
chmod 755 "$build_path"/overlay/etc/init.d/* chmod +x "$build_path"/overlay/etc/init.d/*
chmod 755 "$build_path"/overlay/etc/runlevels/default/* find "$build_path"/overlay/usr -type d -exec chmod 755 {} \;
chmod +x "$build_path"/overlay/usr/local/bin/*
chmod 777 "$build_path"/overlay/tmp chmod 777 "$build_path"/overlay/tmp
chmod 700 "$build_path"/overlay/tmp/.trash chmod 700 "$build_path"/overlay/tmp/.trash
chmod -R 600 "$build_path"/overlay/tmp/.trash/ssh_host_*_key chmod 600 "$build_path"/overlay/tmp/.trash/ssh_host_*_key
find "$build_path"/overlay/usr -type d -exec chmod 755 {} \; doas chown -R 0:0 "$build_path"/overlay/*
chmod 755 "$build_path"/overlay/usr/local/bin/*
doas chown -Rh 0:0 "$build_path"/overlay/* doas tar -cvf "$build_path"/headless.apkovl.tar -C "$build_path"/overlay etc usr tmp
gzip -nk9 "$build_path"/headless.apkovl.tar && mv "$build_path"/headless.apkovl.tar.gz .
touch -md "$(date '+%F 00:00:00')" headless.apkovl.tar.gz
# busybox config on Alpine & Ubuntu has FEATURE_TAR_GNU_EXTENSIONS
# (will preserve user/group/modes & mtime) and FEATURE_TAR_LONG_OPTIONS
# shellcheck disable=SC2046 # we want word splitting as result of find
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
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" doas rm -rf "$build_path"
fi fi

View File

@ -3,7 +3,7 @@
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi # SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
HDLSBSTRP_VERSION="1.2" HDLSBSTRP_VERSION="1.0"
_apk() { _apk() {
local cmd="$1" local cmd="$1"
@ -12,11 +12,11 @@ _apk() {
case $cmd in case $cmd in
add) # install only if not already present add) # install only if not already present
if ! apk info | grep -wq "${pkg}"; then if ! apk info | grep -wq "${pkg}"; then
apk add "$pkg" && printf '%s ' "${pkg}" >>/tmp/.trash/installed apk add "$pkg" && printf '%s ' "${pkg}" >> /tmp/.trash/installed
fi fi
;; ;;
del) # delete only if previously installed del) # delete only if previously installed
if grep -wq "$pkg" /tmp/.trash/installed >/dev/null 2>&1; then if grep -wq "$pkg" /tmp/.trash/installed > /dev/null 2>&1; then
apk del "$pkg" && sed -i 's/\b'"${pkg}"'\b//' /tmp/.trash/installed apk del "$pkg" && sed -i 's/\b'"${pkg}"'\b//' /tmp/.trash/installed
fi fi
;; ;;
@ -27,17 +27,17 @@ _apk() {
} }
_preserve() { _preserve() {
# Create a back-up of element (file, folder, symlink). # create a back-up of element (file, folder, symlink)
[ -z "${1}" ] && return 1 [ -z "${1}" ] && return 1
[ -e "${1}" ] && cp -a "${1}" "${1}".orig [ -e "$1" ] && cp -a "$1" "${1}.orig"
} }
_restore() { _restore() {
# Remove element (file, folder, symlink) and replace by # remove element (file, folder, symlink) and replace by
# previous back-up if available. # previous back-up if available
[ -z "${1}" ] && return 1 [ -z "${1}" ] && return 1
rm -rf "${1}" rm -rf "${1}"
[ -e "${1}".orig ] && mv -f "${1}".orig "${1}" [ -e "${1}.orig" ] && mv -f "${1}.orig" "${1}"
} }
# shellcheck disable=SC2142 # known special case # shellcheck disable=SC2142 # known special case
@ -46,11 +46,11 @@ alias _logger='logger -st "${0##*/}"'
##### End of part to be duplicated into headless_cleanup (do not alter!) ##### End of part to be duplicated into headless_cleanup (do not alter!)
_prep_cleanup() { _prep_cleanup() {
## Prep for final headless_cleanup: ## Prep for final headless_cleanup
# clears any installed packages and settings. # clears any installed packages and settings
# Copy begininng of this file to keep functions. # copy begininng of this file to keep functions
sed -n '/^#* End .*alter!)$/q;p' /usr/local/bin/headless_bootstrap >/tmp/.trash/headless_cleanup sed -n '/^#* End .*alter!)$/q;p' /usr/local/bin/headless_bootstrap > /tmp/.trash/headless_cleanup
cat <<-EOF >>/tmp/.trash/headless_cleanup cat <<-EOF >> /tmp/.trash/headless_cleanup
# Redirect stdout and errors to console as service won't show messages # Redirect stdout and errors to console as service won't show messages
exec 1>/dev/console 2>&1 exec 1>/dev/console 2>&1
@ -64,45 +64,44 @@ cat <<-EOF >>/tmp/.trash/headless_cleanup
_restore "/etc/hostname" _restore "/etc/hostname"
rm -f /etc/modprobe.d/headless_gadget.conf rm -f /etc/modprobe.d/headless_gadget.conf
# Remove from boot service to avoid spurious openrc recalls from unattended script. # remove from boot service to avoid spurious openrc recalls from unattended script
rm -f /etc/runlevels/default/headless_bootstrap rm -f /etc/runlevels/default/headless_bootstrap
rm -f /usr/local/bin/headless_bootstrap rm -f /usr/local/bin/headless_bootstrap
# Run unattended script if available. # Run unattended script if available
install -m755 ${ovlpath}/unattended.sh /tmp/headless_unattended >/dev/null 2>&1 && \ install -m755 "${ovlpath}/unattended.sh" /tmp/headless_unattended > /dev/null 2>&1 && \
_logger "Starting headless_unattended service" && \ _logger "Starting headless_unattended service" && \
rc-service headless_unattended start rc-service headless_unattended start
rm -f /etc/init.d/headless_* rm -f /etc/init.d/headless_*
_logger "Clean-up done, enjoy !" _logger "Clean-up done, enjoy !"
cat /tmp/.trash/banner >/dev/console cat /tmp/.trash/banner > /dev/console
if [ -c /dev/ttyGS${gdgt_id} ]; then if [ -c /dev/ttyGS0 ]; then
# Enabling terminal login into valid serial port: # Enabling terminal login into ttyGS0 serial for 60 sec
# no choice than making permanent change to /etc/securetty (Alpine 3.19 already has ttyGS0). # no choice than making permanent change to pre 3.19 versions of /etc/securetty
grep -q "ttyGS${gdgt_id}" /etc/securetty || echo "ttyGS${gdgt_id}" >>/etc/securetty grep -q "ttyGS0" /etc/securetty || echo "ttyGS0" >> /etc/securetty
/sbin/getty -L 115200 /dev/ttyGS${gdgt_id} vt100 & /sbin/getty -L 115200 ttyGS0 vt100 &
fi fi
exit 0
EOF EOF
chmod +x /tmp/.trash/headless_cleanup chmod +x /tmp/.trash/headless_cleanup
} }
_setup_sshd() { _setup_sshd() {
## Setup temporary SSH server (root login, no password): ## Setup temporary SSH server (root login, no password)
# we use some bundled (or optionaly provided) keys to avoid generation at startup and save time. # We use some bundled (or optionaly provided) keys to avoid generation at startup and save time
_apk add openssh-server _apk add openssh-server
_preserve "/etc/ssh/sshd_config" _preserve "/etc/ssh/sshd_config"
_preserve "/etc/conf.d/sshd" _preserve "/etc/conf.d/sshd"
cat <<-EOF >/etc/ssh/sshd_config cat <<-EOF > /etc/ssh/sshd_config
PermitRootLogin yes PermitRootLogin yes
Banner /tmp/.trash/banner Banner /tmp/.trash/banner
EOF EOF
# Client authorized_keys or no authentication. # Client authorized_keys or no authentication
if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys >/dev/null 2>&1; then if install -m600 "${ovlpath}/authorized_keys" /tmp/.trash/authorized_keys > /dev/null 2>&1; then
_logger "Enabling public key SSH authentication..." _logger "Enabling public key SSH authentication..."
cat <<-EOF >>/etc/ssh/sshd_config cat <<-EOF >> /etc/ssh/sshd_config
AuthenticationMethods publickey AuthenticationMethods publickey
AuthorizedKeysFile /tmp/.trash/authorized_keys AuthorizedKeysFile /tmp/.trash/authorized_keys
# relax strict mode as authorized_keys are inside /tmp # relax strict mode as authorized_keys are inside /tmp
@ -110,7 +109,7 @@ if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys >/dev/
EOF EOF
else else
_logger "No SSH authentication." _logger "No SSH authentication."
cat <<-EOF >>/etc/ssh/sshd_config cat <<-EOF >> /etc/ssh/sshd_config
AuthenticationMethods none AuthenticationMethods none
PermitEmptyPasswords yes PermitEmptyPasswords yes
EOF EOF
@ -119,8 +118,8 @@ fi
# Server keys: inject optional custom keys, or generate new (might be stored), # Server keys: inject optional custom keys, or generate new (might be stored),
# or use bundeled ones (not stored) # or use bundeled ones (not stored)
local keygen_stance="sshd_disable_keygen=yes" local keygen_stance="sshd_disable_keygen=yes"
if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ >/dev/null 2>&1; then if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ > /dev/null 2>&1; then
# Check for empty key within injected ones: if found, generate new keys. # check for empty key within injected ones: if found, generate new keys
if find /etc/ssh/ -maxdepth 1 -type f -name 'ssh_host_*_key*' -empty | grep -q .; then if find /etc/ssh/ -maxdepth 1 -type f -name 'ssh_host_*_key*' -empty | grep -q .; then
rm /etc/ssh/ssh_host_*_key* rm /etc/ssh/ssh_host_*_key*
keygen_stance="" keygen_stance=""
@ -131,138 +130,84 @@ if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ >/dev/null 2>&1; then
fi fi
else else
_logger "Using bundled ssh keys from RAM..." _logger "Using bundled ssh keys from RAM..."
cat <<-EOF >>/etc/ssh/sshd_config cat <<-EOF >> /etc/ssh/sshd_config
HostKey /tmp/.trash/ssh_host_ed25519_key HostKey /tmp/.trash/ssh_host_ed25519_key
HostKey /tmp/.trash/ssh_host_rsa_key HostKey /tmp/.trash/ssh_host_rsa_key
EOF EOF
fi fi
echo "$keygen_stance" >>/etc/conf.d/sshd echo "$keygen_stance" >> /etc/conf.d/sshd
rc-service sshd restart 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"
if wget -q -O /tmp/apkovl -T 10 "$file_url" >/dev/null 2>&1 && \
wget -q -O /tmp/sha -T 10 "$sha_url" >/dev/null 2>&1 && \
[ "$( 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"
if [ "$updt_status" = "successful" ]; then
printf '%s\n\n' "Updated" >>/tmp/.trash/banner
else
printf '\n' >>/tmp/.trash/banner
return 1
fi
# 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() { _tst_version() {
## Compare current version with latest online, notify & eventally calls for update # Tested URL redirects to github project page: is.gd shortener provides basic analytics.
# 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 # Analytics are public and can be checked at https://is.gd/stats.php?url=apkovl_run
# Privacy policy: https://is.gd/privacy.php # Privacy policy: https://is.gd/privacy.php
local vers="" local new_vers=""
local status="failed"
local ref="/macmpi/alpine-linux-headless-bootstrap/releases/tag/v" local ref="/macmpi/alpine-linux-headless-bootstrap/releases/tag/v"
local url="https://is.gd/apkovl_run" if wget -q -O /tmp/homepg -T 10 https://is.gd/apkovl_run > /dev/null 2>&1; then
status="success"
if wget -q -O /tmp/homepg -T 10 "$url" >/dev/null 2>&1; then ver="$( grep -o "$ref.*\"" /tmp/homepg | grep -Eo '[0-9]+[\.[0-9]+]*' )"
_logger "Internet access: success"
vers="$( grep -o "$ref.*\"" /tmp/homepg | grep -Eo '[0-9]+[\.[0-9]+]*' )"
rm -f /tmp/homepg rm -f /tmp/homepg
if [ -n "$vers" ] && ! [ "$vers" = "$HDLSBSTRP_VERSION" ]; then [ -n "$ver" ] && ! [ "$ver" = "$HDLSBSTRP_VERSION" ] && \
vers="!! Version $vers is available on Github project page !!" new_vers="!! Version $ver is available on Github project page !!" && \
_logger "$vers" _logger "$new_vers" && \
printf '%s\n' "$vers" >>/tmp/.trash/banner printf '%s\n\n' "$new_vers" >> /tmp/.trash/banner
# Optionally update apkovl if key-file allows it.
if [ -f "${ovlpath}"/auto-updt ]; then
_logger "Updating overlay file..."
_updt_apkovl &
else
_logger "(check doc to enable auto-update)"
printf '%s\n\n' "(check doc to enable auto-update)" >>/tmp/.trash/banner
fi
fi
else
_logger "Internet access: failed"
fi fi
_logger "Internet access: $status"
} }
_setup_networking() { _setup_networking() {
## Setup network interfaces. ## Setup Network interfaces
local has_wifi wlan_lst if [ -d "/sys/class/net/wlan0" ] && [ -f "${ovlpath}/wpa_supplicant.conf" ]; then
_has_wifi() { return "$has_wifi"; } _logger "Configuring wifi..."
_apk add wpa_supplicant
wlan_lst="$( find /sys/class/net/*/phy80211 -exec \ _preserve "/etc/wpa_supplicant/wpa_supplicant.conf"
sh -c 'printf %s\| "$( basename "$( dirname "$0" )" )"' {} \; 2>/dev/null )" install -m600 "${ovlpath}/wpa_supplicant.conf" /etc/wpa_supplicant/wpa_supplicant.conf
wlan_lst="${wlan_lst%\|}" else
[ -n "$wlan_lst" ] && [ -f "${ovlpath}"/wpa_supplicant.conf ] _logger "No wifi interface or setup file supplied"
has_wifi=$? fi
_preserve "/etc/network/interfaces" _preserve "/etc/network/interfaces"
if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces >/dev/null 2>&1; then if ! install -m644 "${ovlpath}/interfaces" /etc/network/interfaces > /dev/null 2>&1; then
# set default interfaces if not specified by interface file on boot storage
_logger "No interfaces file supplied, building defaults..." _logger "No interfaces file supplied, building defaults..."
cat <<-EOF >/etc/network/interfaces for dev in /sys/class/net/*; do
auto lo dev="$(basename "$dev")"
iface lo inet loopback case ${dev%%[0-9]*} in
lo)
cat <<-EOF >> /etc/network/interfaces
auto $dev
iface $dev inet loopback
EOF EOF
for dev in /sys/class/net/*; do
# shellcheck disable=SC2034 # Unused IFINDEX while still sourced from uevent.
local DEVTYPE INTERFACE IFINDEX
DEVTYPE=""
# shellcheck source=/dev/null
. "$dev"/uevent
case ${INTERFACE%%[0-9]*} in
lo)
;; ;;
eth) eth)
cat <<-EOF >>/etc/network/interfaces cat <<-EOF >> /etc/network/interfaces
auto $INTERFACE auto $dev
iface $INTERFACE inet dhcp iface $dev inet dhcp
EOF EOF
;; ;;
*) wlan)
# According to below we could rely on DEVTYPE for wlan devices [ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && cat <<-EOF >> /etc/network/interfaces
# https://lists.freedesktop.org/archives/systemd-devel/2014-January/015999.html auto $dev
# but...some wlan might still be ill-behaved: use wlan_lst iface $dev inet dhcp
# 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 EOF
# Ensure considered gadget interface is actually the connected one (may have several). ;;
[ "$DEVTYPE" = "gadget" ] && \ usb)
find /sys/class/udc/*/device/gadget."${gdgt_id}"/net/"$INTERFACE" -maxdepth 0 >/dev/null 2>&1 && \ cat <<-EOF >> /etc/network/interfaces
cat <<-EOF >>/etc/network/interfaces && cat <<-EOF >/etc/resolv.conf auto $dev
auto $INTERFACE iface $dev inet static
iface $INTERFACE inet static
address 10.42.0.2/24 address 10.42.0.2/24
gateway 10.42.0.1 gateway 10.42.0.1
EOF EOF
cat <<-EOF > /etc/resolv.conf
nameserver 208.67.222.222 nameserver 208.67.222.222
nameserver 208.67.220.220 nameserver 208.67.220.220
@ -277,53 +222,29 @@ echo "Using following network interfaces:"
cat /etc/network/interfaces cat /etc/network/interfaces
echo "###################################" echo "###################################"
if _has_wifi && grep -qE "$wlan_lst" /etc/network/interfaces; 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
_preserve "/etc/hostname" _preserve "/etc/hostname"
echo "alpine-headless" >/etc/hostname echo "alpine-headless" > /etc/hostname
hostname -F /etc/hostname hostname -F /etc/hostname
grep -q "wlan" /etc/network/interfaces && \
[ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && \
rc-service wpa_supplicant restart
rc-service networking restart rc-service networking restart
} }
_setup_gadget() { _setup_gadget() {
## Load composite USB Serial/USB Ethernel driver & setup terminal. # load composite USB Serial/USB Ethernel driver & setup terminal
_logger "Enabling USB-gadget Serial and Ethernet ports" _logger "Enabling USB-gadget Serial and Ethernet ports"
# Remove conflicting modules in case they were initially loaded (cmdline.txt). lsmod | grep -q "dwc2" || modprobe -qs dwc2
modprobe -r g_serial g_ether g_cdc modprobe -qs g_cdc
modprobe g_cdc # default config: xon/xoff flow control
# Wait for g_cdc to settle and serial ports become available stty -g -F /dev/ttyGS0 >/dev/null 2>&1 && setconsole /dev/ttyGS0
timeout 1 sh <<-EOF # notes to users willing to connect from Linux Ubuntu-based host terminal:
while ! grep -q "ttyGS" /proc/devices; do sleep 0.2; done # - user on host needs to be part of dialout group (reboot required), and
EOF # - disable spurious AT commands from ModemManager on host-side Gadget serial port
# you may create a /etc/udev/rules.d/99-ttyacms-gadget.rules as per:
# Determine which gadget ID is connected with USB cable (assume just one max). # https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284/2
# (setting console to unconnected serial port would block boot) # ATTRS{idVendor}=="0525" ATTRS{idProduct}=="a4aa", ENV{ID_MM_DEVICE_IGNORE}="1"
gdgt_id="$( find /sys/class/udc/*/current_speed -exec \
sh -c 'grep -vq "UNKNOWN" "$0" && find ${0/current_speed/}device/gadget.* -maxdepth 0' {} \; \
| sed 's/\/.*gadget\.//' )"
if [ -c /dev/ttyGS"${gdgt_id}" ]; then
# Default serial config: xon/xoff flow control.
stty -F /dev/ttyGS"${gdgt_id}"
setconsole /dev/ttyGS"${gdgt_id}"
# Notes to users willing to connect from Linux Ubuntu-based host terminal:
# - user on host needs to be part of dialout group (reboot required), and
# - disable spurious AT commands from ModemManager on host-side Gadget serial port
# one may create a /etc/udev/rules.d/99-ttyacms-gadget.rules as per:
# https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284/2
# ATTRS{idVendor}=="0525" ATTRS{idProduct}=="a4aa", ENV{ID_MM_DEVICE_IGNORE}="1"
else
_logger "USB-gadget port not connected !"
modprobe -r g_cdc
fi
} }
@ -334,35 +255,30 @@ fi
exec 1>/dev/console 2>&1 exec 1>/dev/console 2>&1
_logger "Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi" _logger "Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi"
# Help randomness for wpa_supplicant and sshd (urandom until 3.16). # help randomness for wpa_supplicant and sshd (urandom until 3.16)
rc-service seedrng restart || rc-service urandom restart rc-service seedrng restart || rc-service urandom restart
# Setup USB gadget ports if some ports are enabled in peripheral mode. # setup USB gadget mode if device has compatible device-tree
# Note: we assume dwc2/dwc3 is pre-loaded, we just check mode. find /proc/device-tree/soc/usb* -name "dr_mode" -print0 | \
gdgt_id="" xargs -0 grep -q "peripheral" && _setup_gadget
find /sys/class/udc/*/is_a_peripheral -print0 2>/dev/null | \
xargs -0 cat 2>/dev/null | grep -q "0" && \
_setup_gadget
# Determine ovl file location. # Determine ovl file location
# Grab used ovl filename from dmesg. # grab used ovl filename from dmesg
ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )" ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )"
if [ -f "${ovl}" ]; then if [ -f "${ovl}" ]; then
ovlpath="$( dirname "$ovl" )" ovlpath="$( dirname "$ovl" )"
else else
# Search path again as mountpoint have been changed later in the boot process... # search path again as mountpoint have been changed later in the boot process...
ovl="$( basename "${ovl}" )" ovl="$( basename "${ovl}" )"
ovlpath=$( find /media -maxdepth 2 -type d -path '*/.*' -prune -o -type f -name "${ovl}" -exec dirname {} \; | head -1 ) ovlpath=$( find /media -maxdepth 2 -type d -path '*/.*' -prune -o -type f -name "${ovl}" -exec dirname {} \; | head -1 )
ovl="${ovlpath}/${ovl}" ovl="${ovlpath}/${ovl}"
fi fi
# Create banner file. # Create banner file
warn="" warn=""
grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$? grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$?
_is_ro() { return "$is_ro"; } [ "$RO" -eq "0" ] && warn="(remount partition rw!)"
cat <<-EOF > /tmp/.trash/banner
_is_ro && warn="(remount partition rw!)"
cat <<-EOF >/tmp/.trash/banner
Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi
@ -375,12 +291,12 @@ cat <<-EOF >/tmp/.trash/banner
_setup_networking _setup_networking
# Test latest available version online. # Test latest available version online
# Can be skipped by creating a 'opt-out'-named dummy file aside apkovl file. # Can be skipped by creating a 'opt-out'-named dummy file aside apkovl file
[ -f "${ovlpath}"/opt-out ] || _tst_version & [ -f "${ovlpath}/opt-out" ] || _tst_version &
# Setup sshd unless unattended.sh script prevents it. # setup sshd unless unattended.sh script prevents it
grep -q "^#NO_SSH$" "${ovlpath}"/unattended.sh >/dev/null 2>&1 \ grep -q "^#NO_SSH$" "${ovlpath}/unattended.sh" > /dev/null 2>&1 \
|| _setup_sshd || _setup_sshd
_prep_cleanup _prep_cleanup

View File

@ -1,10 +0,0 @@
# 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,11 +37,10 @@ else
fi fi
# also works in case volume is mounted read-only # also works in case volume is mounted read-only
grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$? grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$?
_is_ro() { return "$is_ro"; } [ "$RO" -eq "0" ] && mount -o remount,rw "${ovlpath}"
_is_ro && mount -o remount,rw "${ovlpath}"
rm -f "${ovl}" rm -f "${ovl}"
_is_ro && mount -o remount,ro "${ovlpath}" [ "$RO" -eq "0" ] && mount -o remount,ro "${ovlpath}"
######################################################## ########################################################
@ -99,7 +98,6 @@ cat <<-EOF > /tmp/ANSWERFILE
# trick setup-alpine to pretend existing SSH connection # trick setup-alpine to pretend existing SSH connection
# and therefore keep (do not reset) network interfaces while running in background # and therefore keep (do not reset) network interfaces while running in background
# requires alpine-conf 3.15.1 and later, available from Alpine 3.17
SSH_CONNECTION="FAKE" setup-alpine -ef /tmp/ANSWERFILE SSH_CONNECTION="FAKE" setup-alpine -ef /tmp/ANSWERFILE
lbu commit -d lbu commit -d