kubestronaut-sim runs each exam task on a host of its own - a kind (kubeadm) cluster in rootless podman, or a QEMU/KVM Ubuntu VM for the CKS tasks that need their own kernel (Falco, AppArmor) - and puts a browser desktop in front of them, the way killer.sh and sailor.sh do. - ksim, sim/: the engine (sessions, drills, study mode, grading, coverage, audit), host and VM lifecycle, the desktop - images/: exam host images (common, CKS variant with its tools, VM guest) - cka/ (131 tasks), ckad/ (105), cks/ (110): task banks, curricula with study notes, reference solutions and scripted solutions Every domain of every track has at least 17 pool tasks. `./ksim audit` proves a task three ways: it seeds cleanly on its own host, scores nothing before anyone has worked on it, and reaches full marks when its scripted solution is run on the host as `candidate`. All 346 tasks pass (CKA audited 2026-10-13, CKAD and CKS 2026-10-12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZPd3GGWyxTgAkL6a4gAJz
67 lines
4.6 KiB
Docker
67 lines
4.6 KiB
Docker
# CKS exam host: the common exam host (images/node) plus what the CKS hosts carry - the scanners,
|
|
# a sandbox runtime, a service mesh client, and Kubernetes installed as real packages so that an
|
|
# upgrade is `apt-get install` and `kubeadm upgrade`, as on the day. Built by `./ksim build cks`.
|
|
ARG BASE
|
|
FROM ${BASE}
|
|
RUN apt-get update \
|
|
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
python3-minimal bsdextrautils binutils xxd strace file bzip2 gnupg conntrack tcpdump lsof kmod vsftpd \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
# vsftpd is here to be found and removed in a task; it must not run on every host
|
|
&& (systemctl disable vsftpd >/dev/null 2>&1 || true) \
|
|
&& rm -f /etc/systemd/system/multi-user.target.wants/vsftpd.service
|
|
|
|
# --- Kubernetes as packages -------------------------------------------------------------------
|
|
# A kind node carries kubelet, kubeadm and kubectl as loose binaries. Installing the same version
|
|
# from pkgs.k8s.io puts them under dpkg, so a task can hold a node back on an older patch release
|
|
# and the candidate upgrades it the documented way. kind's own kubelet unit and drop-ins live in
|
|
# /etc/systemd and keep taking precedence over the package's.
|
|
ARG K8S_VERSION
|
|
ARG K8S_MINOR
|
|
RUN 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 \
|
|
# kubeadm refuses to upgrade a node without the cpuset cgroup controller, which a rootless
|
|
# container does not get. The packaged binary is diverted and a wrapper (rootfs/usr/bin/kubeadm)
|
|
# passes --ignore-preflight-errors=SystemVerification to `kubeadm upgrade`, so the candidate
|
|
# types the same commands as on a real node. The diversion survives package upgrades.
|
|
&& dpkg-divert --add --rename --divert /usr/bin/kubeadm.real /usr/bin/kubeadm \
|
|
&& apt-get update \
|
|
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends -o Dpkg::Options::=--force-confold \
|
|
"kubelet=${K8S_VERSION}-*" "kubeadm=${K8S_VERSION}-*" "kubectl=${K8S_VERSION}-*" \
|
|
&& apt-mark hold kubelet kubeadm kubectl \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& test -x /usr/bin/kubeadm.real
|
|
|
|
# --- CKS tools --------------------------------------------------------------------------------
|
|
ARG KUBE_BENCH=0.16.0
|
|
ARG TRIVY=0.75.0
|
|
ARG KUBESEC=2.15.0
|
|
ARG KUBE_LINTER=0.8.3
|
|
ARG BOM=0.8.0
|
|
ARG COSIGN=3.1.3
|
|
ARG ISTIO=1.31.1
|
|
ARG GVISOR=20261005
|
|
RUN cd /tmp && G=https://github.com \
|
|
&& curl -fsSL "$G/aquasecurity/kube-bench/releases/download/v${KUBE_BENCH}/kube-bench_${KUBE_BENCH}_linux_amd64.tar.gz" | tar -xz kube-bench cfg \
|
|
&& install -m755 kube-bench /usr/local/bin/ && mkdir -p /etc/kube-bench && rm -rf /etc/kube-bench/cfg && mv cfg /etc/kube-bench/cfg && rm -f kube-bench \
|
|
&& curl -fsSL "$G/aquasecurity/trivy/releases/download/v${TRIVY}/trivy_${TRIVY}_Linux-64bit.tar.gz" | tar -xz -C /usr/local/bin trivy \
|
|
&& curl -fsSL "$G/controlplaneio/kubesec/releases/download/v${KUBESEC}/kubesec_linux_amd64.tar.gz" | tar -xz -C /usr/local/bin kubesec \
|
|
&& curl -fsSL "$G/stackrox/kube-linter/releases/download/v${KUBE_LINTER}/kube-linter-linux.tar.gz" | tar -xz -C /usr/local/bin kube-linter \
|
|
&& curl -fsSL -o /usr/local/bin/bom "$G/kubernetes-sigs/bom/releases/download/v${BOM}/bom-amd64-linux" \
|
|
&& curl -fsSL -o /usr/local/bin/cosign "$G/sigstore/cosign/releases/download/v${COSIGN}/cosign-linux-amd64" \
|
|
&& curl -fsSL "$G/istio/istio/releases/download/${ISTIO}/istioctl-${ISTIO}-linux-amd64.tar.gz" | tar -xz -C /usr/local/bin istioctl \
|
|
&& curl -fsSL "https://storage.googleapis.com/gvisor/releases/release/${GVISOR}/x86_64/gvisor.tar.bz2" | tar -xj -C /usr/local/bin \
|
|
&& chmod 755 /usr/local/bin/* \
|
|
&& chown -R root:root /usr/local/bin /etc/kube-bench
|
|
|
|
COPY rootfs/ /
|
|
RUN chmod 755 /usr/bin/kubeadm \
|
|
# gVisor as a second runtime handler ("runsc") beside runc; a RuntimeClass selects it
|
|
&& printf '\n[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]\n runtime_type = "io.containerd.runsc.v1"\n [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc.options]\n TypeUrl = "io.containerd.runsc.v1.options"\n ConfigPath = "/etc/containerd/runsc.toml"\n' >> /etc/containerd/config.toml \
|
|
# trivy reads /etc/trivy.yaml (the Docker Hub cache as a mirror, one cache directory for all
|
|
# users), for logins and for sudo alike
|
|
&& install -d -m 1777 /var/lib/trivy \
|
|
&& echo 'TRIVY_CONFIG=/etc/trivy.yaml' >> /etc/environment \
|
|
&& echo 'export TRIVY_CONFIG=/etc/trivy.yaml' >> /etc/exam.bashrc
|