Skip to content

Commit 4026274

Browse files
committed
version 1.1
improved interfaces detection: fixes #20 make.sh: tighten-up archive construction (reproducibility)
1 parent f430fc3 commit 4026274

File tree

5 files changed

+96
-73
lines changed

5 files changed

+96
-73
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Please follow [Alpine Linux Wiki](https://wiki.alpinelinux.org/wiki/Installation
1212
Tools provided here can be used on any plaform for any install modes (diskless, data disk, system disk).
1313

1414
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.\
15-
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>`\
15+
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>`\
1616
(system IP address may be determined with any IP scanning tools such as `nmap`).
1717

1818
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
2929

3030
**Goody:** seamless USB-serial & USB-ethernet gadget mode (PiZero for instance):\
3131
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.\
32-
Serial terminal can then be connected-to from host Computer (xon/xoff flow control: e.g. on Linux with `cu -l ttyACM0`).\
32+
Serial terminal can then be connected-to from host Computer (e.g. `cu -l ttyACM0` on Linux. xon/xoff flow control).\
3333
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 [email protected]`.
3434

3535
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`.
4444
## Want to tweak more ?
4545
This repository may be forked/cloned/downloaded.\
4646
Main script file is [`headless.start`](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/overlay/usr/local/bin/headless_bootstrap).\
47-
Execute `./make.sh` to rebuild `headless.apkovl.tar.gz` after changes.
47+
Execute `./make.sh` to rebuild `headless.apkovl.tar.gz` after changes.\
48+
(requires `busybox`; check `busybox` build options if not running from Alpine or Ubuntu)
4849

4950

5051
## Credits

headless.apkovl.tar.gz

284 Bytes
Binary file not shown.

make.sh

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,34 @@
33
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
44
# SPDX-License-Identifier: MIT
55

6-
6+
# script meant to be run on Alpine (busybox) or on Ubuntu
7+
# verify busybox build options if eventually using other platforms
78
command -v doas > /dev/null || alias doas="/usr/bin/sudo"
89

910
build_path="$(mktemp -d)"
1011
if [ -n "$build_path" ]; then
11-
cp -r overlay "$build_path"/.
12-
find "$build_path"/overlay/ -exec touch -md "$(date '+%F 00:00:00')" {} \;
13-
14-
# setting owner/groups for runtime (won't affect mtime)
12+
# prefer timestamp option for touch as it works on directories too
13+
t_stamp="$( TZ=UTC date +%Y%m%d0000.00 )"
14+
cp -a overlay "$build_path"/.
15+
find "$build_path"/overlay/ -exec sh -c 'TZ=UTC touch -chm -t "$0" "$1"' "$t_stamp" {} \;
16+
# setting modes and owner/groups for runtime (won't affect mtime)
1517
find "$build_path"/overlay/etc -type d -exec chmod 755 {} \;
16-
chmod +x "$build_path"/overlay/etc/init.d/*
17-
find "$build_path"/overlay/usr -type d -exec chmod 755 {} \;
18-
chmod +x "$build_path"/overlay/usr/local/bin/*
18+
chmod 755 "$build_path"/overlay/etc/init.d/*
19+
chmod 755 "$build_path"/overlay/etc/runlevels/default/*
1920
chmod 777 "$build_path"/overlay/tmp
2021
chmod 700 "$build_path"/overlay/tmp/.trash
21-
chmod 600 "$build_path"/overlay/tmp/.trash/ssh_host_*_key
22-
doas chown -R 0:0 "$build_path"/overlay/*
23-
24-
doas tar -cvf "$build_path"/headless.apkovl.tar -C "$build_path"/overlay etc usr tmp
25-
gzip -nk9 "$build_path"/headless.apkovl.tar && mv "$build_path"/headless.apkovl.tar.gz .
26-
touch -md "$(date '+%F 00:00:00')" headless.apkovl.tar.gz
27-
22+
chmod -R 600 "$build_path"/overlay/tmp/.trash/ssh_host_*_key
23+
find "$build_path"/overlay/usr -type d -exec chmod 755 {} \;
24+
chmod 755 "$build_path"/overlay/usr/local/bin/*
25+
doas chown -Rh 0:0 "$build_path"/overlay/*
26+
27+
# busybox config on Alpine & Ubuntu has FEATURE_TAR_GNU_EXTENSIONS
28+
# (will preserve user/group/modes & mtime) and FEATURE_TAR_LONG_OPTIONS
29+
# shellcheck disable=SC2046 # we want word splitting as result of find
30+
doas tar cv -C "$build_path"/overlay --no-recursion \
31+
$(doas find "$build_path"/overlay/ | sed "s|$build_path/overlay/||" | sort | xargs ) | \
32+
gzip -c9n > headless.apkovl.tar.gz
33+
TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz
2834
doas rm -rf "$build_path"
2935
fi
3036

overlay/usr/local/bin/headless_bootstrap

Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi
44
# SPDX-License-Identifier: MIT
55

6-
HDLSBSTRP_VERSION="1.0"
6+
HDLSBSTRP_VERSION="1.1"
77

88
_apk() {
99
local cmd="$1"
@@ -29,15 +29,15 @@ _apk() {
2929
_preserve() {
3030
# create a back-up of element (file, folder, symlink)
3131
[ -z "${1}" ] && return 1
32-
[ -e "$1" ] && cp -a "$1" "${1}.orig"
32+
[ -e "${1}" ] && cp -a "${1}" "${1}".orig
3333
}
3434

3535
_restore() {
3636
# remove element (file, folder, symlink) and replace by
3737
# previous back-up if available
3838
[ -z "${1}" ] && return 1
3939
rm -rf "${1}"
40-
[ -e "${1}.orig" ] && mv -f "${1}.orig" "${1}"
40+
[ -e "${1}".orig ] && mv -f "${1}".orig "${1}"
4141
}
4242

4343
# shellcheck disable=SC2142 # known special case
@@ -69,7 +69,7 @@ cat <<-EOF >> /tmp/.trash/headless_cleanup
6969
rm -f /usr/local/bin/headless_bootstrap
7070
7171
# Run unattended script if available
72-
install -m755 "${ovlpath}/unattended.sh" /tmp/headless_unattended > /dev/null 2>&1 && \
72+
install -m755 "${ovlpath}"/unattended.sh /tmp/headless_unattended > /dev/null 2>&1 && \
7373
_logger "Starting headless_unattended service" && \
7474
rc-service headless_unattended start
7575
@@ -99,7 +99,7 @@ cat <<-EOF > /etc/ssh/sshd_config
9999
EOF
100100

101101
# Client authorized_keys or no authentication
102-
if install -m600 "${ovlpath}/authorized_keys" /tmp/.trash/authorized_keys > /dev/null 2>&1; then
102+
if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys > /dev/null 2>&1; then
103103
_logger "Enabling public key SSH authentication..."
104104
cat <<-EOF >> /etc/ssh/sshd_config
105105
AuthenticationMethods publickey
@@ -161,58 +161,66 @@ _logger "Internet access: $status"
161161

162162
_setup_networking() {
163163
## Setup Network interfaces
164-
if [ -d "/sys/class/net/wlan0" ] && [ -f "${ovlpath}/wpa_supplicant.conf" ]; then
164+
local has_wifi
165+
_has_wifi() { return "$has_wifi"; }
166+
167+
find /sys/class/ieee80211/*/device/net/* -maxdepth 0 -type d -exec basename {} \; > /tmp/.wlan_list 2>/dev/null
168+
[ -s /tmp/.wlan_list ] && [ -f "${ovlpath}"/wpa_supplicant.conf ]
169+
has_wifi=$?
170+
if _has_wifi; then
165171
_logger "Configuring wifi..."
166172
_apk add wpa_supplicant
167173
_preserve "/etc/wpa_supplicant/wpa_supplicant.conf"
168-
install -m600 "${ovlpath}/wpa_supplicant.conf" /etc/wpa_supplicant/wpa_supplicant.conf
174+
install -m600 "${ovlpath}"/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf
175+
rc-service wpa_supplicant restart
169176
else
170-
_logger "No wifi interface or setup file supplied"
177+
_logger "No wifi interface or SSID/pass file supplied"
171178
fi
172179

173180
_preserve "/etc/network/interfaces"
174-
if ! install -m644 "${ovlpath}/interfaces" /etc/network/interfaces > /dev/null 2>&1; then
175-
# set default interfaces if not specified by interface file on boot storage
181+
if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces > /dev/null 2>&1; then
176182
_logger "No interfaces file supplied, building defaults..."
183+
cat <<-EOF > /etc/network/interfaces
184+
auto lo
185+
iface lo inet loopback
186+
187+
EOF
177188
for dev in /sys/class/net/*; do
178-
dev="$(basename "$dev")"
179-
case ${dev%%[0-9]*} in
189+
# shellcheck disable=SC2034 # Unused IFINDEX while still sourced from uevent
190+
local DEVTYPE INTERFACE IFINDEX
191+
DEVTYPE=""
192+
# shellcheck source=/dev/null
193+
. "$dev"/uevent
194+
case ${INTERFACE%%[0-9]*} in
180195
lo)
181-
cat <<-EOF >> /etc/network/interfaces
182-
auto $dev
183-
iface $dev inet loopback
184-
185-
EOF
186-
;;
196+
;;
187197
eth)
188-
cat <<-EOF >> /etc/network/interfaces
189-
auto $dev
190-
iface $dev inet dhcp
198+
cat <<-EOF >> /etc/network/interfaces
199+
auto $INTERFACE
200+
iface $INTERFACE inet dhcp
191201
192202
EOF
193-
;;
194-
wlan)
195-
[ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && cat <<-EOF >> /etc/network/interfaces
196-
auto $dev
197-
iface $dev inet dhcp
198-
199-
EOF
200-
;;
201-
usb)
203+
;;
204+
*)
205+
_has_wifi && grep -q "$INTERFACE" /tmp/.wlan_list && \
202206
cat <<-EOF >> /etc/network/interfaces
203-
auto $dev
204-
iface $dev inet static
205-
address 10.42.0.2/24
206-
gateway 10.42.0.1
207-
208-
EOF
209-
210-
cat <<-EOF > /etc/resolv.conf
211-
nameserver 208.67.222.222
212-
nameserver 208.67.220.220
213-
214-
EOF
215-
;;
207+
auto $INTERFACE
208+
iface $INTERFACE inet dhcp
209+
210+
EOF
211+
[ "$DEVTYPE" = "gadget" ] && \
212+
cat <<-EOF >> /etc/network/interfaces && cat <<-EOF > /etc/resolv.conf
213+
auto $INTERFACE
214+
iface $INTERFACE inet static
215+
address 10.42.0.2/24
216+
gateway 10.42.0.1
217+
218+
EOF
219+
nameserver 208.67.222.222
220+
nameserver 208.67.220.220
221+
222+
EOF
223+
;;
216224
esac
217225
done
218226
fi
@@ -226,25 +234,31 @@ _preserve "/etc/hostname"
226234
echo "alpine-headless" > /etc/hostname
227235
hostname -F /etc/hostname
228236

229-
grep -q "wlan" /etc/network/interfaces && \
230-
[ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && \
231-
rc-service wpa_supplicant restart
232237
rc-service networking restart
238+
rm -f /tmp/.wlan_list
233239
}
234240

235241
_setup_gadget() {
236-
# load composite USB Serial/USB Ethernel driver & setup terminal
242+
## load composite USB Serial/USB Ethernel driver & setup terminal
237243
_logger "Enabling USB-gadget Serial and Ethernet ports"
238244
lsmod | grep -q "dwc2" || modprobe -qs dwc2
239-
modprobe -qs g_cdc
240-
# default config: xon/xoff flow control
241-
stty -g -F /dev/ttyGS0 >/dev/null 2>&1 && setconsole /dev/ttyGS0
245+
# remove conflicting modules in case they were initially loaded (cmdline.txt)
246+
modprobe -rq g_serial g_ether g_cdc
247+
modprobe -q g_cdc && sleep 1
248+
# once driver has settled check if cable is connected: unload if not
249+
[ "$( cat "$udc_gadget"/current_speed )" = "UNKNOWN" ] && \
250+
_logger "USB cable not connected !!" && modprobe -rq g_cdc && return 1
251+
252+
# default serial config: xon/xoff flow control
253+
stty -g -F /dev/ttyGS0 >/dev/null 2>&1
242254
# notes to users willing to connect from Linux Ubuntu-based host terminal:
243255
# - user on host needs to be part of dialout group (reboot required), and
244256
# - disable spurious AT commands from ModemManager on host-side Gadget serial port
245257
# you may create a /etc/udev/rules.d/99-ttyacms-gadget.rules as per:
246258
# https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284/2
247259
# ATTRS{idVendor}=="0525" ATTRS{idProduct}=="a4aa", ENV{ID_MM_DEVICE_IGNORE}="1"
260+
261+
setconsole /dev/ttyGS0
248262
}
249263

250264

@@ -258,9 +272,10 @@ _logger "Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi"
258272
# help randomness for wpa_supplicant and sshd (urandom until 3.16)
259273
rc-service seedrng restart || rc-service urandom restart
260274

261-
# setup USB gadget mode if device has compatible device-tree
262-
find /proc/device-tree/soc/usb* -name "dr_mode" -print0 | \
263-
xargs -0 grep -q "peripheral" && _setup_gadget
275+
# setup USB gadget mode if such device mode is enabled
276+
udc_gadget="$( dirname "$( find -L /sys/class/udc/* -maxdepth 2 -type f -name "is_a_peripheral" 2>/dev/null)" )"
277+
[ "$( cat "$udc_gadget"/is_a_peripheral 2>/dev/null )" = "0" ] && \
278+
_setup_gadget
264279

265280
# Determine ovl file location
266281
# grab used ovl filename from dmesg
@@ -293,10 +308,10 @@ _setup_networking
293308

294309
# Test latest available version online
295310
# Can be skipped by creating a 'opt-out'-named dummy file aside apkovl file
296-
[ -f "${ovlpath}/opt-out" ] || _tst_version &
311+
[ -f "${ovlpath}"/opt-out ] || _tst_version &
297312

298313
# setup sshd unless unattended.sh script prevents it
299-
grep -q "^#NO_SSH$" "${ovlpath}/unattended.sh" > /dev/null 2>&1 \
314+
grep -q "^#NO_SSH$" "${ovlpath}"/unattended.sh > /dev/null 2>&1 \
300315
|| _setup_sshd
301316

302317
_prep_cleanup

sample_unattended.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ cat <<-EOF > /tmp/ANSWERFILE
9898

9999
# trick setup-alpine to pretend existing SSH connection
100100
# and therefore keep (do not reset) network interfaces while running in background
101+
# requires alpine-conf 3.15.1 and later, available from Alpine 3.17
101102
SSH_CONNECTION="FAKE" setup-alpine -ef /tmp/ANSWERFILE
102103
lbu commit -d
103104

0 commit comments

Comments
 (0)