Linux template validation — 5 October 2026¶
Base commit: 743805e (main). Toolchain: Go 1.27.1, linux/amd64.
| Check | Result |
|---|---|
CI on main: gofmt, go vet, go test -race ./..., golangci-lint 2.14.0, govulncheck, gosec 2.29.0 |
Passed |
| CI Linux templates job (disk tools and libvirt schema installed, so integration tests run) | Passed |
| KubeVirt REST VM creation for eleven Linux and twelve Windows templates | Passed with local Kubernetes REST fixture |
| CDI persistent import/DataSource exports for all eleven Linux templates | Passed |
| Native libvirt lifecycle, restart recovery, project isolation, failure retention | Passed with injected virsh runner |
| Libvirt domain XML for all eleven Linux templates | Passed libvirt schema validation |
| Real QCOW2 conversion/resize and NoCloud ISO extraction | Passed; uses qemu-img, genisoimage and isoinfo |
kryton-image validate (cloud-config and domain XML for every template) |
Passed |
| Real guest boot / SSH / cloud-init / root expansion on libvirt | All eleven images passed (see below) |
| Real guest boot on KubeVirt | Blocked by lab cluster networking: guest boots, but DNS fails (see below) |
gosec findings for operator-controlled file paths and the fixed host-tool
allowlist carry narrow #nosec annotations with reasons; nothing is globally
suppressed. Those annotations are not evidence of guest boot certification.
Native libvirt on a real KVM host¶
Host: Ubuntu 24.04.5, kernel 6.8.0, libvirt 10.0.0, QEMU 8.2.2, qemu:///system,
network default. Deployed with
./scripts/deploy-remote.sh <host> <user> --key --apikey --provider libvirt --quick.
Images were acquired with kryton-image fetch:
| Image | Digest source | SHA-256 |
|---|---|---|
| ubuntu-24.04 | noble/current/SHA256SUMS, gpgv good signature from the Ubuntu cloud image key |
6a81c375…5bb4d2 |
| debian-12 | bookworm/latest/SHA512SUMS over HTTPS (no signature published there); bytes matched SHA-512, SHA-256 derived |
7b3faf64…bdecad |
| ubuntu-22.04 | jammy/current/SHA256SUMS, gpgv good signature from the Ubuntu cloud image key |
0c9811a8…57b210e |
| debian-13 | trixie/latest/SHA512SUMS over HTTPS; bytes matched SHA-512, SHA-256 derived |
b2aca2be…f23b9d1 |
| rocky-9 | CHECKSUM signed by the Rocky release key 2022 (21CB256A…350D275D), good signature |
92c206cc…ab4eec8 |
| almalinux-9 | CHECKSUM signed by the AlmaLinux OS 9 key (BF18AC28…CB86B3716), good signature |
6bdab637…002d74b |
| ubuntu-26.04 | resolute/current/SHA256SUMS, gpgv good signature from the Ubuntu cloud image key |
88006518…d97332b2 |
| rocky-10 | CHECKSUM signed by the Rocky Linux 10 key (FC226859…6FEDFC85), good signature |
9fc9e9ff…bbc4b48 |
| almalinux-10 | CHECKSUM signed by the AlmaLinux OS 10 key (EE6DB7B9…C2A1E572), good signature |
bc59485c…4da1e10a |
| centos-stream-10 | SHA256SUM over HTTPS (no signature published beside the image) |
42bd0c8f…a243c0 |
| fedora-44 | Fedora-Cloud-44-1.7-x86_64-CHECKSUM, clearsigned by the Fedora 44 key, good signature |
28680fe5…71f90b7f |
scripts/e2e-linux-templates.sh with the original six images, run on the host so
guests are reachable on the libvirt network (about 11 minutes):
debian-13: boot, SSH, cloud-init, guest agent, disk expansion passed
rocky-9: boot, SSH, cloud-init, guest agent, disk expansion passed
almalinux-9: boot, SSH, cloud-init, guest agent, disk expansion passed
ubuntu-22.04: boot, SSH, cloud-init, guest agent, disk expansion passed
ubuntu-24.04: boot, SSH, cloud-init, guest agent, disk expansion passed
debian-12: boot, SSH, cloud-init, guest agent, disk expansion passed
The five newer templates were added afterwards and run the same way (about 7 minutes):
ubuntu-26.04: boot, SSH, cloud-init, guest agent, disk expansion passed
rocky-10: boot, SSH, cloud-init, guest agent, disk expansion passed
almalinux-10: boot, SSH, cloud-init, guest agent, disk expansion passed
centos-stream-10: boot, SSH, cloud-init, guest agent, disk expansion passed
fedora-44: boot, SSH, cloud-init, guest agent, disk expansion passed
The first real runs found these defects, now fixed with regression tests:
- Debian 13 boot loop. Domains had no video adapter. Debian 13's BIOS
GRUB resets the guest when it hands off to the kernel on such a machine
(reproduced with plain QEMU
-nodefaults -vga none). Domains now include a VGA device. -
Rocky and AlmaLinux version check. They report
VERSION_ID="9.6"for the9template; the gate now accepts point releases. -
Debian never configured. Debian's
cloud-amd64kernel has no AHCI driver, so the SATA NoCloud CD-ROM was invisible (hostname stayedlocalhost, no DHCP). The seed now sits on a virtio-scsi controller. - SSH targeted loopback. The guest agent lists
lofirst, soipAddresses[0]was127.0.0.1. Loopback and link-local addresses are now dropped and IPv4 is listed first. - QEMU could not open disks. Per-machine directories are
0750; the machine data dir must be setgid and owned by the QEMU group.deploy-remote.sh --provider libvirtnow creates it2750 root:kvm.
Listing images re-hashed every artifact on each request (about 1 s for two images). Listings now check the manifest and size; provisioning still verifies the full digest.
Operator console¶
The UI was rebuilt as React + Vite (web/, built into cmd/krytond/web).
Checked in headless Chrome against the libvirt host at 1440 px and 390 px, in
light and dark themes: sign in (rejected and accepted keys), overview, images
(two stored, four "Fetch required" with the exact kryton-image command),
create Ubuntu 24.04 with an SSH key, drawer showing the agent IP and the
ssh ubuntu@<ip> command (used successfully from the host), delete with
confirmation. No console errors. npm run typecheck and 28 vitest tests pass.
KubeVirt on the lab cluster¶
Cluster: k3s with Cilium (kube-proxy replacement, socket load balancing),
KubeVirt v1.4.0, CDI and Longhorn. Ubuntu 24.04 and Debian 12 CDI
DataSources were imported from digest-checked URLs; krytond ran with
KRYTON_PROVIDER=kubevirt and KRYTON_STORAGE_CLASS=longhorn.
Ubuntu 24.04 provisioned, booted to a login prompt and applied its NoCloud
hostname, and SSH with the test key worked on the pod IP. The gate still
cannot pass on this cluster: the guest resolves through the cluster DNS
ClusterIP via masquerade, and Cilium's socket load balancer does not translate
packets forwarded from the VM's tap device. CoreDNS pod IPs and public DNS are
reachable from the guest, but the ClusterIP is not, so cloud-init hangs on
apt-get update and qemu-guest-agent is never installed. Fixing this is a
cluster change (for example Cilium bpf-lb-sock-hostns-only: true), not a
Kryton change.
The run also fixed two gate defects for KubeVirt:
- A long-lived
virtctl port-forwardstops accepting connections after its first failed dial, which happens while the guest is still booting. Each SSH attempt now opens its own tunnel withvirtctl port-forward --stdio. - Filesystem-mode PVCs lose a few percent to filesystem overhead (a 20 GiB Longhorn request gave a 19.5 GiB disk), so the KubeVirt disk check allows 5%.
Remaining¶
Re-run scripts/e2e-linux-templates.sh against KubeVirt on a cluster where
VM masquerade traffic can reach cluster DNS before promoting the KubeVirt
templates.
Native libvirt snapshots, VNC, live migration, Windows and ARM64 support,
ISO installation, offline image customization and artifact signing are future
extensions. This change provides cloud-image acquisition, Linux first-boot
initialization, deployment templates and native machine lifecycle.