Archived
Migrate nix-auto-installer into this flake
Folds the separate nix-auto-installer repo's build outputs into this
flake so it can build every auto-installer artifact it used to,
negating the need for that repo:
- modules/installer/{common,iso,proxmox-lxc}.nix — migrated from
nix-auto-installer's common.nix/installer.nix/proxmox-lxc.nix.
- flake.nix gains nixosConfigurations.{installer,proxmox-lxc} and
packages.x86_64-linux.{iso,lxc,pxe,all}, matching the original
repo's interface (nix build .#iso / .#lxc / .#pxe / .#all).
- Dropped the live Gitea PAT baked into every installer image via
environment.etc."git-credentials" — gitea.lan.ddnsgeek.com/beatzaplenty/nixos.git
allows anonymous read, so the installer (which only ever reads the
flake, never pushes) doesn't need a credential at all.
- installer_old.nix wasn't migrated — dead code, unreferenced by the
source repo's flake.nix, and carried a second stale leaked token.
- The installer environment's own hardcoded login password hash is
preserved as-is: sops-nix has no stable per-boot host key to derive
an age key from on ephemeral installer media, so it can't reuse the
same per-host secret mechanism the rest of this repo uses without
separate design work.
- vars.adminSshKey / vars.dockerHost-style dedup: the installer's SSH
authorized key and its FLAKE_BASE_URL domain were exact duplicates
of values already in variables.nix / modules/common/configuration.nix,
so both now reference the single source of truth instead.
Verified eval-equivalent for every existing host (drvPath-identical)
and confirmed the migrated auto-install.sh script renders byte-for-byte
identical to the source repo's output.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
let
|
let
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
inherit (nixpkgs) lib;
|
inherit (nixpkgs) lib;
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
vars = import ./variables.nix;
|
vars = import ./variables.nix;
|
||||||
|
|
||||||
# Generates a nixosConfiguration from a platform (what it runs on) and
|
# Generates a nixosConfiguration from a platform (what it runs on) and
|
||||||
@@ -86,9 +87,65 @@
|
|||||||
lxc-pxe-boot = mkTarget { platform = "lxc"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
|
lxc-pxe-boot = mkTarget { platform = "lxc"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Auto-install environments (migrated from the former nix-auto-installer
|
||||||
|
# flake): a self-contained NixOS installer that boots, discovers this
|
||||||
|
# flake's own nixosConfigurations over the network, and runs
|
||||||
|
# nixos-install against whichever one the operator picks. These are
|
||||||
|
# deliberately not part of the platform x build-type matrix above —
|
||||||
|
# they're throwaway boot media, not persistent hosts, so they skip
|
||||||
|
# disko/sops-nix/home-manager and just need `vars`.
|
||||||
|
installerTargets = {
|
||||||
|
installer = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
modules = [ ./modules/installer/iso.nix ];
|
||||||
|
specialArgs = { inherit vars; };
|
||||||
|
};
|
||||||
|
|
||||||
|
proxmox-lxc = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
modules = [ ./modules/installer/proxmox-lxc.nix ];
|
||||||
|
specialArgs = { inherit vars; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Same installer environment, built as netboot (kernel + initrd +
|
||||||
|
# iPXE script) instead of an ISO — this is what packages.pxe bundles.
|
||||||
|
netbootSystem = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
modules = [
|
||||||
|
./modules/installer/iso.nix
|
||||||
|
({ modulesPath, ... }: {
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/installer/netboot/netboot-minimal.nix")
|
||||||
|
];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
specialArgs = { inherit vars; };
|
||||||
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
nixosConfigurations = generatedTargets;
|
nixosConfigurations = generatedTargets // installerTargets;
|
||||||
|
|
||||||
|
# Buildable auto-installer artifacts (`nix build .#<name>`), migrated
|
||||||
|
# unchanged from nix-auto-installer's interface.
|
||||||
|
packages.${system} = rec {
|
||||||
|
iso = installerTargets.installer.config.system.build.isoImage;
|
||||||
|
|
||||||
|
lxc = installerTargets.proxmox-lxc.config.system.build.tarball;
|
||||||
|
|
||||||
|
pxe = pkgs.linkFarm "pxe" [
|
||||||
|
{ name = "netboot.ipxe"; path = netbootSystem.config.system.build.netbootIpxeScript; }
|
||||||
|
{ name = "initrd"; path = netbootSystem.config.system.build.netbootRamdisk; }
|
||||||
|
{ name = "kernel"; path = netbootSystem.config.system.build.kernel; }
|
||||||
|
];
|
||||||
|
|
||||||
|
all = pkgs.linkFarm "all" [
|
||||||
|
{ name = "iso"; path = iso; }
|
||||||
|
{ name = "lxc"; path = lxc; }
|
||||||
|
{ name = "pxe"; path = pxe; }
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
{ pkgs, lib, vars, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
networking.useDHCP = lib.mkDefault true;
|
||||||
|
|
||||||
|
time.timeZone = vars.timeZone;
|
||||||
|
|
||||||
|
# Without this, the installer only ever sees cache.nixos.org, which
|
||||||
|
# doesn't carry sops-install-secrets (it's built straight from the
|
||||||
|
# sops-nix flake's own Go source, not part of nixpkgs) — every install
|
||||||
|
# would otherwise compile it from scratch, which is what ran an 8GB LXC
|
||||||
|
# container's disk out of space. Push a built copy to nix-cache once
|
||||||
|
# (from a machine with real disk headroom) and every future install,
|
||||||
|
# of any type, fetches instead of rebuilding.
|
||||||
|
nix.settings = {
|
||||||
|
substituters = [
|
||||||
|
"http://nix-cache"
|
||||||
|
"https://cache.nixos.org/"
|
||||||
|
];
|
||||||
|
trusted-public-keys = [
|
||||||
|
"cache.local-1:usoWYanY3Kpq2+kDIS2nhWoLZiRxanmdysdzqCFBHW4="
|
||||||
|
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
systemPackages = with pkgs; [
|
||||||
|
git
|
||||||
|
curl
|
||||||
|
jq
|
||||||
|
parted
|
||||||
|
e2fsprogs
|
||||||
|
btrfs-progs
|
||||||
|
util-linux
|
||||||
|
disko
|
||||||
|
];
|
||||||
|
|
||||||
|
# Write auto-install script to /root
|
||||||
|
etc."auto-install.sh" = {
|
||||||
|
text = ''
|
||||||
|
#!/run/current-system/sw/bin/bash
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export FLAKE_BASE_URL="git+https://${vars.lanDomain}/beatzaplenty/nixos.git"
|
||||||
|
|
||||||
|
echo "Fetching available NixOS hosts from flake..."
|
||||||
|
mapfile -t options < <(
|
||||||
|
nix eval --json --no-use-registries --no-accept-flake-config --extra-experimental-features "flakes nix-command" \
|
||||||
|
"''${FLAKE_BASE_URL}#nixosConfigurations" \
|
||||||
|
--apply builtins.attrNames \
|
||||||
|
| jq -r '.[]'
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ ''${#options[@]} -eq 0 ]]; then
|
||||||
|
echo "ERROR: No NixOS hosts found in ''${FLAKE_BASE_URL}#nixosConfigurations" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Choose the flake profile to install:"
|
||||||
|
select choice in "''${options[@]}"; do
|
||||||
|
if [[ -n "$choice" ]]; then
|
||||||
|
echo "You selected: $choice"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "Invalid selection. Try again."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Starting install with flake: ''${FLAKE_BASE_URL}#''${choice}"
|
||||||
|
|
||||||
|
# Optional: confirm before proceeding
|
||||||
|
read -rp "Proceed with installation? (y/N): " confirm
|
||||||
|
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A nix-cache host is *the* substituter/remote-builder for every other
|
||||||
|
# host once installed (its own config explicitly excludes itself from
|
||||||
|
# using either — see buildType != "nix-cache" in the nixos flake.nix).
|
||||||
|
# Installing one shouldn't depend on a nix-cache substituter either,
|
||||||
|
# for the same reason — plus in practice "nix-cache" only resolves over
|
||||||
|
# Tailscale, which a fresh installer environment was never connected to
|
||||||
|
# anyway, so it's dead weight even for non-nix-cache installs until
|
||||||
|
# that's sorted out. Override it away here specifically for nix-cache
|
||||||
|
# targets to keep install-time behaviour consistent with run-time.
|
||||||
|
nix_extra_opts=()
|
||||||
|
if [[ "''${choice}" == *-nix-cache ]]; then
|
||||||
|
echo "Installing a nix-cache host — skipping the nix-cache substituter."
|
||||||
|
nix_extra_opts+=(--option substituters "https://cache.nixos.org/")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if nix eval --refresh --json --extra-experimental-features "flakes nix-command" "''${nix_extra_opts[@]}" "''${FLAKE_BASE_URL}#nixosConfigurations.''${choice}.config.disko.devices.disk.main.device" >/dev/null 2>&1; then
|
||||||
|
disko --mode destroy,format,mount \
|
||||||
|
--flake "''${FLAKE_BASE_URL}#''${choice}" "''${nix_extra_opts[@]}" --yes
|
||||||
|
else
|
||||||
|
# No raw disk to partition — true for every LXC container, which has
|
||||||
|
# no block device visible from inside it, only its already-mounted
|
||||||
|
# root filesystem. nixos-install defaults to installing into /mnt; if
|
||||||
|
# nothing points /mnt at the real root, the new system gets built and
|
||||||
|
# written to a disconnected empty directory, never actually becomes
|
||||||
|
# bootable, and the container silently keeps running this installer
|
||||||
|
# environment forever. Bind-mount / onto /mnt so the install actually
|
||||||
|
# lands on the filesystem this container boots from.
|
||||||
|
echo "Selected host has no Disko configuration — installing in place via bind mount."
|
||||||
|
mount --bind / /mnt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# sops-nix derives this host's decryption key from its own SSH host key
|
||||||
|
# at *activation* time, which runs before systemd would otherwise
|
||||||
|
# generate one on first boot. Without pre-seeding it here, secrets
|
||||||
|
# (including the login password) fail to decrypt on first boot.
|
||||||
|
# Generate the key + register it with `nixos`'s sops-nix setup ahead of
|
||||||
|
# time (see nix-auto-installer/scripts/prepare-host-key.sh), then scp it
|
||||||
|
# to /root/host-keys/ on this machine before continuing.
|
||||||
|
mkdir -p /root/host-keys
|
||||||
|
if [[ -f "/root/host-keys/''${choice}_ssh_host_ed25519_key" ]]; then
|
||||||
|
echo "Found pre-seeded SSH host key for ''${choice}, installing to target..."
|
||||||
|
install -D -m 0600 "/root/host-keys/''${choice}_ssh_host_ed25519_key" /mnt/etc/ssh/ssh_host_ed25519_key
|
||||||
|
install -D -m 0644 "/root/host-keys/''${choice}_ssh_host_ed25519_key.pub" /mnt/etc/ssh/ssh_host_ed25519_key.pub
|
||||||
|
else
|
||||||
|
echo "WARNING: no pre-seeded host key found at /root/host-keys/''${choice}_ssh_host_ed25519_key"
|
||||||
|
echo "sops-nix secrets (including the login password) will NOT decrypt on first boot."
|
||||||
|
echo "Run scripts/prepare-host-key.sh for host ''${choice} on your admin workstation first,"
|
||||||
|
echo "then scp the result here, if this host needs sops-nix secrets."
|
||||||
|
read -rp "Continue without a pre-seeded key anyway? (y/N): " skip_key
|
||||||
|
if [[ ! "$skip_key" =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /mnt/install-tmp
|
||||||
|
export TMPDIR=/mnt/install-tmp
|
||||||
|
|
||||||
|
nixos-install \
|
||||||
|
--flake "''${FLAKE_BASE_URL}#''${choice}" \
|
||||||
|
"''${nix_extra_opts[@]}" \
|
||||||
|
--no-root-password
|
||||||
|
|
||||||
|
|
||||||
|
rm -rf /mnt/install-tmp
|
||||||
|
# Redundant copy of the host's private key — the real one is now at
|
||||||
|
# /etc/ssh/ssh_host_ed25519_key. Nothing NixOS-managed ever cleans this
|
||||||
|
# up on its own since it was written imperatively, not declaratively.
|
||||||
|
rm -rf /root/host-keys
|
||||||
|
sleep 10
|
||||||
|
reboot
|
||||||
|
'';
|
||||||
|
|
||||||
|
mode = "0755";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.git.enable = true;
|
||||||
|
|
||||||
|
# Run the installer on first login. Previously this copied an /etc file
|
||||||
|
# into the nixos user's ~/.bash_profile via an activation script that
|
||||||
|
# got dropped in a refactor (and only ever worked for that one user
|
||||||
|
# anyway) — loginShellInit is NixOS's native hook for this, applies to
|
||||||
|
# any user's login shell (root included), and needs no home-directory
|
||||||
|
# file-copying/chown.
|
||||||
|
programs.bash.loginShellInit = ''
|
||||||
|
if [ -n "$PS1" ] && [ ! -e "$HOME/.auto_install_ran" ]; then
|
||||||
|
sudo /etc/auto-install.sh
|
||||||
|
touch "$HOME/.auto_install_ran"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
services.openssh.enable = true;
|
||||||
|
|
||||||
|
services.openssh.settings = {
|
||||||
|
PermitRootLogin = "yes";
|
||||||
|
PasswordAuthentication = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.root = {
|
||||||
|
hashedPassword =
|
||||||
|
"$6$Kwv9KAyvcurAViQF$H4.u3feqGE7lVoNgkFXhE3n2Pmo//9JYDTCz8ifrVHBxPjwa1xMby7tEZ8Bpt5MXs9Rkx6/YbZWxs5CpH0s/70";
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.${vars.primaryUser} = {
|
||||||
|
isNormalUser = true;
|
||||||
|
|
||||||
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
|
];
|
||||||
|
|
||||||
|
shell = pkgs.bashInteractive;
|
||||||
|
|
||||||
|
hashedPassword =
|
||||||
|
"$6$Kwv9KAyvcurAViQF$H4.u3feqGE7lVoNgkFXhE3n2Pmo//9JYDTCz8ifrVHBxPjwa1xMby7tEZ8Bpt5MXs9Rkx6/YbZWxs5CpH0s/70";
|
||||||
|
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
vars.adminSshKey
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{ modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
"${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix"
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{ modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
(modulesPath + "/virtualisation/proxmox-lxc.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
# LXC has no bootloader
|
||||||
|
boot.loader.grub.enable = false;
|
||||||
|
boot.loader.systemd-boot.enable = false;
|
||||||
|
}
|
||||||
@@ -25,6 +25,10 @@
|
|||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxXTQxFnArK5HXG7czeoybZebCGfxpUdusJkPn+BCSp root@server"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxXTQxFnArK5HXG7czeoybZebCGfxpUdusJkPn+BCSp root@server"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Admin SSH public key, authorized on the primary user of every host and
|
||||||
|
# the installer image's nixos/root users.
|
||||||
|
adminSshKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCq/Q5LvIXlZwO2kdeAN5nLGZ59nZB7JHYMEszHxmNtGMzv1lM31jiPNsr0z2EKVZhE7OOfa2IF9rhWYD7JUA9G0yzdZ4WTXFNGVVOJoOVH6vAF3XCxoVilOEwTc7h2Wiy+rzd0B28/3spffzQQWJhY6GRQVa8j+6xAGF60Fcvl1vLosYT9Bn2ZbK4TCWOwAn2jqXIieGpZdn/UNZbGOeKRiCvhktDfMAzuQzN/9jMu/oF4pkPn2X1UrsQdNlvp0Ci8md612MozIpncQJyAF1ADhunr3sMx0isUXiqD29R5DS4TftpekqLNLak+zcxFa8N7DcRNp3DcKfJvyTkwQrR4r+b7lFLYOLHLagSso9CzeW/paAS2q9I5SBm/2DtE1diLLg2jZikYcstsu/G5RgvbzbKqjiaMwTdXC3AMvDxQrs7U5pDRZFzoofG3cpODbTm+uy3m0kP70z0M1K45UbDG0p+itnTu9x40JbQEgefbx38AItNvAIx1A8HO4I1VX28= wayne@stream";
|
||||||
|
|
||||||
# System
|
# System
|
||||||
timeZone = "Australia/Brisbane";
|
timeZone = "Australia/Brisbane";
|
||||||
primaryUser = "nixos"; # main interactive user on every host
|
primaryUser = "nixos"; # main interactive user on every host
|
||||||
|
|||||||
Reference in New Issue
Block a user