💻 Boot it in QEMU
Put the UKI, the machine config and an admin identity onto a disk the way the installer would, then start the node in a local virtual machine with a software TPM.
You need:
- the lab folder, the admin identity and
$LAB/machine.yamlfrom Prepare the node; - QEMU, swtpm, OVMF,
sgdiskand mtools from What you need.
Run the commands from the root of the cryptos-appliance checkout.
task qemu:runThe Taskfile has a task qemu:run target, but it does not get you through this walkthrough. It boots the image with an empty state disk and no machine config, so the node waits in re-provision maintenance mode on the address DHCP gave it, and its port forward points at 10.0.0.10, an address the node only takes once it has a config. The steps below stage the config first, the way the integration test does.
1. Build the installed disk
Make a 2 GiB disk image with the two partitions the installer would create: a 512 MiB EFI partition named EFI, and the rest as cryptos-state, which the node encrypts on first boot. Then copy the machine config onto the EFI partition at EFI/cryptos/machine.yaml, where the node looks for it.
truncate -s 2G "$LAB/installed.img"
sgdisk \
--new=1:0:+512MiB --typecode=1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B --change-name=1:EFI \
--new=2:0:0 --typecode=2:CA7D7CCB-63ED-4C53-861C-1742536059CC --change-name=2:cryptos-state \
"$LAB/installed.img"
sgdisk --info=1 "$LAB/installed.img" | grep 'First sector'
sgdisk reports that it wrote the new table, then prints where the EFI partition starts:
First sector: 2048 (at 1024.0 KiB)
The EFI partition starts 1024 KiB, that is 1048576 bytes, into the image. If your number differs, multiply the first sector by 512 and use that in place of 1048576 below.
ESP="$LAB/installed.img@@1048576"
mformat -F -v EFI -i "$ESP" ::
mmd -i "$ESP" ::/EFI ::/EFI/cryptos
mcopy -i "$ESP" "$LAB/machine.yaml" ::/EFI/cryptos/machine.yaml
mdir -i "$ESP" ::/EFI/cryptos
The last command lists machine.yaml on the EFI partition. If mformat fails, the offset is probably wrong: check the first sector again.
2. Start the software TPM
swtpm socket --tpm2 --tpmstate dir="$LAB/swtpm" --ctrl type=unixio,path="$LAB/swtpm/sock" &
swtpm keeps the TPM's state in $LAB/swtpm and waits for QEMU on the socket.
On first boot the node seals its disk key to this software TPM and creates the Root key inside it. Keep $LAB/swtpm with installed.img. If you delete or swap the TPM folder, the node can never unlock its disk again, and the Root key is gone with it. The same happens if you boot this disk with a rebuilt UKI.
3. Boot the node
qemu-system-x86_64 \
-machine q35,accel=kvm:tcg -m 2048 -nographic \
-drive if=pflash,format=raw,unit=0,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
-drive if=pflash,format=raw,unit=1,file="$LAB/OVMF_VARS.fd" \
-chardev socket,id=chrtpm,path="$LAB/swtpm/sock" \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0 \
-drive format=raw,file="fat:rw:$LAB/esp" \
-drive if=none,id=state,format=raw,file="$LAB/installed.img" \
-device virtio-blk-pci,drive=state \
-netdev user,id=n0,net=10.0.0.0/24,host=10.0.0.1,hostfwd=tcp:127.0.0.1:4443-10.0.0.10:443 \
-device virtio-net-pci,netdev=n0
This terminal is now the node's serial console. The flags are the ones the integration test uses:
- 🔐 TPM:
-tpmdev emulatorwithtpm-tisconnects the VM to swtpm. - 💾 Disks: the
fat:rw:drive is the boot partition with the UKI;installed.imgis the node's disk, as a virtio disk. - 🌐 Network: QEMU's private network is
10.0.0.0/24with the host side at10.0.0.1. Only port 443 of the node is reachable, as127.0.0.1:4443on your machine. - ⚡ Speed:
accel=kvm:tcguses KVM when it can and falls back to software emulation when it cannot.
4. Watch the first boot
The console shows the CryptOS mark and the version, then each boot stage as it finishes. The debug image also prints the detailed kernel and init log in between.
- state volume. The
cryptos-statepartition is empty, so the node formats it as an encrypted LUKS2 volume, seals the key to the TPM, and creates a filesystem inside it. - configuration. The node reads
EFI/cryptos/machine.yaml, saves it inside the encrypted volume, and deletes the staged copy. - network. It sets its hostname and takes
10.0.0.10. - embedded etcd. It starts its internal database.
- management API. It opens the mutual-TLS API on port 443.
Each stage is marked [ok] as it finishes, mixed in with the detailed log lines. When management API is marked, the node is up and waiting for its ceremony, and the console moves on to the node's dashboard.
.-----. | .-----.
| | |
| | |
| .--+--. |
| |#####| |
| |#####| |
| '-----' |
| |
'---------------'
CryptOS PKI v0.1.0
[ok] state volume
[ok] configuration
[ok] network
[ok] embedded etcd
[ok] management API
A stage marked [!!] failed. The boot stops there and the node restarts; there is no shell to fall back to.
With KVM the boot is quick. Under software emulation, give it several minutes.
The dashboard takes keys from this terminal. Once the node has its Root, Ctrl-R opens the reset confirmation, and typing the Root CA's common name then Enter erases the node's identity. Press Esc to back out if you hit it by mistake.
Leave QEMU running and open a second terminal for the next pages.
QEMU quits on Ctrl-A, then X, but that is the same as pulling the power cord: the node gets no orderly shutdown. Once the ceremony is done, stop the node with cryptosctl reboot --power-off instead, as shown in Check identity and status.
Next step
Install the CLI: Install cryptosctl.