kubestronaut-sim/images/vm/guest/provision.sh
vumas3r 0e5f7686ee feat: add guided lessons; move CKA and CKS to v1.37 on Ubuntu hosts
Add lessons: untimed guided labs, one per curriculum objective, on a
host of their own, in the browser and in the terminal. Each step has
a check, a hint and its commands; a lesson writes no history or
scores, only a done mark on its objective. CKAD 2.1-2.4 (24 steps)
and all 27 CKA objectives (135 steps) have one, and `./ksim lesson
audit` proves that every step fails, is done by its own commands,
then passes.

Add CKA tasks 132-147 (cka/bank_course.py) on what a full CKA course
teaches beyond the bank: a real upgrade from v1.36, a drain under a
PodDisruptionBudget, QoS and eviction, in-place resize, kube-proxy's
chains, encryption at rest, an operator, and CSI expansion, snapshots
and clones (ensure_csi_hostpath).

Move CKA and CKS to Kubernetes v1.37 and rebuild the hosts on Ubuntu
24.04, with kubelet, kubeadm and kubectl as held packages from
pkgs.k8s.io; the desktop runs Ubuntu too. Hosts gain man pages,
hexdump and xxd.

Fix CKS Q4, which gave 3 of 12 points untouched, and CKA Q4 and Q49,
whose solutions raced their new CRD.

Audited on v1.37: CKA 147/147, CKAD 105/105, CKS 109/110 (Q103, a
Falco task, failed to seed once and awaits a re-audit); lessons: 135
of 135 CKA steps and 24 of 24 CKAD steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:15:17 -06:00

97 lines
5.4 KiB
Bash

#!/bin/bash
# Build-time provisioning of the VM exam host. Runs once, inside the VM, as the first-boot script of
# the Ubuntu cloud image (cloud-init user-data), and powers the machine off when it is through; what
# is on the disk then is the base disk every VM exam host starts from.
#
# The result: Ubuntu with containerd, kubeadm, kubelet and kubectl as apt packages (held), the
# control-plane images pulled, Falco with the modern eBPF probe, the AppArmor tools, and `candidate`
# with passwordless sudo. No cluster yet: every host makes its own on its first boot
# (guest/firstboot.sh), under its own name.
exec > >(tee -a /var/log/ksim-provision.log /dev/ttyS0) 2>&1
set -euxo pipefail
trap 'echo KSIM-PROVISION-FAILED line $LINENO; sync; poweroff -f' ERR
export DEBIAN_FRONTEND=noninteractive
modprobe 9p; modprobe 9pnet_virtio
mkdir -p /mnt/build && mount -t 9p -o trans=virtio,version=9p2000.L,ro build /mnt/build
. /mnt/build/env # K8S_VERSION=1.37.0 K8S_MINOR=v1.37
# --- nothing that works behind the candidate's back
systemctl disable --now unattended-upgrades.service apt-daily.timer apt-daily-upgrade.timer 2>/dev/null || true
apt-get purge -y snapd unattended-upgrades >/dev/null 2>&1 || true
# --- kernel settings Kubernetes wants, and the modules the host needs at every boot
printf 'overlay\nbr_netfilter\n9p\n9pnet_virtio\n' > /etc/modules-load.d/ksim.conf
modprobe overlay; modprobe br_netfilter
printf 'net.bridge.bridge-nf-call-iptables = 1\nnet.bridge.bridge-nf-call-ip6tables = 1\nnet.ipv4.ip_forward = 1\n' > /etc/sysctl.d/99-kubernetes.conf
sysctl --system >/dev/null
swapoff -a; sed -i '/\sswap\s/d' /etc/fstab
# --- packages: Kubernetes and Falco from their own repositories
mkdir -p /etc/apt/keyrings
curl -fsSL "https://pkgs.k8s.io/core:/stable:/$K8S_MINOR/deb/Release.key" | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$K8S_MINOR/deb/ /" > /etc/apt/sources.list.d/kubernetes.list
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" > /etc/apt/sources.list.d/falcosecurity.list
apt-get update
apt-get install -y --no-install-recommends containerd apparmor-utils jq vim tmux bash-completion conntrack socat strace \
"kubelet=$K8S_VERSION-*" "kubeadm=$K8S_VERSION-*" "kubectl=$K8S_VERSION-*"
apt-mark hold kubelet kubeadm kubectl
curl -fsSL -o /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64" && chmod 755 /usr/local/bin/yq
# --- containerd: systemd cgroups, and Docker Hub through the simulator's cache
mkdir -p /etc/containerd/certs.d/docker.io
containerd config default > /etc/containerd/config.toml
# (containerd 1.x writes the empty registry path as "", 2.x as '' - the first one is the CRI's)
sed -i "s/SystemdCgroup = false/SystemdCgroup = true/; 0,/config_path = \\(\"\"\\|''\\)/s##config_path = \"\/etc\/containerd\/certs.d\"#" /etc/containerd/config.toml
grep -q 'SystemdCgroup = true' /etc/containerd/config.toml && grep -q 'config_path = "/etc/containerd/certs.d"' /etc/containerd/config.toml
install -m644 /mnt/build/hosts.toml /etc/containerd/certs.d/docker.io/hosts.toml
systemctl restart containerd
printf 'runtime-endpoint: unix:///run/containerd/containerd.sock\nimage-endpoint: unix:///run/containerd/containerd.sock\n' > /etc/crictl.yaml
kubeadm config images pull --kubernetes-version "v$K8S_VERSION"
for i in busybox:1.36 nginx:1.27; do crictl pull docker.io/library/$i || true; done
# --- Falco, with the modern eBPF probe (no kernel module, no compiler)
FALCO_FRONTEND=noninteractive FALCO_DRIVER_CHOICE=modern_ebpf apt-get install -y --no-install-recommends falco
systemctl enable falco-modern-bpf.service
falco --version | head -1
# --- the candidate, as on an exam host
userdel -r ubuntu 2>/dev/null || true
useradd -m -s /bin/bash -u 1000 candidate
echo 'candidate ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/candidate && chmod 440 /etc/sudoers.d/candidate
install -m644 /mnt/build/exam.bashrc /etc/exam.bashrc && echo '. /etc/exam.bashrc' >> /etc/bash.bashrc
for h in /etc/skel /root /home/candidate; do install -m644 /mnt/build/vimrc $h/.vimrc; touch $h/.hushlogin; done
chown candidate:candidate /home/candidate/.vimrc /home/candidate/.hushlogin
printf 'PasswordAuthentication no\nPermitRootLogin prohibit-password\nUseDNS no\nPrintMotd no\n' > /etc/ssh/sshd_config.d/00-exam.conf
chmod -x /etc/update-motd.d/* 2>/dev/null || true
# --- what every boot does: name, keys, /opt/course, and on the first one the cluster
install -m755 /mnt/build/firstboot.sh /usr/local/sbin/ksim-firstboot
cat > /etc/systemd/system/ksim-firstboot.service <<'EOF'
[Unit]
Description=ksim exam host: name, keys, /opt/course and the cluster
After=network-online.target containerd.service
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/ksim-firstboot
TimeoutStartSec=600
[Install]
WantedBy=multi-user.target
EOF
systemctl enable ksim-firstboot.service
mkdir -p /opt/course
# --- the image is done: cloud-init must not run again, and the disk should be small
touch /etc/cloud/cloud-init.disabled
apt-get clean
umount /mnt/build || true
fstrim -av || true
echo KSIM-PROVISION-OK
sync
poweroff