Three independent hardenings, addressing a review finding that the kernel and image build pipelines were relying on HTTPS alone for artifact integrity. scripts/make-generic-kernel.sh - Fetch the detached PGP signature (linux-<ver>.tar.sign) alongside the tarball and verify it with gpg before extraction. An isolated $GNUPGHOME under the tempdir keeps the kernel signers out of the invoking user's keyring. - Import the three kernel.org release signing keys (Greg KH / Linus / Sasha Levin) from keyserver.ubuntu.com, falling back to keys.openpgp.org. Ubuntu comes first because keys.openpgp.org strips unverified UIDs on upload, leaving gpg with UID-less keys it refuses to trust. - Require VALIDSIG (cryptographic proof) rather than GOODSIG (printed even for expired keys) before proceeding. Verified end-to-end against a clean tarball (accepts) and a byte-flipped tampered copy (rejects with BADSIG). - gpg + gpgv + xz added to the required-tools check. images/golden/Dockerfile - Pin Docker's apt signing key by fingerprint. After downloading /etc/apt/keyrings/docker.asc we gpg --show-keys --with-colons it, extract the fpr, and compare against the expected 9DC858229FC7DD38854AE2D88D81803C0EBFCD88. A tampered or swapped key aborts the build before any apt repo metadata is fetched. - Replace `curl https://mise.run | sh` with a pinned GitHub release binary (mise v2026.4.18, linux-x64) verified against its published sha256. Refuses to build on unknown architectures rather than silently installing a binary we have no hash for. - Add gnupg to the ESSENTIAL apt-get install so the fingerprint check has gpg available. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
148 lines
5.5 KiB
Bash
Executable file
148 lines
5.5 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# make-generic-kernel.sh
|
|
#
|
|
# Build a minimal Firecracker-optimized vmlinux from upstream kernel.org
|
|
# sources using the vendored Firecracker config. All essential drivers
|
|
# (virtio_blk, virtio_net, ext4, vsock) are compiled in — no modules,
|
|
# no initramfs needed. The result boots any OCI-pulled rootfs directly.
|
|
#
|
|
# Usage:
|
|
# scripts/make-generic-kernel.sh [--version 6.12.8]
|
|
#
|
|
# Output:
|
|
# build/manual/generic-kernel/boot/vmlinux-<version>
|
|
# build/manual/generic-kernel/metadata.json
|
|
|
|
set -euo pipefail
|
|
|
|
log() { printf '[make-generic-kernel] %s\n' "$*" >&2; }
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
OUT_DIR="${BANGER_MANUAL_DIR:-$REPO_ROOT/build/manual}/generic-kernel"
|
|
CONFIG="$REPO_ROOT/configs/firecracker-x86_64-6.1.config"
|
|
KERNEL_VERSION="${KERNEL_VERSION:-6.12.8}"
|
|
KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
|
|
JOBS="${JOBS:-$(nproc)}"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
usage: scripts/make-generic-kernel.sh [--version <ver>]
|
|
|
|
Downloads kernel <ver> from kernel.org, applies the vendored Firecracker
|
|
config, and builds a minimal vmlinux. Default version: $KERNEL_VERSION
|
|
|
|
Output: $OUT_DIR/boot/vmlinux-<ver>
|
|
EOF
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--version) KERNEL_VERSION="$2"; KERNEL_MAJOR="${KERNEL_VERSION%%.*}"; shift 2;;
|
|
-h|--help) usage; exit 0;;
|
|
*) log "unknown arg: $1"; exit 1;;
|
|
esac
|
|
done
|
|
|
|
for tool in curl tar xz make gcc gpg gpgv; do
|
|
command -v "$tool" >/dev/null 2>&1 || { log "missing required tool: $tool"; exit 1; }
|
|
done
|
|
[[ -f "$CONFIG" ]] || { log "config not found: $CONFIG"; exit 1; }
|
|
|
|
# kernel.org release signing keys. Stable (Greg KH) signs most point
|
|
# releases; mainline (Linus) signs .0 drops; Sasha Levin sometimes
|
|
# signs longterm backports. Listing all three keeps the script
|
|
# working across every release channel the user might pick. Rotations
|
|
# are rare and announced; update this list if gpg complains.
|
|
#
|
|
# Fingerprints verified against kernel.org:
|
|
# https://www.kernel.org/signature.html
|
|
KERNEL_SIGNING_KEYS=(
|
|
647F28654894E3BD457199BE38DBBDC86092693E # Greg Kroah-Hartman
|
|
ABAF11C65A2970B130ABE3C479BE3E4300411886 # Linus Torvalds
|
|
E27E5D8A3403A2EF66873BBCDEA66FF797772CDC # Sasha Levin
|
|
)
|
|
|
|
TARBALL="linux-${KERNEL_VERSION}.tar.xz"
|
|
SIGNATURE="linux-${KERNEL_VERSION}.tar.sign"
|
|
BASE_URL="https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x"
|
|
SRC_DIR="$(mktemp -d)"
|
|
trap 'rm -rf "$SRC_DIR"' EXIT
|
|
|
|
# Isolated GNUPGHOME so the verification step can't accidentally
|
|
# trust whatever the invoking user already has in their keyring. The
|
|
# trap above cleans the whole SRC_DIR, including this.
|
|
GPG_HOME="$SRC_DIR/gnupg"
|
|
install -d -m 0700 "$GPG_HOME"
|
|
export GNUPGHOME="$GPG_HOME"
|
|
|
|
log "importing kernel.org signing keys"
|
|
# keyserver.ubuntu.com first: it returns keys with user IDs intact,
|
|
# which gpg needs to mark the key as usable. keys.openpgp.org (the
|
|
# current SKS successor) strips unverified UIDs on upload, and the
|
|
# kernel.org devs haven't all completed its email verification flow,
|
|
# so pulling from there returns UID-less keys that gpg then refuses
|
|
# to trust. We fall back to it anyway in case ubuntu is unreachable.
|
|
if ! gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys "${KERNEL_SIGNING_KEYS[@]}" 2>/dev/null; then
|
|
log "key fetch from keyserver.ubuntu.com failed; trying keys.openpgp.org"
|
|
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "${KERNEL_SIGNING_KEYS[@]}"
|
|
fi
|
|
|
|
log "downloading kernel $KERNEL_VERSION from $BASE_URL/$TARBALL"
|
|
curl -fSL --progress-bar -o "$SRC_DIR/$TARBALL" "$BASE_URL/$TARBALL"
|
|
curl -fSL --progress-bar -o "$SRC_DIR/$SIGNATURE" "$BASE_URL/$SIGNATURE"
|
|
|
|
log "verifying signature"
|
|
# The .tar.sign is a detached signature over the *uncompressed* tar,
|
|
# per kernel.org convention. Pipe the xz-decompressed stream into
|
|
# gpg --verify so we never materialise an unverified tarball on disk.
|
|
# Require VALIDSIG (the cryptographic proof — GOODSIG alone is
|
|
# printed even for expired/revoked keys, VALIDSIG requires a usable
|
|
# key and a mathematically valid signature).
|
|
VERIFY_STATUS="$SRC_DIR/verify.status"
|
|
xz -cd "$SRC_DIR/$TARBALL" | gpg --batch --status-fd 3 --verify "$SRC_DIR/$SIGNATURE" - 3>"$VERIFY_STATUS" 2>/dev/null || true
|
|
if ! grep -qE '^\[GNUPG:\] VALIDSIG' "$VERIFY_STATUS"; then
|
|
log "signature verification FAILED — refusing to build"
|
|
log "gpg status:"
|
|
cat "$VERIFY_STATUS" >&2 || true
|
|
exit 1
|
|
fi
|
|
log "signature OK (signed by $(awk '/^\[GNUPG:\] VALIDSIG/ {print $3}' "$VERIFY_STATUS"))"
|
|
|
|
log "extracting"
|
|
tar -xf "$SRC_DIR/$TARBALL" -C "$SRC_DIR" --strip-components=1
|
|
|
|
log "applying firecracker config"
|
|
cp "$CONFIG" "$SRC_DIR/.config"
|
|
# Adapt the 6.1 config to whatever version we're building. make olddefconfig
|
|
# fills in any new symbols with defaults.
|
|
make -C "$SRC_DIR" olddefconfig >/dev/null 2>&1
|
|
|
|
log "building vmlinux (jobs=$JOBS)"
|
|
make -C "$SRC_DIR" -j"$JOBS" vmlinux 2>&1 | tail -5
|
|
|
|
VMLINUX="$SRC_DIR/vmlinux"
|
|
if [[ ! -f "$VMLINUX" ]]; then
|
|
log "vmlinux not found after build; check build output above"
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$OUT_DIR/boot"
|
|
DEST="$OUT_DIR/boot/vmlinux-${KERNEL_VERSION}"
|
|
cp "$VMLINUX" "$DEST"
|
|
|
|
log "verifying: $(file -b "$DEST" | head -c 80)"
|
|
|
|
cat > "$OUT_DIR/metadata.json" <<EOF
|
|
{
|
|
"kernel_path": "$DEST",
|
|
"kernel_version": "$KERNEL_VERSION",
|
|
"config": "firecracker-x86_64-6.1"
|
|
}
|
|
EOF
|
|
|
|
log "done: $DEST ($(du -h "$DEST" | cut -f1))"
|
|
log "no initrd or modules needed — all drivers are built-in"
|
|
log ""
|
|
log "next steps:"
|
|
log " banger kernel import generic-${KERNEL_VERSION%%.*}.${KERNEL_VERSION#*.} --from $OUT_DIR --distro generic --arch x86_64"
|