Unattended Boot and Install: Difference between revisions

From Alpine Linux
(Delete content. Consolidated into existing install wiki pages and files now hosted on github.)
Tag: Replaced
 
(14 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{TOC right}}
{{Delete|See the latest version on [https://github.com/macmpi/alpine-linux-headless-bootstrap github] .}}
= Introduction =
This wiki describes a modified version of the original method I posted for [https://wiki.alpinelinux.org/wiki/Raspberry_Pi_-_Headless_Installation Headless installation on a Raspberry Pi.] While the original version worked for other architectures, like x86_64, it was targeted at and posted in the Raspberry Pi section since that was my primary focus at the time. Revisions to that page by other authors removed the mention of x86_64 altogether so this version will be more generally written in the hopes that it will be easier to find and that it's applicability will be clearer.
 
Additionally, this version improves upon the original (in my opinion) by automatically logging in the root user and optionally running a shell script. This provides a means to perform unattended installations or other scripted tasks and does not require modification of the stock Alpine installation media / files.
 
The following functionality is provided:
* Automatic DHCP configuration of a network interface, either wired or wireless
* Automatic configuration of sshd with password-less root login enabled
* Automatic login of the root user on tty7
* Automatic script execution (optional)
 
This is accomplished by using an overlay file that Alpine extracts at boot. [http://www.sodface.com/repo/headless.apkovl.tar.gz Download a pre-built overlay file here.]
 
{{Note|I've removed the draft notice from the top of this page but additional testing is needed. Use at your own risk.}}
 
= Basic Use =
For Alpine supported Raspberry Pi models:
# Download the applicable [https://alpinelinux.org/downloads/ Alpine installation tarball] and extract it to an SD card formatted with a single FAT partition.
# Copy the [http://www.sodface.com/repo/headless.apkovl.tar.gz headless.apkovl.tar.gz] file to the root of the SD card.
# Optionally, create a file named '''wifi.txt''' in the root of the SD card. See below for file format.
# Optionally, create a file named '''unattended.sh''' in the root of the SD card. See below for an example.
# Boot the Pi with the SD card.
 
For other architectures (tested on various x86_64 hardware and a Qemu VM)
# Download the applicable [https://alpinelinux.org/downloads/ Alpine installation file] and write it to usb stick or other media as needed.
# Copy the [http://www.sodface.com/repo/headless.apkovl.tar.gz headless.apkovl.tar.gz] file to a second usb stick or other media separate from the installation media.
# Optionally, create a file named '''wifi.txt''' in the root of second media. See below for file format.
# Optionally, create a file named '''unattended.sh''' in the root of the second media. See below for an example.
# Boot the device with both the installation and the secondary media connected.
 
For basic interactive ssh access, only the headless.apkovl.tar.gz file is required. The '''wifi.txt''' file can be omitted if wired ethernet is used. The '''unattended.sh''' file can be omitted.
 
= Wifi Support =
Create a text file named '''wifi.txt''' with the SSID and Password on the first line of the file:
 
<code>SSID Password</code>
 
= Example Scripts =
If a shell script named '''unattended.sh''' is found in the root of the same media that contains the headless.apkovl.tar.gz file, it will be executed (sourced) by the root user after the auto login occurs on tty7. This can be used to script the installation of Alpine to local disk for example. Note too, that since network is available, the '''unattended.sh''' script can also just fetch and execute another script from the local network or internet, if available.
 
I've experimented with different installation scripts. The first is derived from the [https://gitlab.alpinelinux.org/alpine/alpine-conf/-/blob/master/setup-alpine.in setup-alpine] script. It skips the sshd and network interface setup scripts as they are already configured and running.
 
 
{{Cat|unattended.sh|
<nowiki>KEYMAPOPTS="us us"
HOSTNAMEOPTS="-n alpine-test"
TIMEZONEOPTS="-z UTC"
NTPOPTS="-c busybox"
APKREPOSOPTS="-r"
DISKOPTS="-m sys /dev/mmcblk0"
 
source /lib/libalpine.sh
setup-keymap ${KEYMAPOPTS}
setup-hostname ${HOSTNAMEOPTS}
setup-timezone ${TIMEZONEOPTS}
rc-update add networking boot
rc-update add urandom boot
for svc in acpid cron crond; do
        if rc-service --exists $svc; then
                rc-update add $svc
        fi
done
rc-service hostname restart
_dn=$(sed -n \
-e '/^domain[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
-e '/^search[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
-e '${g;p;}' /etc/resolv.conf 2>/dev/null)
_hn=$(hostname)
_hn=${_hn%%.*}
sed -i -e "s/^127\.0\.0\.1.*/127.0.0.1\t${_hn}.${_dn:-$(get_fqdn my.domain)} ${_hn} localhost.localdomain localhost/" /etc/hosts
setup-ntp ${NTPOPTS}
setup-apkrepos ${APKREPOSOPTS}
yes | setup-disk -q ${DISKOPTS}
poweroff</nowiki>
}}
 
 
The next one creates an answer file and runs setup-alpine. Note that sshd and network interface setup are included this time, even though they are both already up. It seems to work but not without complaint. More experimentation is required.
 
 
{{Cat|unattended.sh|
<nowiki>cat <<-EOF > /root/answerfile
KEYMAPOPTS="us us"
HOSTNAMEOPTS="-n alpine-test"
TIMEZONEOPTS="-z UTC"
PROXYOPTS="none"
NTPOPTS="-c busybox"
APKREPOSOPTS="-r"
SSHDOPTS="-c openssh"
DISKOPTS="-m sys /dev/vda"
INTERFACESOPTS="auto lo
iface lo inet loopback
 
auto eth0
iface eth0 inet dhcp
"
empty_root_password="true"
EOF
 
yes | setup-alpine -f /root/answerfile</nowiki>
}}
 
 
The third is like the second except without the separate answer file and using cat to set the INTERFACEOPTS variable based on the interface currently in use.
 
 
{{Cat|unattended.sh|
<nowiki>set -a
 
KEYMAPOPTS="us us"
HOSTNAMEOPTS="-n alpine-test"
TIMEZONEOPTS="-z UTC"
PROXYOPTS="none"
NTPOPTS="-c busybox"
APKREPOSOPTS="-r"
SSHDOPTS="-c openssh"
DISKOPTS="-m sys /dev/mmcblk0"
INTERFACESOPTS=$(cat /etc/network/interfaces)
empty_root_password="true"
 
yes | setup-alpine</nowiki>
}}
 
 
The above scripts need to be improved, tested, and tailored for your own use. Edits welcome!
 
= Overlay File Reference =
 
The [http://www.sodface.com/repo/headless.apkovl.tar.gz headless.apkovl.tar.gz] file has the following structure:
 
{{Cmd|└── etc
    ├── .default_boot_services
    ├── local.d
    │  └── headless.start
    └── runlevels
        └── default
            └── local -> /etc/init.d/local
}}
 
 
 
{{Cat|headless.start|
<nowiki>#!/bin/sh
 
__clean_up()
{
rc-update del local
rm /etc/local.d/headless.start
}
 
__config_net()
{
if [ ${ssid} ]
then
for dev in /sys/class/net/*
do
if [ -e "${dev}"/wireless -o -e "${dev}"/phy80211 ]
then
iface="${dev##*/}"
fi
done
apk add wpa_supplicant
 
cat <<-EOF > /etc/wpa_supplicant/wpa_supplicant.conf
ap_scan=1
autoscan=periodic:10
disable_scan_offload=1
network={
        ssid="${ssid}"
        psk="${psk}"
}
EOF
 
rc-service wpa_supplicant start
else
iface="eth0"
fi
 
cat <<-EOF > /etc/network/interfaces
auto lo
iface lo inet loopback
 
auto ${iface}
iface ${iface} inet dhcp
EOF
 
rc-service networking start
}
 
__enable_ssh()
{
setup-sshd -c openssh
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
echo "PermitEmptyPasswords yes" >> /etc/ssh/sshd_config
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
rc-service sshd restart
mv /etc/ssh/sshd_config.orig /etc/ssh/sshd_config
}
 
__login_root()
{
cat <<-EOF > /root/.profile
rm -f /root/.profile
if [ -e ${ovlpath}/unattended.sh ] && [ \$(tty) = /dev/tty7 ]
then
source ${ovlpath}/unattended.sh
fi
EOF
 
openvt -s -c 7 -- /bin/login -f root
}
 
ovlpath=$(find /media -name *.apkovl.tar.gz -exec dirname {} \;)
read ssid psk < "${ovlpath}/wifi.txt"
 
__config_net
__enable_ssh
__clean_up
__login_root</nowiki>
}}

Latest revision as of 22:23, 12 July 2022

This material is proposed for deletion ...

See the latest version on github . (Discuss)
Make sure no other pages link here and check the page's history before deleting.