feat: add security hardening, AppArmor, and ARM64 Raspberry Pi support (Phase 6)
Some checks failed
CI / Go Tests (push) Has been cancelled
CI / Build Go Binaries (amd64, linux, linux-amd64) (push) Has been cancelled
CI / Build Go Binaries (arm64, linux, linux-arm64) (push) Has been cancelled
CI / Shellcheck (push) Has been cancelled

Security hardening: bind kubeconfig server to localhost, mount hardening
(noexec/nosuid/nodev on tmpfs), sysctl network hardening, kernel module
loading lock after boot, SHA256 checksum verification for downloads,
kernel AppArmor + Audit support, complain-mode AppArmor profiles for
containerd and kubelet, and security integration test.

ARM64 Raspberry Pi support: piCore64 base extraction, RPi kernel build
from raspberrypi/linux fork, RPi firmware fetch, SD card image with 4-
partition GPT and tryboot A/B mechanism, BootEnv Go interface abstracting
GRUB vs RPi boot environments, architecture-aware build scripts, QEMU
aarch64 dev VM and boot test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 13:08:17 -06:00
parent 7abf0e0c04
commit efc7f80b65
38 changed files with 2512 additions and 96 deletions

View File

@@ -8,6 +8,16 @@ CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}"
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
ROOTFS="$ROOTFS_DIR/rootfs"
VERSION="$(cat "$PROJECT_ROOT/VERSION")"
INJECT_ARCH="${TARGET_ARCH:-amd64}"
# Architecture-specific paths
if [ "$INJECT_ARCH" = "arm64" ]; then
LIB_ARCH="aarch64-linux-gnu"
LD_SO="/lib/ld-linux-aarch64.so.1"
else
LIB_ARCH="x86_64-linux-gnu"
LD_SO="/lib64/ld-linux-x86-64.so.2"
fi
if [ ! -d "$ROOTFS" ]; then
echo "ERROR: Rootfs not found: $ROOTFS"
@@ -90,8 +100,13 @@ fi
# --- 3. Custom kernel or TCZ kernel modules ---
# If a custom kernel was built (with CONFIG_CGROUP_BPF=y), use it.
# Otherwise fall back to TCZ-extracted modules with manual modules.dep.
CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel"
CUSTOM_VMLINUZ="$CUSTOM_KERNEL_DIR/vmlinuz"
if [ "$INJECT_ARCH" = "arm64" ]; then
CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel-arm64"
CUSTOM_VMLINUZ="$CUSTOM_KERNEL_DIR/Image"
else
CUSTOM_KERNEL_DIR="$CACHE_DIR/custom-kernel"
CUSTOM_VMLINUZ="$CUSTOM_KERNEL_DIR/vmlinuz"
fi
CUSTOM_MODULES="$CUSTOM_KERNEL_DIR/modules"
# Detect kernel version from rootfs
@@ -131,7 +146,11 @@ if [ -f "$CUSTOM_VMLINUZ" ] && [ -d "$CUSTOM_MODULES/lib/modules/$KVER" ]; then
done
# Use modprobe --show-depends to resolve each module + its transitive deps
MODULES_LIST="$PROJECT_ROOT/build/config/modules.list"
if [ "$INJECT_ARCH" = "arm64" ]; then
MODULES_LIST="$PROJECT_ROOT/build/config/modules-arm64.list"
else
MODULES_LIST="$PROJECT_ROOT/build/config/modules.list"
fi
NEEDED_MODS=$(mktemp)
while IFS= read -r mod; do
# Skip comments and blank lines
@@ -291,21 +310,22 @@ if [ -f /usr/sbin/xtables-nft-multi ]; then
ln -sf xtables-nft-multi "$ROOTFS/usr/sbin/$cmd"
done
# Copy required shared libraries
mkdir -p "$ROOTFS/usr/lib/x86_64-linux-gnu" "$ROOTFS/lib/x86_64-linux-gnu" "$ROOTFS/lib64"
# Copy required shared libraries (architecture-aware paths)
mkdir -p "$ROOTFS/usr/lib/$LIB_ARCH" "$ROOTFS/lib/$LIB_ARCH"
[ "$INJECT_ARCH" != "arm64" ] && mkdir -p "$ROOTFS/lib64"
for lib in \
/lib/x86_64-linux-gnu/libxtables.so.12* \
/lib/x86_64-linux-gnu/libmnl.so.0* \
/lib/x86_64-linux-gnu/libnftnl.so.11* \
/lib/x86_64-linux-gnu/libc.so.6 \
/lib64/ld-linux-x86-64.so.2; do
"/lib/$LIB_ARCH/libxtables.so.12"* \
"/lib/$LIB_ARCH/libmnl.so.0"* \
"/lib/$LIB_ARCH/libnftnl.so.11"* \
"/lib/$LIB_ARCH/libc.so.6" \
"$LD_SO"; do
[ -e "$lib" ] && cp -aL "$lib" "$ROOTFS${lib}" 2>/dev/null || true
done
# Copy xtables modules directory (match extensions)
if [ -d /usr/lib/x86_64-linux-gnu/xtables ]; then
mkdir -p "$ROOTFS/usr/lib/x86_64-linux-gnu/xtables"
cp -a /usr/lib/x86_64-linux-gnu/xtables/*.so "$ROOTFS/usr/lib/x86_64-linux-gnu/xtables/" 2>/dev/null || true
if [ -d "/usr/lib/$LIB_ARCH/xtables" ]; then
mkdir -p "$ROOTFS/usr/lib/$LIB_ARCH/xtables"
cp -a "/usr/lib/$LIB_ARCH/xtables/"*.so "$ROOTFS/usr/lib/$LIB_ARCH/xtables/" 2>/dev/null || true
fi
echo " Installed iptables-nft (xtables-nft-multi) + shared libs"
@@ -314,11 +334,16 @@ else
fi
# Kernel modules list (for init to load at boot)
cp "$PROJECT_ROOT/build/config/modules.list" "$ROOTFS/usr/lib/kubesolo-os/modules.list"
if [ "$INJECT_ARCH" = "arm64" ]; then
cp "$PROJECT_ROOT/build/config/modules-arm64.list" "$ROOTFS/usr/lib/kubesolo-os/modules.list"
else
cp "$PROJECT_ROOT/build/config/modules.list" "$ROOTFS/usr/lib/kubesolo-os/modules.list"
fi
# --- 4. Sysctl config ---
mkdir -p "$ROOTFS/etc/sysctl.d"
cp "$PROJECT_ROOT/build/rootfs/etc/sysctl.d/k8s.conf" "$ROOTFS/etc/sysctl.d/k8s.conf"
cp "$PROJECT_ROOT/build/rootfs/etc/sysctl.d/security.conf" "$ROOTFS/etc/sysctl.d/security.conf"
# --- 5. OS metadata ---
echo "$VERSION" > "$ROOTFS/etc/kubesolo-os-version"
@@ -362,7 +387,35 @@ else
echo " WARN: No CA certificates found in builder — TLS verification will fail"
fi
# --- 9. Ensure /etc/hosts and /etc/resolv.conf exist ---
# --- 9. AppArmor parser + profiles ---
echo " Installing AppArmor..."
if [ -f /usr/sbin/apparmor_parser ]; then
mkdir -p "$ROOTFS/usr/sbin"
cp /usr/sbin/apparmor_parser "$ROOTFS/usr/sbin/apparmor_parser"
chmod +x "$ROOTFS/usr/sbin/apparmor_parser"
# Copy shared libraries required by apparmor_parser
for lib in "/lib/$LIB_ARCH/libapparmor.so.1"*; do
[ -e "$lib" ] && cp -aL "$lib" "$ROOTFS${lib}" 2>/dev/null || true
done
echo " Installed apparmor_parser + shared libs"
else
echo " WARN: apparmor_parser not found in builder (install apparmor package)"
fi
# Copy AppArmor profiles
APPARMOR_PROFILES="$PROJECT_ROOT/build/rootfs/etc/apparmor.d"
if [ -d "$APPARMOR_PROFILES" ]; then
mkdir -p "$ROOTFS/etc/apparmor.d"
cp "$APPARMOR_PROFILES"/* "$ROOTFS/etc/apparmor.d/" 2>/dev/null || true
PROFILE_COUNT=$(ls "$ROOTFS/etc/apparmor.d/" 2>/dev/null | wc -l)
echo " Installed $PROFILE_COUNT AppArmor profiles"
else
echo " WARN: No AppArmor profiles found at $APPARMOR_PROFILES"
fi
# --- 10. Ensure /etc/hosts and /etc/resolv.conf exist ---
if [ ! -f "$ROOTFS/etc/hosts" ]; then
cat > "$ROOTFS/etc/hosts" << EOF
127.0.0.1 localhost