The stock TinyCore kernel config has "# CONFIG_SECURITY is not set" which caused make olddefconfig to silently revert all security configs in a single pass. Fix by applying security configs (AppArmor, Audit, LSM) after the first olddefconfig resolves base dependencies, then running a second pass. Added mandatory verification that exits on missing critical configs. All QEMU test scripts converted from broken -cdrom + -append pattern to direct kernel boot (-kernel + -initrd) via shared test/lib/qemu-helpers.sh helper library. The -append flag only works with -kernel, not -cdrom. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
241 lines
8.4 KiB
Bash
Executable File
241 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# build-kernel.sh — Build custom Tiny Core kernel with CONFIG_CGROUP_BPF=y
|
|
#
|
|
# The stock Tiny Core 17.0 kernel (6.18.2-tinycore64) lacks CONFIG_CGROUP_BPF,
|
|
# which is required for cgroup v2 device control in runc/containerd.
|
|
# This script downloads the TC-patched kernel source, enables CONFIG_CGROUP_BPF,
|
|
# and builds vmlinuz + modules.
|
|
#
|
|
# Output is cached in $CACHE_DIR/custom-kernel/ and reused across builds.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}"
|
|
|
|
# shellcheck source=../config/versions.env
|
|
. "$SCRIPT_DIR/../config/versions.env"
|
|
|
|
KVER="6.18.2-tinycore64"
|
|
KERNEL_BASE_URL="https://distro.ibiblio.org/tinycorelinux/${TINYCORE_VERSION%%.*}.x/${TINYCORE_ARCH}/release/src/kernel"
|
|
KERNEL_SRC_URL="${KERNEL_BASE_URL}/linux-6.18.2-patched.tar.xz"
|
|
KERNEL_CFG_URL="${KERNEL_BASE_URL}/config-${KVER}"
|
|
|
|
CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel"
|
|
CUSTOM_VMLINUZ="$CUSTOM_KERNEL_DIR/vmlinuz"
|
|
CUSTOM_MODULES="$CUSTOM_KERNEL_DIR/modules"
|
|
|
|
mkdir -p "$CACHE_DIR" "$CUSTOM_KERNEL_DIR"
|
|
|
|
# --- Skip if already built ---
|
|
if [ -f "$CUSTOM_VMLINUZ" ] && [ -d "$CUSTOM_MODULES/lib/modules/$KVER" ]; then
|
|
echo "==> Custom kernel already built (cached)"
|
|
echo " vmlinuz: $CUSTOM_VMLINUZ ($(du -h "$CUSTOM_VMLINUZ" | cut -f1))"
|
|
MOD_COUNT=$(find "$CUSTOM_MODULES/lib/modules/$KVER" -name '*.ko*' | wc -l)
|
|
echo " Modules: $MOD_COUNT modules in $CUSTOM_MODULES/lib/modules/$KVER"
|
|
exit 0
|
|
fi
|
|
|
|
echo "==> Building custom kernel with CONFIG_CGROUP_BPF=y..."
|
|
echo " Kernel version: $KVER"
|
|
|
|
# --- Download kernel source ---
|
|
KERNEL_SRC_ARCHIVE="$CACHE_DIR/linux-6.18.2-patched.tar.xz"
|
|
if [ ! -f "$KERNEL_SRC_ARCHIVE" ]; then
|
|
echo "==> Downloading kernel source (~149 MB)..."
|
|
echo " URL: $KERNEL_SRC_URL"
|
|
wget -q --show-progress -O "$KERNEL_SRC_ARCHIVE" "$KERNEL_SRC_URL" 2>/dev/null || \
|
|
curl -fSL "$KERNEL_SRC_URL" -o "$KERNEL_SRC_ARCHIVE"
|
|
echo " Downloaded: $(du -h "$KERNEL_SRC_ARCHIVE" | cut -f1)"
|
|
else
|
|
echo "==> Kernel source already cached: $(du -h "$KERNEL_SRC_ARCHIVE" | cut -f1)"
|
|
fi
|
|
|
|
# --- Download stock config ---
|
|
KERNEL_CFG="$CACHE_DIR/config-${KVER}"
|
|
if [ ! -f "$KERNEL_CFG" ]; then
|
|
echo "==> Downloading stock kernel config..."
|
|
echo " URL: $KERNEL_CFG_URL"
|
|
wget -q -O "$KERNEL_CFG" "$KERNEL_CFG_URL" 2>/dev/null || \
|
|
curl -fSL "$KERNEL_CFG_URL" -o "$KERNEL_CFG"
|
|
else
|
|
echo "==> Stock kernel config already cached"
|
|
fi
|
|
|
|
# --- Extract source ---
|
|
# IMPORTANT: Must extract on a case-sensitive filesystem. The kernel source has
|
|
# files that differ only by case (e.g., xt_mark.h vs xt_MARK.h). If the cache
|
|
# is on macOS (case-insensitive APFS), extraction silently loses files.
|
|
# Use /tmp inside the container (ext4, case-sensitive) for the build.
|
|
KERNEL_BUILD_DIR="/tmp/kernel-build"
|
|
rm -rf "$KERNEL_BUILD_DIR"
|
|
mkdir -p "$KERNEL_BUILD_DIR"
|
|
|
|
echo "==> Extracting kernel source (case-sensitive filesystem)..."
|
|
tar -xf "$KERNEL_SRC_ARCHIVE" -C "$KERNEL_BUILD_DIR"
|
|
|
|
# Find the extracted source directory (could be linux-6.18.2 or linux-6.18.2-patched)
|
|
KERNEL_SRC_DIR=$(find "$KERNEL_BUILD_DIR" -maxdepth 1 -type d -name 'linux-*' | head -1)
|
|
if [ -z "$KERNEL_SRC_DIR" ]; then
|
|
echo "ERROR: Could not find kernel source directory after extraction"
|
|
ls -la "$KERNEL_BUILD_DIR"/
|
|
exit 1
|
|
fi
|
|
echo " Source dir: $(basename "$KERNEL_SRC_DIR")"
|
|
|
|
cd "$KERNEL_SRC_DIR"
|
|
|
|
# --- Apply stock config + enable CONFIG_CGROUP_BPF ---
|
|
echo "==> Applying stock Tiny Core config..."
|
|
cp "$KERNEL_CFG" .config
|
|
|
|
echo "==> Enabling required kernel configs..."
|
|
./scripts/config --enable CONFIG_CGROUP_BPF
|
|
./scripts/config --enable CONFIG_DEVTMPFS
|
|
./scripts/config --enable CONFIG_DEVTMPFS_MOUNT
|
|
./scripts/config --enable CONFIG_MEMCG
|
|
./scripts/config --enable CONFIG_CFS_BANDWIDTH
|
|
|
|
# --- Strip unnecessary subsystems for smallest footprint ---
|
|
# This is a headless K8s edge appliance — no sound, GPU, wireless, etc.
|
|
echo "==> Disabling unnecessary subsystems for minimal footprint..."
|
|
|
|
# Sound subsystem (not needed on headless appliance)
|
|
./scripts/config --disable SOUND
|
|
|
|
# GPU/DRM (serial console only, no display)
|
|
./scripts/config --disable DRM
|
|
|
|
# KVM hypervisor (this IS the guest/bare metal, not a hypervisor)
|
|
./scripts/config --disable KVM
|
|
|
|
# Media/camera/TV/radio (not needed)
|
|
./scripts/config --disable MEDIA_SUPPORT
|
|
|
|
# Wireless networking (wired edge device)
|
|
./scripts/config --disable WIRELESS
|
|
./scripts/config --disable WLAN
|
|
./scripts/config --disable CFG80211
|
|
|
|
# Bluetooth (not needed)
|
|
./scripts/config --disable BT
|
|
|
|
# NFC (not needed)
|
|
./scripts/config --disable NFC
|
|
|
|
# Infiniband (not needed on edge)
|
|
./scripts/config --disable INFINIBAND
|
|
|
|
# PCMCIA (legacy, not needed)
|
|
./scripts/config --disable PCMCIA
|
|
|
|
# Amateur radio (not needed)
|
|
./scripts/config --disable HAMRADIO
|
|
|
|
# ISDN (not needed)
|
|
./scripts/config --disable ISDN
|
|
|
|
# ATM networking (not needed)
|
|
./scripts/config --disable ATM
|
|
|
|
# Joystick/gamepad (not needed)
|
|
./scripts/config --disable INPUT_JOYSTICK
|
|
./scripts/config --disable INPUT_TABLET
|
|
|
|
# FPGA (not needed)
|
|
./scripts/config --disable FPGA
|
|
|
|
# First pass: resolve base dependencies before adding security configs.
|
|
# The stock TC config has "# CONFIG_SECURITY is not set" which causes
|
|
# olddefconfig to strip security-related options if applied in a single pass.
|
|
make olddefconfig
|
|
|
|
# Security: AppArmor LSM + Audit subsystem
|
|
# Applied AFTER first olddefconfig to ensure CONFIG_SECURITY dependencies
|
|
# (SYSFS, MULTIUSER) are resolved before enabling the security subtree.
|
|
echo "==> Enabling AppArmor + Audit kernel configs..."
|
|
./scripts/config --enable CONFIG_AUDIT
|
|
./scripts/config --enable CONFIG_AUDITSYSCALL
|
|
./scripts/config --enable CONFIG_SECURITY
|
|
./scripts/config --enable CONFIG_SECURITYFS
|
|
./scripts/config --enable CONFIG_SECURITY_NETWORK
|
|
./scripts/config --enable CONFIG_SECURITY_APPARMOR
|
|
./scripts/config --set-str CONFIG_LSM "lockdown,yama,apparmor"
|
|
./scripts/config --set-str CONFIG_DEFAULT_SECURITY "apparmor"
|
|
|
|
# Second pass: resolve security config dependencies
|
|
make olddefconfig
|
|
|
|
# Verify critical configs are set
|
|
if ! grep -q 'CONFIG_CGROUP_BPF=y' .config; then
|
|
echo "ERROR: CONFIG_CGROUP_BPF not set after olddefconfig"
|
|
grep 'CGROUP_BPF' .config || echo " (CGROUP_BPF not found in .config)"
|
|
echo ""
|
|
echo "Prerequisites check:"
|
|
grep -E 'CONFIG_BPF=|CONFIG_BPF_SYSCALL=' .config || echo " BPF not found"
|
|
exit 1
|
|
fi
|
|
echo " CONFIG_CGROUP_BPF=y confirmed"
|
|
|
|
if ! grep -q 'CONFIG_SECURITY_APPARMOR=y' .config; then
|
|
echo "ERROR: CONFIG_SECURITY_APPARMOR not set after olddefconfig"
|
|
echo " Security-related configs:"
|
|
grep -E 'CONFIG_SECURITY=|CONFIG_SECURITYFS=|CONFIG_SECURITY_APPARMOR=' .config
|
|
exit 1
|
|
fi
|
|
echo " CONFIG_SECURITY_APPARMOR=y confirmed"
|
|
|
|
if ! grep -q 'CONFIG_AUDIT=y' .config; then
|
|
echo "ERROR: CONFIG_AUDIT not set after olddefconfig"
|
|
exit 1
|
|
fi
|
|
echo " CONFIG_AUDIT=y confirmed"
|
|
|
|
# Show what changed (security-related)
|
|
echo " Key config values:"
|
|
grep -E 'CONFIG_SECURITY=|CONFIG_SECURITY_APPARMOR=|CONFIG_AUDIT=|CONFIG_LSM=|CONFIG_CGROUP_BPF=' .config | sed 's/^/ /'
|
|
|
|
# --- Build kernel + modules ---
|
|
NPROC=$(nproc 2>/dev/null || echo 4)
|
|
echo ""
|
|
echo "==> Building kernel (${NPROC} parallel jobs)..."
|
|
echo " This may take 15-25 minutes..."
|
|
|
|
make -j"$NPROC" bzImage modules 2>&1
|
|
|
|
echo "==> Kernel build complete"
|
|
|
|
# --- Install to staging ---
|
|
echo "==> Installing vmlinuz..."
|
|
cp arch/x86/boot/bzImage "$CUSTOM_VMLINUZ"
|
|
|
|
echo "==> Installing modules (stripped)..."
|
|
rm -rf "$CUSTOM_MODULES"
|
|
mkdir -p "$CUSTOM_MODULES"
|
|
make INSTALL_MOD_STRIP=1 modules_install INSTALL_MOD_PATH="$CUSTOM_MODULES"
|
|
|
|
# Remove build/source symlinks (they point to the build dir which won't exist in rootfs)
|
|
rm -f "$CUSTOM_MODULES/lib/modules/$KVER/build"
|
|
rm -f "$CUSTOM_MODULES/lib/modules/$KVER/source"
|
|
|
|
# Run depmod to generate proper module dependency files
|
|
echo "==> Running depmod..."
|
|
depmod -a -b "$CUSTOM_MODULES" "$KVER" 2>/dev/null || true
|
|
|
|
# Save the final config for reference
|
|
cp .config "$CUSTOM_KERNEL_DIR/.config"
|
|
|
|
# --- Clean up build dir (large, ~1.5 GB) ---
|
|
echo "==> Cleaning kernel build directory..."
|
|
cd /
|
|
rm -rf "$KERNEL_BUILD_DIR"
|
|
|
|
# --- Summary ---
|
|
echo ""
|
|
echo "==> Custom kernel build complete:"
|
|
echo " vmlinuz: $CUSTOM_VMLINUZ ($(du -h "$CUSTOM_VMLINUZ" | cut -f1))"
|
|
MOD_COUNT=$(find "$CUSTOM_MODULES/lib/modules/$KVER" -name '*.ko*' | wc -l)
|
|
echo " Modules: $MOD_COUNT modules"
|
|
echo " Modules size: $(du -sh "$CUSTOM_MODULES/lib/modules/$KVER" | cut -f1)"
|
|
echo ""
|