Compare commits

..

No commits in common. "a2d5c8a304e7f0dfb534966a8fe441d0c61a6432" and "bafc443817f2bb46fe3e9382e482200237c6de31" have entirely different histories.

4 changed files with 34 additions and 94 deletions

View File

@ -1,16 +1,16 @@
# Bootstrap Alpine Linux on a headless system # Deploy Alpine Linux on a headless system
[Alpine Linux documentation](https://docs.alpinelinux.org/user-handbook/0.1a/Installing/setup_alpine.html) assumes **initial setup** is carried-out on a system with a keyboard & display to interract with.\ [Alpine Linux documentation](https://docs.alpinelinux.org/user-handbook/0.1a/Installing/setup_alpine.html) assumes **initial setup** is carried-out on a system with a keyboard & display to interract with.\
However, in many cases one might want to deploy a headless system that is only available through a network connection (ethernet, wifi or as USB ethernet gadget). However, in many cases one might want to deploy a headless system that is only available through a network connection (ethernet, wifi or as USB ethernet gadget).
This repo provides an **overlay file** to initially bootstrap[^1] a headless system (leveraging Alpine distro's `initramfs` feature): it starts a ssh server to log-into from another Computer, so that actual install on fresh system (or rescue on existing disk-based system) can then be performed remotely. This repo provides an **overlay file** to initially boot such headless system (leveraging Alpine distro's `initramfs` feature): it starts a basic ssh server to log-into from another Computer, in order to then perform actual system setup.
## Setup procedure: ## Install procedure:
Please follow [Alpine Linux Wiki](https://wiki.alpinelinux.org/wiki/Installation#Installation_Overview) to download & create installation media for the target platform.\ Please follow [Alpine Linux Wiki](https://wiki.alpinelinux.org/wiki/Installation#Installation_Overview) to download & create installation media for the target platform.\
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://github.com/macmpi/alpine-linux-headless-bootstrap/raw/main/headless.apkovl.tar.gz)[^2] overlay file 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://github.com/macmpi/alpine-linux-headless-bootstrap/raw/main/headless.apkovl.tar.gz)[^1] overlay file at the root of Alpine Linux boot media (or onto any custom side-media) and boot-up the system.\
With default network interface definitions (and SSID/pass file if using wifi), system can then be remotely accessed with: `ssh root@<IP>`\ With default network interface definitions (and SSID/pass 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`). (system IP address may be determined with any IP scanning tools such as `nmap`).
@ -19,23 +19,21 @@ From there, actual system install can be performed as usual with `setup-alpine`
Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapping configuration (check sample files): Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapping configuration (check sample files):
- `wpa_supplicant.conf`[^3] (*mandatory for wifi usecase*): define wifi SSID & password. - `wpa_supplicant.conf`[^2] (*mandatory for wifi usecase*): define wifi SSID & password.
- `interfaces`[^3] (*optional*): define network interfaces at will, if defaults DCHP-based are not suitable. - `interfaces`[^2] (*optional*): define network interfaces at will, if defaults DCHP-based are not suitable.
- `ssh_host_*_key*` (*optional*): provide 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 custom ssh keys to be injected (may be stored), instead of using bundled ones[^1] (not stored). Providing an empty key file will trigger new keys generation (ssh server may take longer to start).
- `unattended.sh`[^3] (*optional*): create custom automated deployment script to further tune & extend actual setup (backgrounded). - `unattended.sh`[^2] (*optional*): create custom automated deployment script to further tune & extend actual setup (backgrounded).
**Goody:** seamless USB-ethernet gadget boostrapping (PiZero for instance):\ **Goody:** seamless USB bootstrapping for PiZero devices (or similar supporting USB ethernet gadget networking):\
On supporting Pi devices, just add `dtoverlay=dwc2` in `usercfg.txt` (or `config.txt`), and plug USB cable into Computer port.\ Just add `dtoverlay=dwc2` in `usercfg.txt` (or `config.txt`), and plug USB cable into Computer port.\
With Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from Computer with: `ssh root@10.42.0.2` With Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from Computer with: `ssh root@10.42.0.2`
Main execution steps are logged in `/var/log/messages`. Main execution steps are logged in `/var/log/messages`.
[^1]: Initial boot fully preserves system's original state (config files & installed packages): a fresh system will therefore come-up as unconfigured. [^1]: About bundled ssh keys: as this package is essentially intended to **quickly bootstrap** system in order to configure it, it purposely embeds [some ssh keys](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/overlay/etc/ssh) so that bootstrapping is as fast as possible. Those (temporary) keys are moved in RAM /tmp, so they will **not be saved/reused** once permanent configuration is set (with or without ssh server voluntarily installed in permanent 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/etc/ssh) 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]: These files are linux text files: Windows/macOS users need to use text editors supporting linux text line-ending (such as [notepad++](https://notepad-plus-plus.org/), BBEdit or any similar).
[^3]: These files are linux text files: Windows/macOS users need to use text editors supporting linux text line-ending (such as [notepad++](https://notepad-plus-plus.org/), BBEdit or any similar).
## How to customize ? ## How to customize ?

Binary file not shown.

View File

@ -3,44 +3,7 @@
# Copyright 2022 - 2023, macmpi # Copyright 2022 - 2023, macmpi
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
VERSION="0.8" VERSION="0.7"
_apk() {
local cmd="$1"
local pkg="$2"
case $cmd in
add) # install only if not already present
if ! apk info | grep -wq "${pkg}"; then
apk add "$pkg" && printf "${pkg} " >> /tmp/.trash/installed
fi
;;
del) # delete only if previously installed
if grep -wq "$pkg" /tmp/.trash/installed; then
apk del "$pkg" && sed -i 's/\b'"${pkg}"'\b//' /tmp/.trash/installed
fi
;;
*)
echo "only add/del: wrong usage"; exit
;;
esac
}
_preserve() {
[ -f "$1" ] && cp "$1" "${1}.orig"
}
_restore() {
if [ -f "${1}.orig" ]; then
mv -- "${1}.orig" "${1}"
else
rm -rf "${1}"
fi
}
##### End of part to be dupplicated into post-cleanup (do not alter!)
# Redirect stdout and errors to console as rc.local does not log anything # Redirect stdout and errors to console as rc.local does not log anything
exec 1>/dev/console 2>&1 exec 1>/dev/console 2>&1
@ -48,25 +11,20 @@ exec 1>/dev/console 2>&1
logger -st ${0##*/} "Alpine Linux headless bootstrap v$VERSION by macmpi" logger -st ${0##*/} "Alpine Linux headless bootstrap v$VERSION by macmpi"
mkdir /tmp/.trash mkdir /tmp/.trash
ovlpath=$( find /media -maxdepth 2 -type d -path '*/.*' -prune -o -type f -name *.apkovl.tar.gz -exec dirname {} \; | head -1 )
# grab used ovl filename from dmesg # Help randomess for wpa_supplicant and ssh server
ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )"
ovlpath="$( dirname "$ovl" )"
# Help randomness for wpa_supplicant and ssh server
rc-service seedrng start rc-service seedrng start
## Setup Network interfaces ## Setup Network interfaces
if [ -f "${ovlpath}/wpa_supplicant.conf" ]; then if [ -f "${ovlpath}/wpa_supplicant.conf" ]; then
logger -st ${0##*/} "Wifi setup found !" logger -st ${0##*/} "Wifi setup found !"
_apk add wpa_supplicant 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
else else
logger -st ${0##*/} "Wifi setup not found !" logger -st ${0##*/} "Wifi setup not found !"
fi fi
_preserve "/etc/network/interfaces"
if ! install -m644 "${ovlpath}/interfaces" /etc/network/interfaces; then if ! install -m644 "${ovlpath}/interfaces" /etc/network/interfaces; then
# set default interfaces if not specified by interface file on boot storage # set default interfaces if not specified by interface file on boot storage
logger -st ${0##*/} "No interfaces file supplied, building default interfaces..." logger -st ${0##*/} "No interfaces file supplied, building default interfaces..."
@ -116,21 +74,18 @@ fi
echo "Using following network interfaces:" echo "Using following network interfaces:"
cat /etc/network/interfaces cat /etc/network/interfaces
_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 && \ grep -q "wlan" /etc/network/interfaces && [ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && rc-service wpa_supplicant start
[ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && \
rc-service wpa_supplicant start
rc-service networking start rc-service networking start
## 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 apk add openssh
_preserve "/etc/ssh/sshd_config" cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
_preserve "/etc/conf.d/sshd" cp /etc/conf.d/sshd /etc/conf.d/sshd.orig
cat <<-EOF >> /etc/ssh/sshd_config cat <<-EOF >> /etc/ssh/sshd_config
AuthenticationMethods none AuthenticationMethods none
@ -140,29 +95,20 @@ cat <<-EOF >> /etc/ssh/sshd_config
EOF EOF
# Banner file # Banner file
warn=""
grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$?
[ "$RO" -eq "0" ] && warn="(remount partition rw!)"
cat <<-EOF > /tmp/.trash/banner cat <<-EOF > /tmp/.trash/banner
Alpine Linux headless bootstrap v$VERSION by macmpi Alpine Linux headless bootstrap v$VERSION by macmpi
You may want to delete/rename .apkovl file before reboot ${warn}:
$ovl
(can be done automatically with unattended script - see sample snippet)
EOF EOF
# Bundled temporary keys are moved in RAM /tmp so they won't be stored # Bundled temporary keys are moved in RAM /tmp so they won't be stored
# within permanent config later (new ones will then be generated at reboot) # within permanent config later (new ones will then be generated)
KEYGEN_STANCE="sshd_disable_keygen=yes" KEYGEN_STANCE="sshd_disable_keygen=yes"
mv /etc/ssh/ssh_host_*_key* /tmp/.trash/. mv /etc/ssh/ssh_host_*_key* /tmp/.trash/.
# Inject optional custom keys (those might be stored) # Inject optional custom keys (those might be stored)
if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/; then if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/; then
# check for empty key within injected ones: if found, generate new keys # check for empty key within injected ones: generate new keys if found
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=""
@ -184,19 +130,16 @@ rc-service sshd start
## Prep for final post-cleanup ## Prep for final post-cleanup
## clears any installed packages and settings ## clears any installed packages and settings
# copy begininng of this file to keep functions cat <<-EOF > /tmp/.trash/post-cleanup
sed -n '/^#* End .*alter!)$/q;p' /etc/local.d/headless.start > /tmp/.trash/post-cleanup #!/bin/sh
cat <<-EOF >> /tmp/.trash/post-cleanup
logger -st ${0##*/} "Cleaning-up..." logger -st ${0##*/} "Cleaning-up..."
_restore "/etc/ssh/sshd_config" mv /etc/ssh/sshd_config.orig /etc/ssh/sshd_config
_restore "/etc/conf.d/sshd" mv /etc/conf.d/sshd.orig /etc/conf.d/sshd
_apk del openssh apk del openssh
_restore "/etc/wpa_supplicant/wpa_supplicant.conf" apk del wpa_supplicant
_apk del wpa_supplicant rm -rf /etc/wpa_supplicant
_restore "/etc/network/interfaces" rm /etc/network/interfaces
_restore "/etc/hostname" rm /etc/hostname
rm /etc/modules-load.d/g_ether.conf rm /etc/modules-load.d/g_ether.conf
rm /etc/modprobe.d/g_ether.conf rm /etc/modprobe.d/g_ether.conf
rc-update del local default rc-update del local default

View File

@ -14,13 +14,12 @@ sleep 60
## This snippet removes apkovl file on volume after initial boot ## This snippet removes apkovl file on volume after initial boot
ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )" ovlpath=$( find /media -maxdepth 2 -type d -path '*/.*' -prune -o -type f -name *.apkovl.tar.gz -exec dirname {} \; | head -1 )
ovlpath="$( dirname "$ovl" )"
# 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; RO=$? grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$?
[ "$RO" -eq "0" ] && mount -o remount,rw "${ovlpath}" [ "$RO" -eq "0" ] && mount -o remount,rw "${ovlpath}"
rm -f "${ovl}" rm "${ovlpath}"/*.apkovl.tar.gz
[ "$RO" -eq "0" ] && mount -o remount,ro "${ovlpath}" [ "$RO" -eq "0" ] && mount -o remount,ro "${ovlpath}"
######################################################## ########################################################