Unattended installation of Alpine Linux

Posted on

Today I would like to share a quick and dirty way to perform unattended installations of Alpine Linux. I use it to spin up offline and diskless local virtual machines on OpenBSD’s built-in virtual machine monitor vmm(4) for one-off tasks such as testing or cross-compiling a piece of software. However, the method I describe is also suitable for headless installations on your favorite single board computer, and remote installations in the cloud.

The idea is to modify one of Alpine’s official ISO 9660 images to run a custom script on boot. The script feeds prepared answers to Alpine’s main installation script, setup-alpine, and performs some optional adjustments.

To begin with, download one of Alpine’s installation images from the official downloads page. I chose the virtual image because of it’s small size, but you can also use the extended image if you like.

$ curl --location --remote-name-all \
    https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/alpine-virt-3.18.4-x86_64{.iso,.iso.sha256,.iso.asc}
$ sha256 -c alpine-*.iso.sha256

If you wish to verify the PGP signature, you need to import Alpine’s PGP key first.

$ curl --location https://alpinelinux.org/keys/ncopa.asc |
    gpg --import -
$ gpg --verify alpine*.iso.asc

Next, we create a so-called overlay file. This is essentially a tarball to be extracted in the file system root. See the wiki page on Alpine’s local backup utility.

Create a directory for the overlay:

$ mkdir ovl

Enable the default boot services as described here:

$ mkdir -p ovl/etc
$ touch ovl/etc/.default_boot_services

Enable the local service. This service will run our custom installation script on boot.

$ mkdir -p ovl/etc/runlevels/default
$ ln -sf /etc/init.d/local ovl/etc/runlevels/default

Configure the APK repositories in ovl/etc/apk/repositories:

/media/cdrom/apks
https://dl-cdn.alpinelinux.org/alpine/v3.18/main
https://dl-cdn.alpinelinux.org/alpine/v3.18/community

Add our custom installation script ovl/etc/local.d/auto-setup-alpine.start. Feel free to adapt the script to your personal needs. Take care though. You cannot see the script’s output on the console.

#! /bin/sh

set -o errexit
set -o nounset

# Uncomment to shutdown on completion.
#trap 'poweroff' EXIT INT

# Close standard input.
exec 0<&-

# Run only once.
rm -f /etc/local.d/auto-setup-alpine.start
rm -f /etc/runlevels/default/local

timeout 300 setup-alpine -ef /etc/auto-setup-alpine/answers
rm -rf /etc/auto-setup-alpine

# Disable password authentication
sed -i -e 's/^root:x:/root:*:/' -e 's/^stefan:x:/stefan:*:/' /etc/passwd
sed -i -e 's/^root:[^:]*/root:*/' -e 's/^stefan:[^:]*/stefan:*/' /etc/shadow

apk update
apk upgrade

apk add man-pages mandoc mandoc-apropos docs

cat >/etc/doas.d/site.conf <<EOF
permit nopass :wheel
permit nopass keepenv root
EOF

# Uncomment for sys install.
#sed -i -e 's/relatime/noatime/' /etc/fstab

Ensure that the script is executable:

$ chmod 755 ovl/etc/local.d/auto-setup-alpine.start

Put the answers for the setup-alpine script into ovl/etc/auto-setup-alpine/answers. Feel free to adapt the answers to your personal needs. You can run setup-alpine -c answers to create a new answer file with the defaults. Or you can read the source.

KEYMAPOPTS=none
HOSTNAMEOPTS=alpine
DEVDOPTS=mdev
TIMEZONEOPTS="-z UTC"
PROXYOPTS=none
APKREPOSOPTS="-1"
SSHDOPTS=openssh
NTPOPTS="openntpd"

# Diskless
DISKOPTS=none
LBUOPTS=none
APKCACHEOPTS=none

# Admin user name and ssh key.
USEROPTS="-a -u -g audio,video,netdev stefan"
USERSSHKEY="ssh-rsa AAA... stefan@localhost"

# Contents of /etc/network/interfaces
INTERFACESOPTS="auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
"

Create the actual overlay file using GNU tar:

$ tar --owner=0 --group=0 -czf localhost.apkovl.tar.gz -C ovl .

Add the overlay file to the ISO 9660 image using GNU xorriso. Note that the prefix of the overlay file name must be the hostname of the target machine, in this case localhost.

$ xorriso \
  -indev alpine-virt-3.18.4-x86_64.iso \
  -outdev my-alpine.iso \
  -map localhost.apkovl.tar.gz /localhost.apkovl.tar.gz \
  -boot_image any replay

That’s it. You can boot off this image and await a completely unattended installation of Alpine Linux.