diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aebcd14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Build artifacts +build/ +images/ +*.img +filesystem.squashfs +version.txt +latest + +# OS files +.DS_Store +*~ diff --git a/http/boot.ipxe b/http/boot.ipxe new file mode 100644 index 0000000..bb3cb75 --- /dev/null +++ b/http/boot.ipxe @@ -0,0 +1,10 @@ +#!ipxe +echo Booting Ubuntu Noble K3s Node via iPXE +echo Loading kernel from http://192.168.100.1:8800/vmlinuz +kernel --name vmlinuz http://192.168.100.1:8800/vmlinuz +echo Loading initramfs from http://192.168.100.1:8800/initrd-netboot.img +initrd --name initrd http://192.168.100.1:8800/initrd-netboot.img +echo Setting kernel arguments for HTTP root mounting +imgargs vmlinuz root=http://192.168.100.1:8800/filesystem.squashfs rootfstype=squashfs overlayroot=tmpfs ip=dhcp console=ttyS0,115200 earlyprintk=ttyS0,115200 loglevel=7 +echo Booting system... +boot vmlinuz diff --git a/initramfs/hooks/netboot b/initramfs/hooks/netboot new file mode 100755 index 0000000..cfe102f --- /dev/null +++ b/initramfs/hooks/netboot @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Add useful binaries to initrd for netboot HTTP root mounting +# + +if [ "$1" = "prereqs" ]; then + echo "udev" + exit +fi + +. /usr/share/initramfs-tools/hook-functions + +# Essential utilities for netboot - copy_exec handles dependencies automatically +copy_exec "/usr/bin/wget" +copy_exec "/usr/bin/curl" +copy_exec "/usr/bin/unsquashfs" +copy_exec "/usr/sbin/switch_root" + +# Useful tools for debugging +copy_exec "/usr/bin/awk" +copy_exec "/usr/bin/bash" +copy_exec "/usr/bin/chmod" +copy_exec "/usr/bin/file" +copy_exec "/usr/bin/free" +copy_exec "/usr/bin/grep" +copy_exec "/usr/bin/gunzip" +copy_exec "/usr/bin/gzip" +copy_exec "/usr/bin/less" +copy_exec "/usr/bin/lsblk" +copy_exec "/usr/bin/mount" +copy_exec "/usr/bin/readlink" +copy_exec "/usr/bin/sed" +copy_exec "/usr/bin/timeout" +copy_exec "/usr/bin/touch" +copy_exec "/usr/bin/vi" + +# Note: copy_exec automatically handles all shared library dependencies via ldd() +# No need to manually copy libc or other libraries - copy_exec handles this diff --git a/initramfs/initramfs.conf b/initramfs/initramfs.conf new file mode 100644 index 0000000..1d245e9 --- /dev/null +++ b/initramfs/initramfs.conf @@ -0,0 +1,70 @@ +# +# initramfs.conf +# Configuration file for mkinitramfs(8). See initramfs.conf(5). +# + +# +# MODULES: [ most | netboot | dep | list ] +# +# most - Add most filesystem and all harddrive drivers. +# +# dep - Try and guess which modules to load. +# +# netboot - Add the base modules, network modules, but skip block devices. +# +# list - Only include modules from the 'additional modules' list +# + +MODULES=most + +# +# BUSYBOX: [ y | n | auto ] +# +# Use busybox shell and utilities. If set to n, klibc utilities will be used. +# If set to auto (or unset), busybox will be used if installed and klibc will +# be used otherwise. +# + +BUSYBOX=auto + +# +# COMPRESS: [ gzip | bzip2 | lz4 | lzma | lzop | xz | zstd ] +# + +COMPRESS=gzip + +# +# DEVICE: ... +# +# Specify a specific network interface, like eth0 +# Overridden by optional ip= or BOOTIF= bootarg +# + +DEVICE= + +# +# NFSROOT: [ auto | HOST:MOUNT ] +# + +NFSROOT=auto + +# +# RUNSIZE: ... +# +# The size of the /run tmpfs mount point, like 256M or 10% +# Overridden by optional initramfs.runsize= bootarg +# + +RUNSIZE=10% + +# +# FSTYPE: ... +# +# The filesystem type(s) to support, or "auto" to use the current root +# filesystem type +# + +FSTYPE=auto + +# Disable hibernation +RESUME=none diff --git a/initramfs/modules b/initramfs/modules new file mode 100644 index 0000000..3bf98c9 --- /dev/null +++ b/initramfs/modules @@ -0,0 +1,18 @@ +# List of modules that you want to include in your initramfs. +# They will be loaded at boot time in the order below. +# +# Syntax: module_name [args ...] +# + +# Filesystem support +nls_iso8859-1 +isofs +ext4 +squashfs +overlay + +# Network modules +af_packet + +# RTL8125 network driver for 2.5GbE +r8125 diff --git a/initramfs/scripts/netboot b/initramfs/scripts/netboot new file mode 100755 index 0000000..517e14b --- /dev/null +++ b/initramfs/scripts/netboot @@ -0,0 +1,147 @@ +#!/bin/sh + +# Import standard initramfs functions +. /scripts/functions + +export PATH=/usr/bin:/usr/sbin:/bin:/sbin +MOUNTPOINT=/root +TMPFS_MOUNT=/mnt + +# Parse kernel command line for HTTP root +parse_cmdline() { + for x in $(cat /proc/cmdline); do + case $x in + root=http://*) + export ROOT_URL="${x#root=}" ;; + rootfstype=*) + export ROOTFSTYPE="${x#rootfstype=}" ;; + overlayroot=*) + export OVERLAYROOT="${x#overlayroot=}" ;; + ip=*) + export BOOTIP="${x#ip=}" ;; + *) + : ;; + esac + done +} + +mountroot() { + rc=1 + parse_cmdline + + if test -z "${ROOT_URL}"; then + log_failure_msg "No root URL defined (root=http://... not found)" + return ${rc} + fi + + # Configure networking before attempting downloads + log_begin_msg "Configuring network" + modprobe af_packet || log_warning_msg "af_packet load failed" + + # Load RTL8125 driver (already in module list but explicit load for debugging) + modprobe r8125 || log_warning_msg "r8125 driver load failed, may use generic driver" + + configure_networking + udevadm trigger + timeout 30 udevadm settle || log_warning_msg "udevadm settle timed out" + export DEVICE + log_end_msg + + # Validate networking is up + INTERFACE_UP=0 + for iface in $(ip link show | grep "^[0-9]" | awk -F: '{print $2}' | tr -d ' '); do + if ip addr show "$iface" | grep -q "inet "; then + INTERFACE_UP=1 + log_begin_msg "Interface $iface has IP address" + ip addr show "$iface" | grep "inet " | awk '{print $2}' + break + fi + done + + if [ $INTERFACE_UP -eq 0 ]; then + log_failure_msg "No network interface obtained an IP address" + return ${rc} + fi + + # Extract filename from URL + FILE_NAME=$(basename "${ROOT_URL}") + FILE_PATH="/${FILE_NAME}" + + # Download the root filesystem with retries and timeouts + log_begin_msg "Downloading root filesystem from ${ROOT_URL}" + if wget --timeout=30 --tries=3 --waitretry=5 \ + --progress=dot:mega \ + "${ROOT_URL}" -O "${FILE_PATH}"; then + log_end_msg + else + log_failure_msg "Failed to download from ${ROOT_URL} after retries" + rm -f "${FILE_PATH}" + return ${rc} + fi + + # Verify the downloaded file is a valid SquashFS + if ! file "${FILE_PATH}" | grep -q "Squash"; then + log_failure_msg "Downloaded file is not a valid SquashFS image" + rm -f "${FILE_PATH}" + return ${rc} + fi + + # Handle SquashFS images with overlay + if echo "${FILE_NAME}" | grep -q squashfs; then + log_begin_msg "Setting up SquashFS with overlay" + + # Mount read-only SquashFS + if ! mount -t squashfs "${FILE_PATH}" "${MOUNTPOINT}" -o ro; then + log_failure_msg "Failed to mount SquashFS at ${MOUNTPOINT}" + rm -f "${FILE_PATH}" + return ${rc} + fi + log_begin_msg "SquashFS mounted at ${MOUNTPOINT}" + log_end_msg + + # Setup overlay if requested + if [ -n "${OVERLAYROOT}" ]; then + log_begin_msg "Mounting ${OVERLAYROOT} for overlay" + + # Create tmpfs for upper and work directories + if ! mount -o size=2G -t "${OVERLAYROOT}" tmpfs_overlay "${TMPFS_MOUNT}"; then + log_failure_msg "Failed to mount tmpfs for overlay" + umount "${MOUNTPOINT}" + rm -f "${FILE_PATH}" + return ${rc} + fi + + # Create overlay structure + mkdir -p "${TMPFS_MOUNT}/upper" "${TMPFS_MOUNT}/work" + + # Mount overlay combining read-only lower + writable upper + if ! mount -t overlay \ + -o "lowerdir=${MOUNTPOINT},upperdir=${TMPFS_MOUNT}/upper,workdir=${TMPFS_MOUNT}/work" \ + overlay_root "${MOUNTPOINT}"; then + log_failure_msg "Failed to mount overlay filesystem" + umount "${TMPFS_MOUNT}" + umount "${MOUNTPOINT}" + rm -f "${FILE_PATH}" + return ${rc} + fi + + log_end_msg + log_begin_msg "Overlay mounted successfully" + log_end_msg + + # Clean up downloaded image as it's now mounted + rm -f "${FILE_PATH}" + rc=0 + else + # Direct SquashFS mount without overlay + log_begin_msg "Mounted SquashFS without overlay" + log_end_msg + rc=0 + fi + else + log_failure_msg "Unknown filesystem type: ${FILE_NAME}" + rm -f "${FILE_PATH}" + fi + + return ${rc} +}