Files
spinifex/scripts/build-lambda-runtime-microvm-image.sh
T
Josh Siviour a541ee312d feat(lambda): add the runtime image, guest agent and authenticated TCP control connection (not wired) (#1448)
* feat(lambda): add the Lambda runtime image and guest agent

* fix(lambda): stop the guest agent after a timed-out invocation

* docs(lambda): declare the L-GUEST executor-to-agent contract

* refactor(vm): remove the unused virtio-serial port builder

Lambda guest control moves to TCP over the microVM's network, so no
caller needs SerialPorts. This reverts d7905107b.

* fix(lambda): drop the serial carrier from guest startup and keep virtio-net

run-agent no longer waits for a carrier device named on the kernel
command line, init loads virtio_net instead of virtio_console, and the
image keeps virtio_net's failover and net_failover dependencies. The
normal agent still refuses to start without an authenticated transport.

* feat(lambda): authenticate the guest's TCP control connection with pinned mutual TLS

hostlink loads the per-generation identity from fw_cfg and dials the host
over TLS 1.3 with ALPN spinifex-lambda-guest/1. VerifyPeer requires exactly
the pinned certificate, validates it, and checks its single URI SAN names
the expected scope and role. The L-GUEST contract now describes this
transport; message framing and fencing stay open, and the normal agent
still refuses to start.

* feat(lambda): add the host end of the guest's TCP mutual TLS connection

guestconn creates fresh host and guest identities for each environment
generation, produces the fw_cfg entries the guest reads, and accepts only
that generation's exact guest certificate. Peers on the untrusted bridge
that fail the handshake are closed and the listener keeps waiting until
its context ends; the cancel callback finishes before the deadline is
cleared so the listener can be reused.

The pinned-certificate check and the L-GUEST vocabulary move to
foundation/pki and contracts/lambda/v1 so both ends share them.

* docs(lambda): describe the shared connection vocabulary in the contract README
2026-11-02 22:09:58 +10:00

98 lines
3.6 KiB
Bash
Executable File

#!/bin/bash
# build-lambda-runtime-microvm-image.sh — build the Lambda runtime microVM kernel
# + initramfs: a guest running the Lambda guest agent, with virtio-net for its
# host control connection. It is not shipped.
#
# Inputs:
# $REPO_ROOT/build/microvm/lambda-runtime/{init.sh,inittab,run-agent}
# $REPO_ROOT/bin/lambda-agent (make bin/lambda-agent)
#
# Outputs:
# $REPO_ROOT/build/microvm/lambda-runtime/vmlinuz
# $REPO_ROOT/build/microvm/lambda-runtime/initramfs.cpio.gz
#
# Rootfs strategy and module stripping: scripts/lib/microvm-image.sh.
# shellcheck disable=SC2034 # variables are consumed by the sourced library
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BUILD_DIR="$REPO_ROOT/build/microvm/lambda-runtime"
INIT_SH="$BUILD_DIR/init.sh"
INITTAB="$BUILD_DIR/inittab"
RUN_AGENT="$BUILD_DIR/run-agent"
LAMBDA_AGENT_BIN="$REPO_ROOT/bin/lambda-agent"
TAG="[build-lambda-runtime-microvm-image]"
ALPINE_VERSION="${ALPINE_VERSION:-3.24}"
ALPINE_MIRROR="${ALPINE_MIRROR:-https://dl-cdn.alpinelinux.org/alpine}"
# kmod: the image's own depmod/modprobe rebuild module metadata. busybox ships ip.
ALPINE_PACKAGES="busybox linux-virt kmod ca-certificates"
# tiny_power_button turns the ACPI power button into a signal busybox init
# handles as poweroff.
MICROVM_EXTRA_MODULES="${MICROVM_EXTRA_MODULES:-tiny_power_button}"
# virtio_net needs failover and net_failover, which the virtio* glob misses.
MICROVM_EXTRA_KEEP_GLOBS=("*failover*.ko*")
MICROVM_GATE_MODULES="qemu_fw_cfg virtio_net"
# shellcheck source=lib/microvm-image.sh
source "$REPO_ROOT/scripts/lib/microvm-image.sh"
echo "$TAG repo root: $REPO_ROOT"
echo "$TAG build dir: $BUILD_DIR"
microvm_check_tools
for f in "$INIT_SH" "$INITTAB" "$RUN_AGENT"; do
if [ ! -f "$f" ]; then
echo "ERROR: $f not found — cannot build initramfs" >&2
exit 1
fi
done
if [ ! -x "$LAMBDA_AGENT_BIN" ]; then
echo "ERROR: $LAMBDA_AGENT_BIN not found — run 'make bin/lambda-agent'" >&2
exit 1
fi
if ! file -b "$LAMBDA_AGENT_BIN" | grep -q "statically linked"; then
echo "ERROR: $LAMBDA_AGENT_BIN is not statically linked — it cannot run on the musl guest" >&2
exit 1
fi
microvm_init_dirs
microvm_make_rootfs
microvm_empty_dev
echo "$TAG installing init, inittab, run-agent and lambda-agent..."
install -m 0755 "$INIT_SH" "$CHROOT_DIR/init"
install -m 0644 "$INITTAB" "$CHROOT_DIR/etc/inittab"
mkdir -p "$CHROOT_DIR/usr/local/lib/lambda-runtime" "$CHROOT_DIR/usr/local/bin"
install -m 0755 "$RUN_AGENT" "$CHROOT_DIR/usr/local/lib/lambda-runtime/run-agent"
install -m 0755 "$LAMBDA_AGENT_BIN" "$CHROOT_DIR/usr/local/bin/lambda-agent"
# Function-code mountpoint and scratch space ship empty.
mkdir -p "$CHROOT_DIR/var/task" "$CHROOT_DIR/tmp" \
"$CHROOT_DIR/proc" "$CHROOT_DIR/sys" "$CHROOT_DIR/dev" "$CHROOT_DIR/etc/ssl/certs"
# The bootstrap runs unprivileged, so / must be traversable (mktemp -d makes it
# 0700) and nothing it runs from may be group-writable.
chmod 0755 "$CHROOT_DIR" "$CHROOT_DIR/var/task" "$CHROOT_DIR/usr/local/lib/lambda-runtime"
microvm_locate_kernel
microvm_strip_modules
microvm_strip_rootfs
microvm_stage_kernel
microvm_pack_initramfs
# Nothing reaches $BUILD_DIR until the staged pair passes these checks.
for out in vmlinuz initramfs.cpio.gz; do
if [ ! -s "$STAGE_DIR/$out" ]; then
echo "ERROR: staged $out is missing or empty" >&2
exit 1
fi
done
if ! gzip -t "$STAGE_DIR/initramfs.cpio.gz"; then
echo "ERROR: staged initramfs.cpio.gz is not valid gzip" >&2
exit 1
fi
mv -f "$STAGE_DIR/vmlinuz" "$BUILD_DIR/vmlinuz"
mv -f "$STAGE_DIR/initramfs.cpio.gz" "$BUILD_DIR/initramfs.cpio.gz"
echo ""
echo "$TAG done."