version 1.1
improved interfaces detection: fixes https://github.com/macmpi/alpine-linux-headless-bootstrap/issues/20 make.sh: tighten-up archive construction (reproducibility)
This commit is contained in:
parent
f430fc3ce5
commit
4026274721
|
@ -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).
|
||||
|
||||
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 file](#extra-configuration) if using wifi), system can then be remotely accessed with: `ssh root@<IP>`\
|
||||
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>`\
|
||||
(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!).\
|
||||
|
@ -29,7 +29,7 @@ Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapp
|
|||
|
||||
**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`).\
|
||||
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`.
|
||||
|
||||
Main execution steps are logged: `cat /var/log/messages | grep headless`.
|
||||
|
@ -44,7 +44,8 @@ Main execution steps are logged: `cat /var/log/messages | grep headless`.
|
|||
## Want to tweak more ?
|
||||
This repository may be forked/cloned/downloaded.\
|
||||
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
|
||||
|
|
Binary file not shown.
34
make.sh
34
make.sh
|
@ -3,28 +3,34 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
|
||||
# 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"
|
||||
|
||||
build_path="$(mktemp -d)"
|
||||
if [ -n "$build_path" ]; then
|
||||
cp -r overlay "$build_path"/.
|
||||
find "$build_path"/overlay/ -exec touch -md "$(date '+%F 00:00:00')" {} \;
|
||||
|
||||
# setting owner/groups for runtime (won't affect mtime)
|
||||
# prefer timestamp option for touch as it works on directories too
|
||||
t_stamp="$( TZ=UTC date +%Y%m%d0000.00 )"
|
||||
cp -a overlay "$build_path"/.
|
||||
find "$build_path"/overlay/ -exec sh -c 'TZ=UTC touch -chm -t "$0" "$1"' "$t_stamp" {} \;
|
||||
# setting modes and owner/groups for runtime (won't affect mtime)
|
||||
find "$build_path"/overlay/etc -type d -exec chmod 755 {} \;
|
||||
chmod +x "$build_path"/overlay/etc/init.d/*
|
||||
find "$build_path"/overlay/usr -type d -exec chmod 755 {} \;
|
||||
chmod +x "$build_path"/overlay/usr/local/bin/*
|
||||
chmod 755 "$build_path"/overlay/etc/init.d/*
|
||||
chmod 755 "$build_path"/overlay/etc/runlevels/default/*
|
||||
chmod 777 "$build_path"/overlay/tmp
|
||||
chmod 700 "$build_path"/overlay/tmp/.trash
|
||||
chmod 600 "$build_path"/overlay/tmp/.trash/ssh_host_*_key
|
||||
doas chown -R 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
|
||||
chmod -R 600 "$build_path"/overlay/tmp/.trash/ssh_host_*_key
|
||||
find "$build_path"/overlay/usr -type d -exec chmod 755 {} \;
|
||||
chmod 755 "$build_path"/overlay/usr/local/bin/*
|
||||
doas chown -Rh 0:0 "$build_path"/overlay/*
|
||||
|
||||
# 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
|
||||
TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz
|
||||
doas rm -rf "$build_path"
|
||||
fi
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
HDLSBSTRP_VERSION="1.0"
|
||||
HDLSBSTRP_VERSION="1.1"
|
||||
|
||||
_apk() {
|
||||
local cmd="$1"
|
||||
|
@ -29,7 +29,7 @@ _apk() {
|
|||
_preserve() {
|
||||
# create a back-up of element (file, folder, symlink)
|
||||
[ -z "${1}" ] && return 1
|
||||
[ -e "$1" ] && cp -a "$1" "${1}.orig"
|
||||
[ -e "${1}" ] && cp -a "${1}" "${1}".orig
|
||||
}
|
||||
|
||||
_restore() {
|
||||
|
@ -37,7 +37,7 @@ _restore() {
|
|||
# previous back-up if available
|
||||
[ -z "${1}" ] && return 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
|
||||
|
@ -69,7 +69,7 @@ cat <<-EOF >> /tmp/.trash/headless_cleanup
|
|||
rm -f /usr/local/bin/headless_bootstrap
|
||||
|
||||
# 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" && \
|
||||
rc-service headless_unattended start
|
||||
|
||||
|
@ -99,7 +99,7 @@ cat <<-EOF > /etc/ssh/sshd_config
|
|||
EOF
|
||||
|
||||
# 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..."
|
||||
cat <<-EOF >> /etc/ssh/sshd_config
|
||||
AuthenticationMethods publickey
|
||||
|
@ -161,58 +161,66 @@ _logger "Internet access: $status"
|
|||
|
||||
_setup_networking() {
|
||||
## Setup Network interfaces
|
||||
if [ -d "/sys/class/net/wlan0" ] && [ -f "${ovlpath}/wpa_supplicant.conf" ]; then
|
||||
local has_wifi
|
||||
_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 ]
|
||||
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
|
||||
install -m600 "${ovlpath}"/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf
|
||||
rc-service wpa_supplicant restart
|
||||
else
|
||||
_logger "No wifi interface or setup file supplied"
|
||||
_logger "No wifi interface or SSID/pass file supplied"
|
||||
fi
|
||||
|
||||
_preserve "/etc/network/interfaces"
|
||||
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
|
||||
if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces > /dev/null 2>&1; then
|
||||
_logger "No interfaces file supplied, building defaults..."
|
||||
cat <<-EOF > /etc/network/interfaces
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
EOF
|
||||
for dev in /sys/class/net/*; do
|
||||
dev="$(basename "$dev")"
|
||||
case ${dev%%[0-9]*} in
|
||||
# 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)
|
||||
cat <<-EOF >> /etc/network/interfaces
|
||||
auto $dev
|
||||
iface $dev inet loopback
|
||||
|
||||
EOF
|
||||
;;
|
||||
;;
|
||||
eth)
|
||||
cat <<-EOF >> /etc/network/interfaces
|
||||
auto $INTERFACE
|
||||
iface $INTERFACE inet dhcp
|
||||
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
_has_wifi && grep -q "$INTERFACE" /tmp/.wlan_list && \
|
||||
cat <<-EOF >> /etc/network/interfaces
|
||||
auto $dev
|
||||
iface $dev inet dhcp
|
||||
auto $INTERFACE
|
||||
iface $INTERFACE inet dhcp
|
||||
|
||||
EOF
|
||||
;;
|
||||
wlan)
|
||||
[ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && cat <<-EOF >> /etc/network/interfaces
|
||||
auto $dev
|
||||
iface $dev inet dhcp
|
||||
EOF
|
||||
[ "$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
|
||||
|
||||
EOF
|
||||
;;
|
||||
usb)
|
||||
cat <<-EOF >> /etc/network/interfaces
|
||||
auto $dev
|
||||
iface $dev 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
|
||||
|
||||
cat <<-EOF > /etc/resolv.conf
|
||||
nameserver 208.67.222.222
|
||||
nameserver 208.67.220.220
|
||||
|
||||
EOF
|
||||
;;
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
@ -226,25 +234,31 @@ _preserve "/etc/hostname"
|
|||
echo "alpine-headless" > /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
|
||||
rm -f /tmp/.wlan_list
|
||||
}
|
||||
|
||||
_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"
|
||||
lsmod | grep -q "dwc2" || modprobe -qs dwc2
|
||||
modprobe -qs g_cdc
|
||||
# default config: xon/xoff flow control
|
||||
stty -g -F /dev/ttyGS0 >/dev/null 2>&1 && setconsole /dev/ttyGS0
|
||||
# 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
|
||||
|
||||
# default serial config: xon/xoff flow control
|
||||
stty -g -F /dev/ttyGS0 >/dev/null 2>&1
|
||||
# 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
|
||||
# you 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"
|
||||
|
||||
setconsole /dev/ttyGS0
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,9 +272,10 @@ _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 device has compatible device-tree
|
||||
find /proc/device-tree/soc/usb* -name "dr_mode" -print0 | \
|
||||
xargs -0 grep -q "peripheral" && _setup_gadget
|
||||
# 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
|
||||
|
||||
# Determine ovl file location
|
||||
# grab used ovl filename from dmesg
|
||||
|
@ -293,10 +308,10 @@ _setup_networking
|
|||
|
||||
# Test latest available version online
|
||||
# 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
|
||||
grep -q "^#NO_SSH$" "${ovlpath}/unattended.sh" > /dev/null 2>&1 \
|
||||
grep -q "^#NO_SSH$" "${ovlpath}"/unattended.sh > /dev/null 2>&1 \
|
||||
|| _setup_sshd
|
||||
|
||||
_prep_cleanup
|
||||
|
|
|
@ -98,6 +98,7 @@ cat <<-EOF > /tmp/ANSWERFILE
|
|||
|
||||
# trick setup-alpine to pretend existing SSH connection
|
||||
# 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
|
||||
lbu commit -d
|
||||
|
||||
|
|
Loading…
Reference in New Issue