nixosModules.image builds a GPT disk image with systemd-repart: ESP with a UKI carrying the store's verity root hash, an erofs /nix/store on a dm-verity partition, a tmpfs root, an immutable /etc overlay and an ext4 state partition on /var. No Nix on the device; it is replaced as a whole. The store is made with 4 KiB erofs blocks: systemd-repart passes the 512 byte sector size to mkfs.erofs, and libblkid cannot verify the checksum of such a superblock, so udev never reported the file system and the initrd waited forever for /dev/mapper/usr. examples/image (nix build .#image) is a hardware test build with a debug console password; checks.image boots the image under UEFI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
3.3 KiB
Nix
116 lines
3.3 KiB
Nix
# Appliance image: a GPT disk image with a dm-verity protected, read-only
|
|
# /nix/store partition, a volatile (tmpfs) root and a small persistent state
|
|
# partition for /var. Built with systemd-repart; the unified kernel image on the
|
|
# ESP carries the hash of the store partition, so a modified store does not boot.
|
|
#
|
|
# The system cannot be rebuilt on the device (no Nix); it is replaced as a whole.
|
|
# Importing this module turns a configuration into such an image
|
|
# (`config.system.build.image`).
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
modulesPath,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.router.image;
|
|
inherit (config.image.repart.verityStore) partitionIds;
|
|
efiArch = pkgs.stdenv.hostPlatform.efiArch;
|
|
in
|
|
{
|
|
imports = [
|
|
(modulesPath + "/image/repart.nix")
|
|
(modulesPath + "/profiles/image-based-appliance.nix")
|
|
(modulesPath + "/profiles/perlless.nix")
|
|
];
|
|
|
|
options.router.image = {
|
|
espSize = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "128M";
|
|
description = "Size of the EFI system partition (holds the unified kernel image).";
|
|
};
|
|
stateSize = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "512M";
|
|
description = "Minimum size of the persistent state partition mounted on /var.";
|
|
};
|
|
};
|
|
|
|
config = {
|
|
system.image.id = lib.mkDefault "nixos-router";
|
|
|
|
# No Nix on the device: the rollback helpers need it, and boot counting
|
|
# takes over their job in images.
|
|
router.rollback.enable = false;
|
|
|
|
boot.loader.grub.enable = false;
|
|
|
|
fileSystems = {
|
|
"/" = {
|
|
fsType = "tmpfs";
|
|
options = [
|
|
"mode=0755"
|
|
"size=50%"
|
|
];
|
|
};
|
|
# The only persistent, writable place: DHCP leases, SSH host keys,
|
|
# journal, secrets (/var/lib/router).
|
|
"/var" = {
|
|
device = "/dev/disk/by-partlabel/state";
|
|
fsType = "ext4";
|
|
options = [
|
|
"nosuid"
|
|
"nodev"
|
|
];
|
|
};
|
|
};
|
|
|
|
# /etc is a read-only overlay; users are created by userborn, which keeps
|
|
# its password files on the state partition.
|
|
system.etc.overlay.mutable = false;
|
|
services.userborn.passwordFilesLocation = "/var/lib/nixos";
|
|
|
|
# Host keys live on the state partition as well.
|
|
services.openssh.hostKeys = [
|
|
{
|
|
path = "/var/lib/ssh/ssh_host_ed25519_key";
|
|
type = "ed25519";
|
|
}
|
|
];
|
|
|
|
# /usr is the read-only verity mount; there is nothing to create there.
|
|
system.activationScripts.usrbinenv = lib.mkForce "";
|
|
|
|
image.repart = {
|
|
enable = true;
|
|
|
|
# systemd-repart passes the image's sector size (512) to mkfs.erofs as the
|
|
# block size; libblkid cannot verify the superblock checksum of an erofs
|
|
# with blocks <= 1 KiB, so udev never reports the store's file system and
|
|
# systemd waits forever for /dev/mapper/usr. Appended options win.
|
|
mkfsOptions.erofs = [ "-b4096" ];
|
|
verityStore = {
|
|
enable = true;
|
|
# The firmware's default boot path: no NVRAM entry needed.
|
|
ukiPath = "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI";
|
|
};
|
|
|
|
partitions = {
|
|
${partitionIds.esp}.repartConfig = {
|
|
Type = "esp";
|
|
Format = "vfat";
|
|
SizeMinBytes = cfg.espSize;
|
|
};
|
|
"30-state".repartConfig = {
|
|
Type = "var";
|
|
Format = "ext4";
|
|
Label = "state";
|
|
SizeMinBytes = cfg.stateSize;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|