From 271e8eef2d962c3493a856899a50784c32528bb8 Mon Sep 17 00:00:00 2001 From: minoplhy Date: Sun, 3 Nov 2024 22:17:34 +0700 Subject: [PATCH] alpine-initramfs-dropbear: init --- alpine-initramfs-dropbear/README.md | 111 ++ .../dropbear/unlock_disk | 29 + .../features.d/dropbear.files | 3 + .../features.d/dropbear.modules | 3 + alpine-initramfs-dropbear/initramfs-dropbear | 1055 +++++++++++++++++ 5 files changed, 1201 insertions(+) create mode 100644 alpine-initramfs-dropbear/README.md create mode 100644 alpine-initramfs-dropbear/dropbear/unlock_disk create mode 100644 alpine-initramfs-dropbear/features.d/dropbear.files create mode 100644 alpine-initramfs-dropbear/features.d/dropbear.modules create mode 100644 alpine-initramfs-dropbear/initramfs-dropbear diff --git a/alpine-initramfs-dropbear/README.md b/alpine-initramfs-dropbear/README.md new file mode 100644 index 0000000..73bb05e --- /dev/null +++ b/alpine-initramfs-dropbear/README.md @@ -0,0 +1,111 @@ +# Alpine Dropbear Initramfs +This script took a huge references from: + +* [https://github.com/Deeplerg/fork-alpine-initramfs-dropbear](https://github.com/Deeplerg/fork-alpine-initramfs-dropbear) + +* [https://github.com/mk-f/alpine-initramfs-dropbear](https://github.com/mk-f/alpine-initramfs-dropbear) + +* [https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/master/initramfs-init.in](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/master/initramfs-init.in) + +This script is modified from [alpine/mkinitfs - initramfs-init.in](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/master/initramfs-init.in) + +significant changes: + +* Add dropbear + +* Add dropbear session timer, this make typical decryption still works even if dropbear/network is broken + +Please install `dropbear` before continuing + +1. copy `dropbear/unlock_disk` to `/etc/dropbear/unlock_disk` +2. copy `authorized_keys` to `/etc/dropbear/authorized_keys` +3. copy `features.d` to /`etc/mkinitfs/features.d` + +Note: if you're using Deeplerg/mk-f scripts before don't forget to change `unlock_disk` as i modified that one too. + +### /etc/mkinitfs.conf +`features="ata base ide scsi usb virtio ext4 cryptsetup keymap dropbear network"` +* features+= `dropbear` `network` + +### /etc/update-extlinux.conf +`modules=sd-mod,usb-storage,ext4,ata_piix,virtio_net,e1000e,virtio_pci` +* if network is not working (/sys/class/net/*/address not found etc.) try adding `e1000e` or `virtio_net` `virtio_pci` + +`default_kernel_opts="cryptroot=UUID=xxx cryptdm=root quiet rootfstype=ext4 dropbear= ip=>"` +* ip= can be both static and dhcp(if supported) `ip=:::::` `ip=dhcp` + + +`update-extlinux` + +`mkinitfs -i path/to/initramfs-dropbear ` + +## Full Diff: +```diff +325a326,367 +> setup_dropbear() { +> local port="${KOPT_dropbear}" +> local keys="" +> +> # set the unlock_disc script as shell for root +> sed -i 's|\(root:x:0:0:root:/root:\).*$|\1/etc/dropbear/unlock_disk|' /etc/passwd +> echo '/etc/dropbear/unlock_disk' > /etc/shells +> +> # transfer authorized_keys +> mkdir /root/.ssh +> cp /etc/dropbear/authorized_keys /root/.ssh/authorized_keys +> +> dropbear -R -E -s -j -k -p $port +> +> # [ -b /dev/mapper/${KOPT_cryptdm} ] +> #|| return 1 +> } +> +> # A simple timer that do nothing but prevent any process to run +> setup_dropbear_timer() { +> timer=200 +> while [ $timer -gt 0 ]; do +> printf "\r%d Press 'c' to cancel or 'p' to add 30 seconds " "$timer" +> +> if read -t 1 -r timer_control; then +> case $timer_control in +> "c") return 0 ;; +> "p") timer=$((timer + 30)) ;; +> esac +> fi +> +> # Check for /tmp/timer_kill to terminate this counter +> if [ -f /tmp/timer_kill ]; then +> return 0 +> fi +> +> sleep 1 +> timer=$((timer - 1)) +> done +> printf "\n" +> } +> +453c495 +< s390x_net dasd ssh_key BOOTIF zfcp uevent_buf_size aoe aoe_iflist aoe_mtu wireguard" +--- +> s390x_net dasd ssh_key BOOTIF zfcp uevent_buf_size aoe aoe_iflist aoe_mtu wireguard dropbear" +581c623,633 +< if [ -n "$KOPT_cryptroot" ]; then +--- +> if [ -n "$KOPT_dropbear" ]; then +> if [ -n "$KOPT_cryptroot" ]; then +> configure_ip +> setup_dropbear +> setup_dropbear_timer +> #|| echo "Failed to setup dropbear" +> fi +> fi +> +> # Add Workaround for dropbear +> if [ -n "$KOPT_cryptroot" ] && [ ! -b /dev/mapper/"${KOPT_cryptdm}" ]; then +1003c1055 +< reboot +--- +> reboot +\ No newline at end of file + +``` \ No newline at end of file diff --git a/alpine-initramfs-dropbear/dropbear/unlock_disk b/alpine-initramfs-dropbear/dropbear/unlock_disk new file mode 100644 index 0000000..bf2db55 --- /dev/null +++ b/alpine-initramfs-dropbear/dropbear/unlock_disk @@ -0,0 +1,29 @@ +#!/bin/sh + +set -- $(cat /proc/cmdline) + +for opt; do + case "$opt" in + cryptroot=*) + KOPT_cryptroot=${opt#cryptroot=} + continue + ;; + cryptdm=*) + KOPT_cryptdm=${opt#cryptdm=} + continue + ;; + root=*) + KOPT_root=${opt#root=} + continue + ;; + esac +done + +while [ ! -b /dev/mapper/${KOPT_cryptdm} ]; do + /sbin/nlplug-findfs -c ${KOPT_cryptroot} -m ${KOPT_cryptdm} ${KOPT_debug_init:+-d} -p /sbin/mdev ${KOPT_root} + sleep 2 +done + +# make a new file to kill the timer +echo "" > /tmp/timer_kill +killall -9 dropbear \ No newline at end of file diff --git a/alpine-initramfs-dropbear/features.d/dropbear.files b/alpine-initramfs-dropbear/features.d/dropbear.files new file mode 100644 index 0000000..6644349 --- /dev/null +++ b/alpine-initramfs-dropbear/features.d/dropbear.files @@ -0,0 +1,3 @@ +/usr/sbin/dropbear +/sbin/cryptsetup +/etc/dropbear/* \ No newline at end of file diff --git a/alpine-initramfs-dropbear/features.d/dropbear.modules b/alpine-initramfs-dropbear/features.d/dropbear.modules new file mode 100644 index 0000000..6c771f1 --- /dev/null +++ b/alpine-initramfs-dropbear/features.d/dropbear.modules @@ -0,0 +1,3 @@ +kernel/crypto/* +kernel/arch/*/crypto/* +kernel/drivers/md/dm-crypt.ko \ No newline at end of file diff --git a/alpine-initramfs-dropbear/initramfs-dropbear b/alpine-initramfs-dropbear/initramfs-dropbear new file mode 100644 index 0000000..cd0448c --- /dev/null +++ b/alpine-initramfs-dropbear/initramfs-dropbear @@ -0,0 +1,1055 @@ +#!/bin/sh + +# this is the init script version +VERSION=@VERSION@ +SINGLEMODE=no +sysroot="$ROOT"/sysroot +splashfile=/.splash.ctrl +repofile="$ROOT"/tmp/repositories + +# some helpers +ebegin() { + last_emsg="$*" + echo "$last_emsg..." > "$ROOT"/dev/kmsg + [ "$KOPT_quiet" = yes ] && return 0 + echo -n " * $last_emsg: " +} +eend() { + local msg + if [ "$1" = 0 ] || [ $# -lt 1 ] ; then + echo "$last_emsg: ok." > "$ROOT"/dev/kmsg + [ "$KOPT_quiet" = yes ] && return 0 + echo "ok." + else + shift + echo "$last_emsg: failed. $*" > "$ROOT"/dev/kmsg + if [ "$KOPT_quiet" = "yes" ]; then + echo -n "$last_emsg " + fi + echo "failed. $*" + echo "initramfs emergency recovery shell launched. Type 'exit' to continue boot" + /bin/busybox sh + fi +} + +unpack_apkovl() { + local ovl="$1" + local dest="$2" + local suffix=${ovl##*.} + local i + ovlfiles=/tmp/ovlfiles + if [ "$suffix" = "gz" ]; then + tar -C "$dest" -zxvf "$ovl" > $ovlfiles + return $? + fi + + # we need openssl. let apk handle deps + apk add --quiet --initdb --repositories-file $repofile openssl || return 1 + + if ! openssl list -1 -cipher-commands | grep "^$suffix$" > /dev/null; then + errstr="Cipher $suffix is not supported" + return 1 + fi + local count=0 + # beep + echo -e "\007" + while [ $count -lt 3 ]; do + openssl enc -d -$suffix -in "$ovl" | tar --numeric-owner \ + -C "$dest" -zxv >$ovlfiles 2>/dev/null && return 0 + count=$(( $count + 1 )) + done + ovlfiles= + return 1 +} + +# find mount dir and mount opts for given device in an fstab +get_fstab_mount_info() { + local search_dev="$1" + local fstab="$2" + local mntopts= + case "$search_dev" in + UUID*|LABEL*) search_dev=$(findfs "$search_dev");; + esac + [ -r "$fstab" ] || return 1 + local search_maj_min=$(stat -L -c '%t,%T' $search_dev) + while read dev mnt fs mntopts chk; do + case "$dev" in + UUID*|LABEL*) dev=$(findfs "$dev");; + esac + if [ -b "$dev" ] || [ -n "$ROOT" ]; then + local maj_min=$(stat -L -c '%t,%T' $dev) + if [ "$maj_min" = "$search_maj_min" ]; then + echo "$mnt $mntopts" + return + fi + fi + done < $fstab +} + +# add a boot service to $sysroot +rc_add() { + mkdir -p $sysroot/etc/runlevels/$2 + ln -sf /etc/init.d/$1 $sysroot/etc/runlevels/$2/$1 +} + +# Recursively resolve tty aliases like console or tty0 +list_console_devices() { + if ! [ -e "$ROOT"/sys/class/tty/$1/active ]; then + echo $1 + return + fi + + for dev in $(cat "$ROOT"/sys/class/tty/$1/active); do + list_console_devices $dev + done +} + +detect_serial_consoles() { + if [ -f "$ovl" ] || [ "$KOPT_autodetect_serial" = "no" ]; then + return + fi + local n=$(awk '$7 ~ /CTS/ || $7 ~ /DSR/ { print $1 }' "$ROOT"/proc/tty/driver/serial 2>/dev/null) + if [ -n "$n" ]; then + echo ttyS${n%:} + fi + for i in "$ROOT"/sys/class/tty/*; do + if [ -e "$i"/device ]; then + echo ${i##*/} + fi + done +} + +setup_inittab_console() { + term=vt100 + # Inquire the kernel for list of console= devices + consoles="$(for c in console $KOPT_consoles $(detect_serial_consoles); do list_console_devices $c; done)" + for tty in $consoles; do + # ignore tty devices that gives I/O error + if ! stty -g -F /dev/$tty >/dev/null 2>/dev/null; then + continue + fi + # do nothing if inittab already have the tty set up + if ! grep -q "^$tty:" $sysroot/etc/inittab 2>/dev/null; then + echo "# enable login on alternative console" \ + >> $sysroot/etc/inittab + # Baudrate of 0 keeps settings from kernel + echo "$tty::respawn:/sbin/getty -L 0 $tty $term" \ + >> $sysroot/etc/inittab + fi + if [ -e "$sysroot"/etc/securetty ] && ! grep -q -w "$tty" "$sysroot"/etc/securetty; then + echo "$tty" >> "$sysroot"/etc/securetty + fi + done +} + +# collect ethernet interfaces, sorted by index +ethernets() { + for i in "$ROOT/sys/class/net/"*; do + local iface="${i##*/}" + if [ -d "$i/device" ]; then + echo "$(cat "$i/ifindex") $iface" + fi + done | sort -n | awk '{print $2}' +} + +# find the interface that is has operstate up +find_first_interface_up() { + local n=0 + [ $# -eq 0 ] && return 0 + while [ "$n" -le "${LINK_WAIT_MAX:-40}" ]; do + for i in "$@"; do + if grep -q -F -x "up" "$ROOT/sys/class/net/$i/operstate"; then + echo "$i" + return + fi + done + sleep 0.1 + n=$((n+1)) + done + return 1 +} + +# if "ip=dhcp" is specified on the command line, we obtain an IP address +# using udhcpc. we do this now and not by enabling kernel-mode DHCP because +# kernel-model DHCP appears to require that network drivers be built into +# the kernel rather than as modules. At this point all applicable modules +# in the initrd should have been loaded. +# +# You need af_packet.ko available as well modules for your Ethernet card. +# +# See https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt +# for documentation on the format. +# +# Valid syntaxes: +# ip=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf: +# :dns0-ip:dns1-ip:ntp0-ip +# ip=dhcp +# "server-ip", "hostname" and "ntp0-ip" are not supported here. +# Default (when configure_ip is called without setting ip=): +# ip=dhcp +# +configure_ip() { + [ -n "$MAC_ADDRESS" ] && return + + local IFS=':' + # shellcheck disable=SC2086 + set -- ${KOPT_ip:-dhcp} + unset IFS + + local client_ip="$1" + local gw_ip="$3" + local netmask="$4" + local iface="$6" + local autoconf="$7" + local dns1="$8" + local dns2="$9" + + case "$client_ip" in + off|none) return;; + dhcp) autoconf="dhcp";; + esac + + if [ -e "$ROOT"/etc/mactab ]; then + $MOCK nameif -s + fi + + if [ -z "$iface" ] && [ -n "$KOPT_BOOTIF" ]; then + mac=$(printf "%s\n" "$KOPT_BOOTIF"|sed 's/^01-//;s/-/:/g') + iface=$(grep -l "$mac" "$ROOT"/sys/class/net/*/address | awk -F/ '{print $(NF-1); exit}') + fi + + if [ -z "$iface" ]; then + # shellcheck disable=SC2046 + set -- $(ethernets) + for i in "$@"; do + $MOCK ip link set dev "$i" up + done + iface=$(find_first_interface_up "$@") || iface="$1" + + # we will use the found interface later so lets keep it up + for i in "$@"; do + if [ "$i" != "$iface" ]; then + $MOCK ip link set dev "$i" down + fi + done + fi + + if [ -z "$iface" ]; then + echo "ERROR: IP requested but no network interface was found" + return 1 + fi + + if [ "$autoconf" = "dhcp" ]; then + # automatic configuration + if [ ! -e "$ROOT"/usr/share/udhcpc/default.script ]; then + echo "ERROR: DHCP requested but not present in initrd" + return 1 + fi + ebegin "Obtaining IP via DHCP ($iface)" + $MOCK ip link set dev "$iface" up + $MOCK udhcpc -i "$iface" -f -q + eend $? + else + # manual configuration + if [ -z "$client_ip" ] && [ -z "$netmask" ]; then + return + fi + ebegin "Setting IP ($iface)" + if ifconfig "$iface" "$client_ip" netmask "$netmask"; then + [ -z "$gw_ip" ] || ip route add 0.0.0.0/0 via "$gw_ip" dev "$iface" + fi + eend $? + fi + + # Never executes if variables are empty + for i in $dns1 $dns2; do + echo "nameserver $i" >> /etc/resolv.conf + done + + MAC_ADDRESS=$(cat "$ROOT/sys/class/net/$iface/address") +} + +# relocate mountpoint according given fstab and set mount options +remount_fstab_entry() { + local fstab="${1}" + local dir= + if ! [ -e "$repofile" ]; then + return + fi + echo "$ovl" | cat - "$repofile" | while read dir; do + # skip http(s)/ftp repos for netboot + if [ -z "$dir" ] || ! [ -d "$ROOT/$dir" -o -f "$ROOT/$dir" ]; then + continue + fi + local dev=$(df -P "$dir" | tail -1 | awk '{print $1}') + local mntinfo="$(get_fstab_mount_info "$dev" "$fstab")" + local mnt="${mntinfo% *}" + local mntopts="${mntinfo#* }" + if [ -n "$mnt" ]; then + local oldmnt=$(awk -v d=$ROOT$dev '$1==d {print $2}' "$ROOT"/proc/mounts 2>/dev/null) + if [ "$oldmnt" != "$mnt" ]; then + mkdir -p "$mnt" + $MOCK mount -o move "$oldmnt" "$mnt" + fi + if [ -n "$mntopts" ]; then + $MOCK mount -o remount,"$mntopts" "$mnt" + fi + fi + done +} + +# find the dirs under ALPINE_MNT that are boot repositories +find_boot_repositories() { + if [ -n "$ALPINE_REPO" ]; then + echo "$ALPINE_REPO" | tr ',' '\n' + else + find /media/* -maxdepth 3 -name .boot_repository -type f \ + | sed 's:/.boot_repository$::' + fi +} + +setup_nbd() { + $MOCK modprobe -q nbd max_part=8 || return 1 + local IFS=, n=0 + set -- $KOPT_nbd + unset IFS + for ops; do + local server="${ops%:*}" + local port="${ops#*:}" + local device="/dev/nbd${n}" + [ -b "$device" ] || continue + nbd-client "$server" "$port" "$device" && n=$((n+1)) + done + [ "$n" != 0 ] || return 1 +} + +setup_dropbear() { + local port="${KOPT_dropbear}" + local keys="" + + # set the unlock_disc script as shell for root + sed -i 's|\(root:x:0:0:root:/root:\).*$|\1/etc/dropbear/unlock_disk|' /etc/passwd + echo '/etc/dropbear/unlock_disk' > /etc/shells + + # transfer authorized_keys + mkdir /root/.ssh + cp /etc/dropbear/authorized_keys /root/.ssh/authorized_keys + + dropbear -R -E -s -j -k -p $port + + # [ -b /dev/mapper/${KOPT_cryptdm} ] + #|| return 1 +} + +# A simple timer that do nothing but prevent any process to run +setup_dropbear_timer() { + timer=200 + while [ $timer -gt 0 ]; do + printf "\r%d Press 'c' to cancel or 'p' to add 30 seconds " "$timer" + + if read -t 1 -r timer_control; then + case $timer_control in + "c") return 0 ;; + "p") timer=$((timer + 30)) ;; + esac + fi + + # Check for /tmp/timer_kill to terminate this counter + if [ -f /tmp/timer_kill ]; then + return 0 + fi + + sleep 1 + timer=$((timer - 1)) + done + printf "\n" +} + +setup_wireguard() { + $MOCK modprobe -q wireguard || return 1 + local IFS=';' + set -- $KOPT_wireguard + unset IFS + + local device="$1" + local ips="$2" + local config="${3:-/etc/wireguard/initrd.conf}" + + local IFS=',' + set -- $ips + unset IFS + + $MOCK ip link add "$device" type wireguard + $MOCK wg setconf "$device" "$config" + $MOCK ip link set dev "$device" up + + for addr in $@; do + $MOCK ip addr add dev "$device" "$addr" + done +} + +rtc_exists() { + local rtc= + for rtc in /dev/rtc /dev/rtc[0-9]*; do + [ -e "$rtc" ] && break + done + [ -e "$rtc" ] +} + +# This is used to predict if network access will be necessary +is_url() { + case "$1" in + http://*|https://*|ftp://*) + return 0;; + *) + return 1;; + esac +} + +# Do some tasks to make sure mounting the ZFS pool is A-OK +prepare_zfs_root() { + local _root_vol=${KOPT_root#ZFS=} + local _root_pool=${_root_vol%%/*} + + # Force import if this has been imported on a different system previously. + # Import normally otherwise + if [ "$KOPT_zfs_force" = 1 ]; then + zpool import -N -d /dev -f $_root_pool + else + zpool import -N -d /dev $_root_pool + fi + + + # Ask for encryption password + if [ $(zpool list -H -o feature@encryption $_root_pool) = "active" ]; then + local _encryption_root=$(zfs get -H -o value encryptionroot $_root_vol) + if [ "$_encryption_root" != "-" ]; then + eval zfs load-key $_encryption_root + fi + fi +} + +want_tiny_cloud() { + if [ -f "$sysroot/etc/tiny-cloud.disabled" ]; then + return 1 + fi + case "$KOPT_ds" in + nocloud*) return 0;; + esac + if grep -q "^ds=nocloud" "$ROOT"/sys/class/dmi/id/product_serial 2>/dev/null; then + return 0 + fi + findfs LABEL=cidata >/dev/null 2>&1 || findfs LABEL=CIDATA >/dev/null 2>&1 +} + +/bin/busybox mkdir -p "$ROOT"/usr/bin \ + "$ROOT"/usr/sbin \ + "$ROOT"/proc \ + "$ROOT"/sys \ + "$ROOT"/dev \ + "$sysroot" \ + "$ROOT"/media/cdrom \ + "$ROOT"/media/usb \ + "$ROOT"/tmp \ + "$ROOT"/etc \ + "$ROOT"/run/cryptsetup + +# Spread out busybox symlinks and make them available without full path +/bin/busybox --install -s +export PATH="$PATH:/usr/bin:/bin:/usr/sbin:/sbin" + +# Make sure /dev/null is a device node. If /dev/null does not exist yet, the command +# mounting the devtmpfs will create it implicitly as an file with the "2>" redirection. +# The -c check is required to deal with initramfs with pre-seeded device nodes without +# error message. +[ -c /dev/null ] || $MOCK mknod -m 666 /dev/null c 1 3 + +$MOCK mount -t sysfs -o noexec,nosuid,nodev sysfs /sys +$MOCK mount -t devtmpfs -o exec,nosuid,mode=0755,size=2M devtmpfs /dev 2>/dev/null \ + || $MOCK mount -t tmpfs -o exec,nosuid,mode=0755,size=2M tmpfs /dev + +# Make sure /dev/kmsg is a device node. Writing to /dev/kmsg allows the use of the +# earlyprintk kernel option to monitor early init progress. As above, the -c check +# prevents an error if the device node has already been seeded. +[ -c /dev/kmsg ] || $MOCK mknod -m 660 /dev/kmsg c 1 11 + +$MOCK mount -t proc -o noexec,nosuid,nodev proc /proc +# pty device nodes (later system will need it) +[ -c /dev/ptmx ] || $MOCK mknod -m 666 /dev/ptmx c 5 2 +[ -d /dev/pts ] || $MOCK mkdir -m 755 /dev/pts +$MOCK mount -t devpts -o gid=5,mode=0620,noexec,nosuid devpts /dev/pts + +# shared memory area (later system will need it) +mkdir -p "$ROOT"/dev/shm +$MOCK mount -t tmpfs -o nodev,nosuid,noexec shm /dev/shm + + +# read the kernel options. we need surve things like: +# acpi_osi="!Windows 2006" xen-pciback.hide=(01:00.0) +set -- $(cat "$ROOT"/proc/cmdline) + +myopts="autodetect_serial chart cryptroot cryptdm cryptheader cryptoffset + cryptdiscards cryptkey debug_init ds init init_args keep_apk_new modules + pkgs quiet root_size root usbdelay ip alpine_repo apkovl splash + blacklist overlaytmpfs overlaytmpfsflags rootfstype rootflags nbd resume resume_offset + s390x_net dasd ssh_key BOOTIF zfcp uevent_buf_size aoe aoe_iflist aoe_mtu wireguard dropbear" + +for opt; do + case "$opt" in + s|single|1) + SINGLEMODE=yes + continue + ;; + console=*) + opt="${opt#*=}" + KOPT_consoles="${opt%%,*} $KOPT_consoles" + switch_root_opts="-c /dev/${opt%%,*}" + continue + ;; + esac + + for i in $myopts; do + case "$opt" in + $i=*) eval "KOPT_${i}"='${opt#*=}';; + $i) eval "KOPT_${i}=yes";; + no$i) eval "KOPT_${i}=no";; + esac + done +done + +echo "Alpine Init $VERSION" > "$ROOT"/dev/kmsg +[ "$KOPT_quiet" = yes ] || echo "Alpine Init $VERSION" + +# enable debugging if requested +[ -n "$KOPT_debug_init" ] && set -x + +# set default values +: ${KOPT_init:=/sbin/init} + +# pick first keymap if found +for map in "$ROOT"/etc/keymap/*; do + if [ -f "$map" ]; then + ebegin "Setting keymap ${map##*/}" + zcat "$map" | loadkmap + eend + break + fi +done + +# start bootcharting if wanted +if [ "$KOPT_chart" = yes ]; then + ebegin "Starting bootchart logging" + $MOCK /sbin/bootchartd start-initfs + eend 0 +fi + +# The following values are supported: +# alpine_repo=auto -- default, search for .boot_repository +# alpine_repo=http://... -- network repository +ALPINE_REPO=${KOPT_alpine_repo} +[ "$ALPINE_REPO" = "auto" ] && ALPINE_REPO= + +# hide kernel messages +[ "$KOPT_quiet" = yes ] && dmesg -n 1 + +# optional blacklist +if [ -n "$KOPT_blacklist" ]; then + mkdir -p "$ROOT"/etc/modprobe.d + for i in $(echo "$KOPT_blacklist" | tr ',' ' '); do + echo "blacklist $i" >> "$ROOT"/etc/modprobe.d/boot-opt-blacklist.conf + done +fi + +# determine if we are going to need networking +if [ -n "$KOPT_ip" ] || [ -n "$KOPT_nbd" ] || \ + is_url "$KOPT_apkovl" || is_url "$ALPINE_REPO"; then + + do_networking=true +else + do_networking=false +fi + +if [ -n "$KOPT_zfcp" ]; then + $MOCK modprobe zfcp + for _zfcp in $(echo "$KOPT_zfcp" | tr ',' ' ' | tr [A-Z] [a-z]); do + echo 1 > /sys/bus/ccw/devices/"${_zfcp%%:*}"/online + done +fi + +if [ -n "$KOPT_dasd" ]; then + for mod in dasd_mod dasd_eckd_mod dasd_fba_mod; do + $MOCK modprobe $mod + done + for _dasd in $(echo "$KOPT_dasd" | tr ',' ' ' | tr [A-Z] [a-z]); do + echo 1 > /sys/bus/ccw/devices/"${_dasd%%:*}"/online + done +fi + +if [ "${KOPT_s390x_net%%,*}" = "qeth_l2" ]; then + for mod in qeth qeth_l2 qeth_l3; do + $MOCK modprobe $mod + done + _channel="$(echo ${KOPT_s390x_net#*,} | tr [A-Z] [a-z])" + echo "$_channel" > /sys/bus/ccwgroup/drivers/qeth/group + echo 1 > /sys/bus/ccwgroup/drivers/qeth/"${_channel%%,*}"/layer2 + echo 1 > /sys/bus/ccwgroup/drivers/qeth/"${_channel%%,*}"/online +fi + +# make sure we load zfs module if root=ZFS=... +rootfstype=${KOPT_rootfstype} +if [ -z "$rootfstype" ]; then + case "$KOPT_root" in + ZFS=*) rootfstype=zfs ;; + esac +fi + +# load available drivers to get access to modloop media +ebegin "Loading boot drivers" + +$MOCK modprobe -a $(echo "$KOPT_modules $rootfstype" | tr ',' ' ' ) loop squashfs simpledrm 2> /dev/null +if [ -f "$ROOT"/etc/modules ] ; then + sed 's/\#.*//g' < /etc/modules | + while read module args; do + $MOCK modprobe -q $module $args + done +fi +eend 0 + +# workaround for vmware +if grep -q VMware "$ROOT"/sys/devices/virtual/dmi/id/sys_vendor 2>/dev/null; then + $MOCK modprobe -a ata_piix mptspi sr-mod +fi + +if [ -n "$KOPT_dropbear" ]; then + if [ -n "$KOPT_cryptroot" ]; then + configure_ip + setup_dropbear + setup_dropbear_timer + #|| echo "Failed to setup dropbear" + fi +fi + +# Add Workaround for dropbear +if [ -n "$KOPT_cryptroot" ] && [ ! -b /dev/mapper/"${KOPT_cryptdm}" ]; then + cryptopts="-c ${KOPT_cryptroot}" + if [ "$KOPT_cryptdiscards" = "yes" ]; then + cryptopts="$cryptopts -D" + fi + if [ -n "$KOPT_cryptdm" ]; then + cryptopts="$cryptopts -m ${KOPT_cryptdm}" + fi + if [ -n "$KOPT_cryptheader" ]; then + cryptopts="$cryptopts -H ${KOPT_cryptheader}" + fi + if [ -n "$KOPT_cryptoffset" ]; then + cryptopts="$cryptopts -o ${KOPT_cryptoffset}" + fi + if [ "$KOPT_cryptkey" = "yes" ]; then + cryptopts="$cryptopts -k /crypto_keyfile.bin" + elif [ -n "$KOPT_cryptkey" ]; then + cryptopts="$cryptopts -k ${KOPT_cryptkey}" + fi +fi + +if [ -n "$KOPT_wireguard" ]; then + configure_ip + setup_wireguard || echo "Failed to setup wireguard tunnel." +fi + +if [ -n "$KOPT_nbd" ]; then + # TODO: Might fail because nlplug-findfs hasn't plugged eth0 yet + configure_ip + setup_nbd || echo "Failed to setup nbd device." +fi + +if [ -n "$KOPT_aoe" ]; then + if [ -n "$KOPT_aoe_iflist" ]; then + for iface in $(echo "$KOPT_aoe_iflist" | tr ',' ' '); do + $MOCK ip link set dev "$iface" up + if [ -n "$KOPT_aoe_mtu" ]; then + $MOCK ip link set dev "$iface" mtu "$KOPT_aoe_mtu" + fi + done + $MOCK modprobe aoe aoe_iflist="$KOPT_aoe_iflist" + else + $MOCK modprobe aoe + fi + if [ "$KOPT_aoe" != "yes" ]; then + for target in $(echo "$KOPT_aoe" | tr ',' ' '); do + while [ ! -e /dev/etherd/e$target ]; do + echo discover "$target" >>/dev/etherd/discover + sleep 1 + done + done + fi +fi + +# zpool reports /dev/zfs missing if it can't read /etc/mtab +ln -s /proc/mounts "$ROOT"/etc/mtab + +# check if root=... was set +if [ -n "$KOPT_root" ]; then + # run nlplug-findfs before SINGLEMODE so we load keyboard drivers + ebegin "Mounting root" + $MOCK nlplug-findfs $cryptopts -p /sbin/mdev ${KOPT_debug_init:+-d} \ + ${KOPT_uevent_buf_size:+-U $KOPT_uevent_buf_size} \ + $KOPT_root + + if [ "$SINGLEMODE" = "yes" ]; then + echo "Entering single mode. Type 'exit' to continue booting." + sh + fi + + if echo "$KOPT_modules $rootfstype" | grep -qw btrfs; then + /sbin/btrfs device scan >/dev/null || \ + echo "Failed to scan devices for btrfs filesystem." + fi + + if [ -n "$KOPT_resume" ]; then + echo "Resume from disk" + if [ -e $ROOT/sys/power/resume ]; then + case "$KOPT_resume" in + UUID*|LABEL*) resume_dev=$(findfs "$KOPT_resume");; + *) resume_dev="$KOPT_resume";; + esac + printf "%d:%d" $(stat -Lc "0x%t 0x%T" "$resume_dev") > $ROOT/sys/power/resume + if [ -n "$KOPT_resume_offset" ]; then + echo "$KOPT_resume_offset" > $ROOT/sys/power/resume_offset + fi + else + echo "resume: no hibernation support found" + fi + fi + + if [ "$KOPT_overlaytmpfs" = "yes" ]; then + # Create mountpoints + mkdir -p /media/root-ro /media/root-rw $sysroot/media/root-ro \ + $sysroot/media/root-rw + # Mount read-only underlying rootfs + rootflags="${KOPT_rootflags:+$KOPT_rootflags,}ro" + $MOCK mount ${KOPT_rootfstype:+-t $KOPT_rootfstype} -o $rootflags \ + $KOPT_root /media/root-ro + # Mount writable overlay tmpfs + overlaytmpfsflags="mode=0755,${KOPT_overlaytmpfsflags:+$KOPT_overlaytmpfsflags,}rw" + $MOCK mount -t tmpfs -o $overlaytmpfsflags root-tmpfs /media/root-rw + # Create additional mountpoints and do the overlay mount + mkdir -p /media/root-rw/work /media/root-rw/root + $MOCK mount -t overlay -o \ + lowerdir=/media/root-ro,upperdir=/media/root-rw/root,workdir=/media/root-rw/work \ + overlayfs $sysroot + else + if [ "$rootfstype" = "zfs" ]; then + prepare_zfs_root + fi + $MOCK mount ${rootfstype:+-t} ${rootfstype} \ + -o ${KOPT_rootflags:-ro} \ + ${KOPT_root#ZFS=} $sysroot + fi + + eend $? + + if [ -r "$sysroot/etc/fstab" ]; then + while read dev mnt fs mntopts chk; do + if [ "$mnt" = "/usr" ]; then + ebegin "Mounting /usr" + $MOCK modprobe -a $fs 2> /dev/null + $MOCK mount -t $fs -o $mntopts $dev $sysroot/usr + eend $? + fi + done < $sysroot/etc/fstab + fi + + cat "$ROOT"/proc/mounts 2>/dev/null | while read DEV DIR TYPE OPTS ; do + if [ "$DIR" != "/" -a "$DIR" != "$sysroot" -a -d "$DIR" ]; then + mkdir -p $sysroot/$DIR + $MOCK mount -o move $DIR $sysroot/$DIR + fi + done + $MOCK sync + exec switch_root $switch_root_opts $sysroot $chart_init "$KOPT_init" $KOPT_init_args + echo "initramfs emergency recovery shell launched" + exec /bin/busybox sh +fi + +if $do_networking; then + repoopts="-n" +else + repoopts="-b $repofile" +fi + +# locate boot media and mount it +ebegin "Mounting boot media" +$MOCK nlplug-findfs $cryptopts -p /sbin/mdev ${KOPT_debug_init:+-d} \ + ${KOPT_usbdelay:+-t $(( $KOPT_usbdelay * 1000 ))} \ + ${KOPT_uevent_buf_size:+-U $KOPT_uevent_buf_size} \ + $repoopts -a "$ROOT"/tmp/apkovls +eend $? + +# Setup network interfaces +if $do_networking; then + configure_ip +fi + +# early console? +if [ "$SINGLEMODE" = "yes" ]; then + echo "Entering single mode. Type 'exit' to continue booting." + sh +fi + +# mount tmpfs sysroot +rootflags="mode=0755" +if [ -n "$KOPT_root_size" ]; then + echo "WARNING: the boot option root_size is deprecated. Use rootflags instead" + rootflags="$rootflags,size=$KOPT_root_size" +fi +if [ -n "$KOPT_rootflags" ]; then + rootflags="$rootflags,$KOPT_rootflags" +fi + +$MOCK mount -t tmpfs -o $rootflags tmpfs $sysroot + +if [ -z "$KOPT_apkovl" ]; then + # Not manually set, use the apkovl found by nlplug + if [ -e "$ROOT"/tmp/apkovls ]; then + ovl=$(head -n 1 "$ROOT"/tmp/apkovls) + fi +elif is_url "$KOPT_apkovl"; then + # Fetch apkovl via network + MACHINE_UUID=$(cat "$ROOT"/sys/class/dmi/id/product_uuid 2>/dev/null) + url="$(echo "$KOPT_apkovl" | sed -e "s/{MAC}/$MAC_ADDRESS/" -e "s/{UUID}/$MACHINE_UUID/")" + ovl="/tmp/${url##*/}" + ovl="${ovl%%\?*}" + $MOCK wget -O "$ovl" "$url" || ovl= +else + ovl="$KOPT_apkovl" +fi + +# parse pkgs=pkg1,pkg2 +if [ -n "$KOPT_pkgs" ]; then + pkgs=$(echo "$KOPT_pkgs" | tr ',' ' ' ) +fi + +# load apkovl or set up a minimal system +if [ -f "$ovl" ]; then + ebegin "Loading user settings from $ovl" + # create apk db and needed /dev/null and /tmp first + apk add --root $sysroot --initdb --quiet + + unpack_apkovl "$ovl" $sysroot + eend $? $errstr || ovlfiles= + # hack, incase /root/.ssh was included in apkovl + [ -d "$sysroot/root" ] && chmod 700 "$sysroot/root" + pkgs="$pkgs $(cat $sysroot/etc/apk/world 2>/dev/null)" +fi + +if [ -f "$sysroot/etc/.default_boot_services" -o ! -f "$ovl" ]; then + # add some boot services by default + rc_add devfs sysinit + rc_add dmesg sysinit + rc_add mdev sysinit + rc_add hwdrivers sysinit + rc_add modloop sysinit + + rc_add modules boot + rc_add sysctl boot + rc_add hostname boot + rc_add bootmisc boot + rc_add syslog boot + + rc_add mount-ro shutdown + rc_add killprocs shutdown + rc_add savecache shutdown + + rc_add firstboot default + + # add openssh + if [ -n "$KOPT_ssh_key" ]; then + pkgs="$pkgs openssh" + rc_add sshd default + fi + + if want_tiny_cloud; then + pkgs="$pkgs tiny-cloud-alpine ifupdown-ng doas" + rc_add tiny-cloud-boot boot + rc_add tiny-cloud-early default + rc_add tiny-cloud-main default + rc_add tiny-cloud-final default + fi + + rm -f "$sysroot/etc/.default_boot_services" +fi + +if [ "$KOPT_splash" != "no" ]; then + echo "IMG_ALIGN=CM" > /tmp/fbsplash.cfg + for fbdev in /dev/fb[0-9]; do + [ -e "$fbdev" ] || break + num="${fbdev#/dev/fb}" + for img in /media/*/fbsplash$num.ppm; do + [ -e "$img" ] || break + config="${img%.*}.cfg" + [ -e "$config" ] || config=/tmp/fbsplash.cfg + fbsplash -s "$img" -d "$fbdev" -i "$config" + break + done + done + for fbsplash in /media/*/fbsplash.ppm; do + [ -e "$fbsplash" ] && break + done +fi + +if [ -n "$fbsplash" ] && [ -e "$fbsplash" ]; then + ebegin "Starting bootsplash" + mkfifo $sysroot/$splashfile + config="${fbsplash%.*}.cfg" + [ -e "$config" ] || config=/tmp/fbsplash.cfg + setsid fbsplash -T 16 -s "$fbsplash" -i $config -f $sysroot/$splashfile & + eend 0 +else + KOPT_splash="no" +fi + +if [ -f $sysroot/etc/fstab ]; then + has_fstab=1 + fstab=$sysroot/etc/fstab + + # let user override tmpfs size in fstab in apkovl + mountopts=$(awk '$2 == "/" && $3 == "tmpfs" { print $4 }' $sysroot/etc/fstab) + if [ -n "$mountopts" ]; then + $MOCK mount -o remount,$mountopts $sysroot + fi + # relocate mounts and adjust mount options + # this is so a generated /etc/apk/repositories will use correct + # mount dir + remount_fstab_entry "$sysroot"/etc/fstab +elif [ -f "$ROOT"/etc/fstab ]; then + remount_fstab_entry "$ROOT"/etc/fstab +fi + +# hack so we get openrc +pkgs="$pkgs alpine-base" + +# copy keys so apk finds them. apk looks for stuff relative --root +mkdir -p $sysroot/etc/apk/keys/ +$MOCK cp -a /etc/apk/keys $sysroot/etc/apk + +# generate apk repositories file. needs to be done after relocation +find_boot_repositories > $repofile + +# silently fix apk arch in case the apkovl does not match +if [ -r "$sysroot"/etc/apk/arch ]; then + apk_arch="$(apk --print-arch)" + if [ -n "$apk_arch" ]; then + echo "$apk_arch" > "$sysroot"/etc/apk/arch + fi +fi + +# generate repo opts for apk +for i in $(cat $repofile); do + repo_opt="$repo_opt --repository $i" +done + +# install new root +ebegin "Installing packages to root filesystem" + +if [ "$KOPT_chart" = yes ]; then + pkgs="$pkgs acct" +fi + +# use swclock if no RTC is found +if rtc_exists || [ "$(uname -m)" = "s390x" ]; then + rc_add hwclock boot +else + rc_add swclock boot +fi + +# enable support for modloop verification +for _pubkey in "$ROOT"/var/cache/misc/*modloop*.SIGN.RSA.*.pub; do + # check only if the glob matched something + [ -f "$_pubkey" ] || continue + + # then do it in one iteration anyway + mkdir -p "$sysroot"/var/cache/misc + cp "$ROOT"/var/cache/misc/*modloop*.SIGN.RSA.*.pub "$sysroot"/var/cache/misc + pkgs="$pkgs openssl" + break +done + +apkflags="--initramfs-diskless-boot --progress" +if [ -z "$MAC_ADDRESS" ]; then + apkflags="$apkflags --no-network" +else + apkflags="$apkflags --update-cache" +fi + +if [ "$KOPT_quiet" = yes ]; then + apkflags="$apkflags --quiet" +fi + +if [ "$KOPT_keep_apk_new" != yes ]; then + apkflags="$apkflags --clean-protected" + [ -n "$ovlfiles" ] && apkflags="$apkflags --overlay-from-stdin" +fi +mkdir -p $sysroot/sys $sysroot/proc $sysroot/dev +$MOCK mount -o bind /sys $sysroot/sys +$MOCK mount -o bind /proc $sysroot/proc +$MOCK mount -o bind /dev $sysroot/dev +if [ -n "$ovlfiles" ]; then + apk add --root $sysroot $repo_opt $apkflags $pkgs <$ovlfiles +else + apk add --root $sysroot $repo_opt $apkflags $pkgs +fi +$MOCK umount $sysroot/sys $sysroot/proc $sysroot/dev +eend $? + +# unmount ovl mount if needed +if [ -n "$ovl_unmount" ]; then + $MOCK umount $ovl_unmount 2>/dev/null +fi + +# remount according default fstab from package +if [ -z "$has_fstab" ] && [ -f "$sysroot"/etc/fstab ]; then + remount_fstab_entry "$sysroot"/etc/fstab +fi + +# generate repositories if none exists. this needs to be done after relocation +if ! [ -f "$sysroot"/etc/apk/repositories ]; then + find_boot_repositories > "$sysroot"/etc/apk/repositories +fi + +# fix inittab if alternative console +setup_inittab_console + +! [ -f "$sysroot"/etc/resolv.conf ] && [ -f /etc/resolv.conf ] && \ + cp /etc/resolv.conf "$sysroot"/etc + +# setup bootchart for switch_root +chart_init="" +if [ "$KOPT_chart" = yes ]; then + /sbin/bootchartd stop-initfs "$sysroot" + chart_init="/sbin/bootchartd start-rootfs" +fi + +if [ ! -x "${sysroot}${KOPT_init}" ]; then + [ "$KOPT_splash" != "no" ] && echo exit > $sysroot/$splashfile + echo "$KOPT_init not found in new root. Launching emergency recovery shell" + echo "Type exit to continue boot." + /bin/busybox sh +fi + +# switch over to new root +cat "$ROOT"/proc/mounts 2>/dev/null | while read DEV DIR TYPE OPTS ; do + if [ "$DIR" != "/" -a "$DIR" != "$sysroot" -a -d "$DIR" ]; then + mkdir -p $sysroot/$DIR + $MOCK mount -o move $DIR $sysroot/$DIR + fi +done +sync + +[ "$KOPT_splash" = "init" ] && echo exit > $sysroot/$splashfile +echo "" +exec switch_root $switch_root_opts $sysroot $chart_init "$KOPT_init" $KOPT_init_args + +[ "$KOPT_splash" != "no" ] && echo exit > $sysroot/$splashfile +echo "initramfs emergency recovery shell launched" +exec /bin/busybox sh +reboot \ No newline at end of file