The installer ISO no longer needs a prebuilt system: `router-setup` asks for the disk, the WAN and LAN interfaces, DHCP or PPPoE (optionally on a VLAN), host name, LAN address and a password, writes settings.json and the secrets, builds that router offline (the ISO carries a reference router system whose closure has everything the build needs) and installs it with `router-install --system`. The password is used for the web UI and, hashed, as root's console password (routerSettings sets root's hashedPasswordFile; it applies when the account is created at installation). checks.setup answers router-setup on the console and checks the installed box: its settings, the web UI and console login with that password, and a rebuild that reproduces the installed system. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
128 lines
4.1 KiB
Nix
128 lines
4.1 KiB
Nix
# `router-install`: write a prebuilt router system to a disk. Nothing is built
|
|
# on the box; the system closure travels with the installer.
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.router.installer;
|
|
|
|
installScript = pkgs.writeShellApplication {
|
|
name = "router-install";
|
|
runtimeInputs = [
|
|
config.system.build.nixos-install
|
|
pkgs.coreutils
|
|
pkgs.dosfstools
|
|
pkgs.e2fsprogs
|
|
pkgs.gptfdisk
|
|
pkgs.util-linux
|
|
];
|
|
text = ''
|
|
usage() {
|
|
echo "usage: router-install [--yes] [--secrets DIR] [--system PATH] <disk>" >&2
|
|
echo " Wipes <disk> (e.g. /dev/nvme0n1) and installs a router system." >&2
|
|
echo " --secrets DIR copied to /var/lib/router (PPPoE password, WireGuard keys, ...)" >&2
|
|
echo " --system PATH the system to install${lib.optionalString (cfg.system != null) " (default: ${cfg.system})"}" >&2
|
|
exit 2
|
|
}
|
|
|
|
yes=0
|
|
secrets=
|
|
system=${if cfg.system != null then toString cfg.system else ""}
|
|
disk=
|
|
while [ $# -gt 0 ]; do
|
|
case $1 in
|
|
--yes) yes=1 ;;
|
|
--secrets) shift; [ $# -gt 0 ] || usage; secrets=$1 ;;
|
|
--system) shift; [ $# -gt 0 ] || usage; system=$1 ;;
|
|
-*) usage ;;
|
|
*) [ -z "$disk" ] || usage; disk=$1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
[ -n "$disk" ] || usage
|
|
[ -n "$system" ] || usage
|
|
[ -x "$system/bin/switch-to-configuration" ] || { echo "router-install: $system is not a NixOS system" >&2; exit 1; }
|
|
|
|
[ "$(id -u)" -eq 0 ] || { echo "router-install: must run as root" >&2; exit 1; }
|
|
[ -b "$disk" ] || { echo "router-install: $disk is not a block device" >&2; exit 1; }
|
|
if [ -n "$secrets" ] && [ ! -d "$secrets" ]; then
|
|
echo "router-install: $secrets is not a directory" >&2
|
|
exit 1
|
|
fi
|
|
if lsblk -nr -o MOUNTPOINTS "$disk" | grep -q .; then
|
|
echo "router-install: $disk or one of its partitions is mounted; refusing" >&2
|
|
exit 1
|
|
fi
|
|
[ -d /sys/firmware/efi ] || echo "warning: not booted in UEFI mode; the box itself must boot UEFI" >&2
|
|
|
|
echo "About to ERASE $disk:"
|
|
lsblk -o NAME,SIZE,MODEL,FSTYPE,LABEL "$disk"
|
|
if [ "$yes" -ne 1 ]; then
|
|
printf 'Type the device name (%s) to continue: ' "$disk"
|
|
read -r answer
|
|
[ "$answer" = "$disk" ] || { echo "aborted" >&2; exit 1; }
|
|
fi
|
|
|
|
part() {
|
|
case $disk in
|
|
*[0-9]) echo "''${disk}p$1" ;;
|
|
*) echo "$disk$1" ;;
|
|
esac
|
|
}
|
|
|
|
echo ">> partitioning"
|
|
sgdisk --zap-all "$disk" >/dev/null
|
|
sgdisk -n 1:0:+512M -t 1:ef00 -c 1:boot -n 2:0:0 -t 2:8300 -c 2:nixos "$disk" >/dev/null
|
|
partprobe "$disk" || true
|
|
udevadm settle
|
|
|
|
echo ">> formatting"
|
|
mkfs.fat -F 32 -n boot "$(part 1)" >/dev/null
|
|
mkfs.ext4 -q -F -L nixos "$(part 2)"
|
|
|
|
echo ">> mounting"
|
|
mkdir -p /mnt
|
|
mount "$(part 2)" /mnt
|
|
mkdir -p /mnt/boot
|
|
mount -o umask=0077 "$(part 1)" /mnt/boot
|
|
|
|
if [ -n "$secrets" ]; then
|
|
echo ">> copying secrets"
|
|
install -d -m 0700 /mnt/var/lib/router
|
|
cp -rT "$secrets" /mnt/var/lib/router
|
|
chown -R root:root /mnt/var/lib/router
|
|
chmod -R go-rwx /mnt/var/lib/router
|
|
else
|
|
echo "note: no --secrets given; put the PPPoE password etc. in /var/lib/router later" >&2
|
|
fi
|
|
|
|
echo ">> installing $system"
|
|
nixos-install --system "$system" --root /mnt --no-root-passwd --no-channel-copy
|
|
|
|
echo ">> unmounting"
|
|
umount -R /mnt
|
|
sync
|
|
echo "Done. Remove the installer and reboot."
|
|
'';
|
|
};
|
|
in
|
|
{
|
|
options.router.installer.system = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.package;
|
|
default = null;
|
|
description = ''
|
|
The system (a `system.build.toplevel`) `router-install` writes to disk;
|
|
null if it is always given with `--system` (as `router-setup` does).
|
|
'';
|
|
};
|
|
|
|
config = {
|
|
environment.systemPackages = [ installScript ];
|
|
# The closure must be on the installer itself.
|
|
system.extraDependencies = lib.optional (cfg.system != null) cfg.system;
|
|
};
|
|
}
|