Virtualisation

12

QEMU

krypt/sh runs QEMU fully rootless. Networking is provided by passt — a userspace network stack that requires no privileges and no tun/tap devices. Display is via -display gtk using the Wayland backend.

Installation

# pkg add qemu passt

Your user needs to be in the kvm group for hardware acceleration: usermod -aG kvm username.

Running a VM

# Start passt first
$ passt --socket /tmp/passt-kali.sock &
# Launch VM
$ qemu-system-x86_64 \
    -enable-kvm \
    -m 2048 \
    -cpu host -smp 4 \
    -drive file=kali.qcow2,format=qcow2,if=virtio \
    -netdev stream,id=net0,server=off,addr.type=unix,addr.path=/tmp/passt-kali.sock \
    -device virtio-net-pci,netdev=net0 \
    -device virtio-tablet-pci \
    -device virtio-vga,xres=1920,yres=1080 \
    -display gtk &

Rootless networking (passt)

passt provides NAT networking to QEMU VMs without root, tun/tap, or kernel modules. For an internal network between two VMs (such as Whonix Gateway and Workstation), use a multicast socket:

# Gateway — internal interface (second -netdev)
-netdev socket,id=internal,mcast=230.0.0.1:1234,localaddr=127.0.0.1 \
-device virtio-net-pci,netdev=internal \
# Workstation — same mcast address
-netdev socket,id=internal,mcast=230.0.0.1:1234,localaddr=127.0.0.1 \
-device virtio-net-pci,netdev=internal \
localaddr=127.0.0.1 is required. Without it, QEMU binds the multicast socket to the wireless interface and the VMs cannot see each other.

Clipboard sharing

Clipboard sharing between the krypt/sh host and QEMU guests uses the qemu-vdagent chardev — the agent protocol without the SPICE display stack. Build QEMU with --enable-gtk-clipboard and add to the launch command:

# Add to QEMU launch flags
-device virtio-serial,packed=on,ioeventfd=on \
-device virtserialport,name=com.redhat.spice.0,chardev=vdagent0 \
-chardev qemu-vdagent,id=vdagent0,name=vdagent,clipboard=on,mouse=off \
# Inside the guest — install and start spice-vdagent
$ systemctl enable --now spice-vdagentd
13

Podman

Podman runs containers rootlessly on krypt/sh. The runtime stack is podmancrun (OCI runtime) → aardvark-dns + netavark (networking) → catatonit (init). No Docker daemon. No root required.

Installation

# pkg add podman crun aardvark-dns netavark catatonit

The kernel must have user namespaces, cgroups v2, and overlay filesystem enabled — see the Security & sandboxing and Cgroups sections.

Rootless containers

Configure subuid and subgid ranges for your user — required for rootless operation:

# Add to /etc/subuid and /etc/subgid
# echo "username:100000:65536" >> /etc/subuid
# echo "username:100000:65536" >> /etc/subgid
# Initialize the podman user configuration
$ podman system migrate

Running containers

# Run a container
$ podman run --rm -it alpine sh
# Run detached with a name
$ podman run -d --name myapp -p 8080:80 nginx
# List running containers
$ podman ps
# Stop and remove
$ podman stop myapp && podman rm myapp
14

Xen

krypt/sh supports Xen as a paravirtualised hypervisor for hard VM isolation — each domU runs in its own hardware-enforced trust boundary. The boot path is limine → multiboot2 → xen.gz + vmlinuz. Direct xen.efi EFI boot is not supported on all firmware — limine is the correct and tested bootloader.

dom0 kernel

The Linux kernel running as dom0 requires Xen-specific options. Build the kernel with make LLVM=1 and enable:

CONFIG_XEN=y
CONFIG_XEN_DOM0=y
CONFIG_XEN_PRIVILEGED_GUEST=y
CONFIG_XEN_BALLOON=y
CONFIG_XEN_SCRUB_PAGES_DEFAULT=y
CONFIG_XEN_DEV_EVTCHN=y
CONFIG_XEN_BACKEND=y
CONFIG_XEN_NETDEV_BACKEND=m
CONFIG_XEN_BLKDEV_BACKEND=m
CONFIG_XEN_PCIDEV_BACKEND=m
CONFIG_XEN_GNTDEV=y
CONFIG_XEN_GRANT_DEV_ALLOC=y
CONFIG_SWIOTLB_XEN=y
CONFIG_XEN_FBDEV_FRONTEND=y
CONFIG_XEN_SAVE_RESTORE=y
CONFIG_XEN_ACPI_PROCESSOR=y

Booting with limine

Install limine as BOOTX64.EFI and configure it to load Xen via multiboot2. Place xen.gz and the dom0 kernel on the ESP:

timeout: 5
default_entry: 1
/Xen dom0 krypt/sh
    protocol: multiboot2
    path: boot():/xen.gz
    cmdline: dom0_mem=6144M,max:6144M dom0_max_vcpus=4 iommu=no noreboot
    module_path: boot():/vmlinuz-6.12.77
    module_cmdline: root=/dev/nvme0n1p6 rw quiet
xen.gz must be on the ESP. Copy it from /boot/xen.gz to /boot/efi/xen.gz after every Xen package update. limine reads from the ESP only.

Xen runit services

Three runit services are required for Xen toolstack operation. All services guard against non-dom0 boots — they exit cleanly if /proc/xen/capabilities does not contain control_d. Do not symlink them into /service/ until after a confirmed first Xen boot.

ServicePurpose
xenstoredXenStore daemon — required first, all other tools depend on it
xencommonsXencommons setup — hotplug and backend services
xenconsoledConsole multiplexer — access domU consoles via xl console
# ln -s /etc/sv/xenstored  /service/
# ln -s /etc/sv/xencommons  /service/
# ln -s /etc/sv/xenconsoled /service/

Creating domUs

domUs are created with xl. A basic PVH domU configuration using a LUKS-encrypted partition:

name = "fedora"
type = "pvh"
memory = 4096
vcpus = 4
disk = [ "/dev/mapper/fedora,raw,xvda,rw" ]
vif  = [ "bridge=xenbr0" ]
bootloader = "pygrub"
# Open LUKS partition first
# cryptsetup open /dev/nvme0n1p9 fedora
# Create and start the domU
# xl create fedora.cfg
# List running domains
# xl list
# Connect to console
# xl console fedora
# Shutdown
# xl shutdown fedora

LLVM musl libc libressl Independent