Archived
Merge pull request 'Worktree debian pxe' (#73) from worktree-debian-pxe into main
Reviewed-on: #73
This commit is contained in:
+40
-4
@@ -15,6 +15,7 @@ rescue/inspection use.
|
|||||||
- TFTP root for first-stage bootloaders: `/srv/pxe/tftp`
|
- TFTP root for first-stage bootloaders: `/srv/pxe/tftp`
|
||||||
- iPXE entry script: `/srv/pxe/http/boot.ipxe`
|
- iPXE entry script: `/srv/pxe/http/boot.ipxe`
|
||||||
- Generated iPXE menu: `/srv/pxe/http/menu.ipxe`
|
- Generated iPXE menu: `/srv/pxe/http/menu.ipxe`
|
||||||
|
- Debian Minimal iPXE script: `/srv/pxe/http/debian.ipxe`
|
||||||
- SystemRescue iPXE script: `/srv/pxe/http/systemrescue.ipxe`
|
- SystemRescue iPXE script: `/srv/pxe/http/systemrescue.ipxe`
|
||||||
- TFTP fallback script: `/srv/pxe/tftp/autoexec.ipxe`
|
- TFTP fallback script: `/srv/pxe/tftp/autoexec.ipxe`
|
||||||
- Boot binaries copied from the Nix `ipxe` package:
|
- Boot binaries copied from the Nix `ipxe` package:
|
||||||
@@ -28,18 +29,23 @@ The host creates these directories with systemd tmpfiles:
|
|||||||
```text
|
```text
|
||||||
/srv/pxe
|
/srv/pxe
|
||||||
/srv/pxe/http
|
/srv/pxe/http
|
||||||
/srv/pxe/http/images
|
/srv/pxe/http/images -> /mnt/pxe-images (symlink to NFS share)
|
||||||
/srv/pxe/http/auto-installer
|
/srv/pxe/http/auto-installer
|
||||||
/srv/pxe/http/nixos-minimal
|
/srv/pxe/http/nixos-minimal
|
||||||
|
/srv/pxe/http/debian
|
||||||
/srv/pxe/http/systemrescue
|
/srv/pxe/http/systemrescue
|
||||||
/srv/pxe/http/ubuntu
|
/srv/pxe/http/ubuntu
|
||||||
/srv/pxe/http/rescue
|
/srv/pxe/http/rescue
|
||||||
/srv/pxe/tftp
|
/srv/pxe/tftp
|
||||||
```
|
```
|
||||||
|
|
||||||
Mount shared image storage under `/srv/pxe/http`, preferably
|
`/srv/pxe/http/images` is a symlink to `/mnt/pxe-images`, which is an NFSv4.2
|
||||||
`/srv/pxe/http/images` unless a menu entry expects files in a specific
|
mount of `server.sweet.home:/tank/proxmox/pxe-images`
|
||||||
directory such as `/srv/pxe/http/auto-installer`.
|
(`modules/pxe-boot/mount-pxe-images.nix`). Place large images there (ISOs,
|
||||||
|
disk images) rather than on the pxe-boot host's own root disk. For an LXC
|
||||||
|
pxe-boot container the mount uses `nofail` (eager, non-blocking on server
|
||||||
|
unavailability); for a Proxmox VM it uses `x-systemd.automount` (lazy,
|
||||||
|
triggered on first access).
|
||||||
|
|
||||||
The HTTP iPXE chain is:
|
The HTTP iPXE chain is:
|
||||||
|
|
||||||
@@ -54,6 +60,7 @@ The generated menu currently exposes entries for:
|
|||||||
|
|
||||||
- NixOS Auto-Installer
|
- NixOS Auto-Installer
|
||||||
- NixOS Minimal
|
- NixOS Minimal
|
||||||
|
- Debian Minimal
|
||||||
- SystemRescue environment
|
- SystemRescue environment
|
||||||
- iPXE shell
|
- iPXE shell
|
||||||
- Reboot
|
- Reboot
|
||||||
@@ -84,12 +91,38 @@ directory name (`auto-installer` / `nixos-minimal`), so each one's
|
|||||||
generated system name (`nixos-system-<name>-*`) is self-describing rather
|
generated system name (`nixos-system-<name>-*`) is self-describing rather
|
||||||
than the nixpkgs default of `nixos-system-nixos-*` for both.
|
than the nixpkgs default of `nixos-system-nixos-*` for both.
|
||||||
|
|
||||||
|
The Debian Minimal entry chains `http://<pxeServerIp>/debian.ipxe`, which loads
|
||||||
|
the Debian bookworm netboot kernel and initrd from `/srv/pxe/http/debian/`. The
|
||||||
|
`fetch-debian-netboot.service` oneshot downloads these files from
|
||||||
|
`deb.debian.org` on first boot (idempotent — skips if files are already
|
||||||
|
present):
|
||||||
|
|
||||||
|
```text
|
||||||
|
/srv/pxe/http/debian/linux (Debian bookworm netboot kernel)
|
||||||
|
/srv/pxe/http/debian/initrd.gz (Debian bookworm netboot initrd)
|
||||||
|
```
|
||||||
|
|
||||||
|
The service requires outbound internet access on the pxe-boot host. To
|
||||||
|
re-download (e.g. after a Debian point release), delete the files and restart
|
||||||
|
the service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm /srv/pxe/http/debian/linux /srv/pxe/http/debian/initrd.gz
|
||||||
|
systemctl restart fetch-debian-netboot.service
|
||||||
|
```
|
||||||
|
|
||||||
|
To update to a different Debian release, change `debianRelease` in
|
||||||
|
`modules/build-types/pxe-boot.nix` and redeploy.
|
||||||
|
|
||||||
The SystemRescue entry expects the source ISO at:
|
The SystemRescue entry expects the source ISO at:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
/srv/pxe/http/images/systemrescue.iso
|
/srv/pxe/http/images/systemrescue.iso
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Since `/srv/pxe/http/images` is the NFS-backed symlink, place the ISO on the
|
||||||
|
NFS share at `server.sweet.home:/tank/proxmox/pxe-images/systemrescue.iso`.
|
||||||
|
|
||||||
The `stage-systemrescue.service` oneshot extracts that ISO into:
|
The `stage-systemrescue.service` oneshot extracts that ISO into:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -113,6 +146,9 @@ After deployment by an operator, basic service checks are:
|
|||||||
```bash
|
```bash
|
||||||
curl http://pxe-boot/boot.ipxe
|
curl http://pxe-boot/boot.ipxe
|
||||||
curl http://pxe-boot/menu.ipxe
|
curl http://pxe-boot/menu.ipxe
|
||||||
|
curl http://pxe-boot/debian.ipxe
|
||||||
|
curl -I http://pxe-boot/debian/linux
|
||||||
|
curl -I http://pxe-boot/debian/initrd.gz
|
||||||
curl http://pxe-boot/systemrescue.ipxe
|
curl http://pxe-boot/systemrescue.ipxe
|
||||||
curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/vmlinuz
|
curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/vmlinuz
|
||||||
curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/sysresccd.img
|
curl -I http://pxe-boot/systemrescue/sysresccd/boot/x86_64/sysresccd.img
|
||||||
|
|||||||
@@ -21,6 +21,39 @@ let
|
|||||||
chain ${pxeBaseUrl}/boot.ipxe
|
chain ${pxeBaseUrl}/boot.ipxe
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
debianRelease = "bookworm";
|
||||||
|
debianMirror = "https://deb.debian.org/debian";
|
||||||
|
debianNetbootBase = "${debianMirror}/dists/${debianRelease}/main/installer-amd64/current/images/netboot/debian-installer/amd64";
|
||||||
|
|
||||||
|
debianIpxe = pkgs.writeText "debian.ipxe" ''
|
||||||
|
#!ipxe
|
||||||
|
|
||||||
|
set base ${pxeBaseUrl}
|
||||||
|
|
||||||
|
kernel ''${base}/debian/linux
|
||||||
|
initrd ''${base}/debian/initrd.gz
|
||||||
|
boot
|
||||||
|
'';
|
||||||
|
|
||||||
|
fetchDebianNetboot = pkgs.writeShellScript "fetch-debian-netboot" ''
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
dir="${httpRoot}/debian"
|
||||||
|
mirror="${debianNetbootBase}"
|
||||||
|
|
||||||
|
if [ -f "$dir/linux" ] && [ -f "$dir/initrd.gz" ]; then
|
||||||
|
echo "Debian ${debianRelease} netboot files already present; skipping download."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Downloading Debian ${debianRelease} netboot kernel and initrd from $mirror ..."
|
||||||
|
${pkgs.curl}/bin/curl -fsSL -o "$dir/linux.tmp" "$mirror/linux"
|
||||||
|
${pkgs.curl}/bin/curl -fsSL -o "$dir/initrd.gz.tmp" "$mirror/initrd.gz"
|
||||||
|
mv "$dir/linux.tmp" "$dir/linux"
|
||||||
|
mv "$dir/initrd.gz.tmp" "$dir/initrd.gz"
|
||||||
|
echo "Debian ${debianRelease} netboot files staged."
|
||||||
|
'';
|
||||||
|
|
||||||
systemRescueIpxe = pkgs.writeText "systemrescue.ipxe" ''
|
systemRescueIpxe = pkgs.writeText "systemrescue.ipxe" ''
|
||||||
#!ipxe
|
#!ipxe
|
||||||
|
|
||||||
@@ -70,6 +103,7 @@ let
|
|||||||
menu PXE Boot Menu
|
menu PXE Boot Menu
|
||||||
item auto-installer NixOS Auto-Installer
|
item auto-installer NixOS Auto-Installer
|
||||||
item nixos-minimal NixOS Minimal
|
item nixos-minimal NixOS Minimal
|
||||||
|
item debian Debian Minimal
|
||||||
item rescue Rescue Environment
|
item rescue Rescue Environment
|
||||||
item shell iPXE Shell
|
item shell iPXE Shell
|
||||||
item reboot Reboot
|
item reboot Reboot
|
||||||
@@ -82,6 +116,9 @@ let
|
|||||||
:nixos-minimal
|
:nixos-minimal
|
||||||
chain ''${base}/nixos-minimal/netboot.ipxe
|
chain ''${base}/nixos-minimal/netboot.ipxe
|
||||||
|
|
||||||
|
:debian
|
||||||
|
chain ''${base}/debian.ipxe
|
||||||
|
|
||||||
:rescue
|
:rescue
|
||||||
chain ''${base}/systemrescue.ipxe
|
chain ''${base}/systemrescue.ipxe
|
||||||
|
|
||||||
@@ -95,6 +132,7 @@ in
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
../pxe-boot/stage-installer-artifacts.nix
|
../pxe-boot/stage-installer-artifacts.nix
|
||||||
|
../pxe-boot/mount-pxe-images.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
@@ -129,34 +167,56 @@ in
|
|||||||
openssh.settings.PermitRootLogin = "yes";
|
openssh.settings.PermitRootLogin = "yes";
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd = {
|
||||||
"d ${pxeRoot} 0755 root root -"
|
tmpfiles.rules = [
|
||||||
"d ${httpRoot} 0755 root root -"
|
"d ${pxeRoot} 0755 root root -"
|
||||||
"d ${httpRoot}/images 0755 root root -"
|
"d ${httpRoot} 0755 root root -"
|
||||||
"d ${httpRoot}/auto-installer 0755 root root -"
|
"L+ ${httpRoot}/images - - - - ${vars.nfsShares.proxmoxPxeImages.mountpoint}"
|
||||||
"d ${httpRoot}/nixos-minimal 0755 root root -"
|
"d ${httpRoot}/auto-installer 0755 root root -"
|
||||||
"d ${httpRoot}/systemrescue 0755 root root -"
|
"d ${httpRoot}/nixos-minimal 0755 root root -"
|
||||||
"d ${httpRoot}/ubuntu 0755 root root -"
|
"d ${httpRoot}/systemrescue 0755 root root -"
|
||||||
"d ${httpRoot}/rescue 0755 root root -"
|
"d ${httpRoot}/debian 0755 root root -"
|
||||||
"d ${tftpRoot} 0755 root root -"
|
"d ${httpRoot}/ubuntu 0755 root root -"
|
||||||
"C+ ${httpRoot}/boot.ipxe 0644 root root - ${bootIpxe}"
|
"d ${httpRoot}/rescue 0755 root root -"
|
||||||
"C+ ${httpRoot}/menu.ipxe 0644 root root - ${menuIpxe}"
|
"d ${tftpRoot} 0755 root root -"
|
||||||
"C+ ${httpRoot}/systemrescue.ipxe 0644 root root - ${systemRescueIpxe}"
|
"C+ ${httpRoot}/boot.ipxe 0644 root root - ${bootIpxe}"
|
||||||
"C+ ${tftpRoot}/autoexec.ipxe 0644 root root - ${autoexecIpxe}"
|
"C+ ${httpRoot}/menu.ipxe 0644 root root - ${menuIpxe}"
|
||||||
"C+ ${tftpRoot}/ipxe.efi 0644 root root - ${pkgs.ipxe}/ipxe.efi"
|
"C+ ${httpRoot}/debian.ipxe 0644 root root - ${debianIpxe}"
|
||||||
"C+ ${tftpRoot}/undionly.kpxe 0644 root root - ${pkgs.ipxe}/undionly.kpxe"
|
"C+ ${httpRoot}/systemrescue.ipxe 0644 root root - ${systemRescueIpxe}"
|
||||||
];
|
"C+ ${tftpRoot}/autoexec.ipxe 0644 root root - ${autoexecIpxe}"
|
||||||
|
"C+ ${tftpRoot}/ipxe.efi 0644 root root - ${pkgs.ipxe}/ipxe.efi"
|
||||||
systemd.services.stage-systemrescue = {
|
"C+ ${tftpRoot}/undionly.kpxe 0644 root root - ${pkgs.ipxe}/undionly.kpxe"
|
||||||
description = "Stage SystemRescue ISO contents for HTTP PXE boot";
|
|
||||||
after = [
|
|
||||||
"local-fs.target"
|
|
||||||
"systemd-tmpfiles-setup.service"
|
|
||||||
];
|
];
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
serviceConfig = {
|
services = {
|
||||||
Type = "oneshot";
|
fetch-debian-netboot = {
|
||||||
ExecStart = stageSystemRescue;
|
description = "Download Debian ${debianRelease} netboot kernel and initrd for HTTP PXE boot";
|
||||||
|
after = [
|
||||||
|
"local-fs.target"
|
||||||
|
"systemd-tmpfiles-setup.service"
|
||||||
|
"network-online.target"
|
||||||
|
];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = fetchDebianNetboot;
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
stage-systemrescue = {
|
||||||
|
description = "Stage SystemRescue ISO contents for HTTP PXE boot";
|
||||||
|
after = [
|
||||||
|
"local-fs.target"
|
||||||
|
"systemd-tmpfiles-setup.service"
|
||||||
|
];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = stageSystemRescue;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{ config, vars, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# Use the same FQDN approach as docker/mount-data.nix — a bare hostname is
|
||||||
|
# unreliable: systemd-resolved only tries LLMNR for single-label names, and
|
||||||
|
# a global search domain causes it to skip the interface-scoped LAN DNS.
|
||||||
|
nfsServer = "${vars.nfsServerHost}.${vars.homeDomain}";
|
||||||
|
|
||||||
|
# x-systemd.automount is unsupported inside LXC containers (systemd logs
|
||||||
|
# "Starting of <unit>.automount unsupported" and never mounts). Use nofail
|
||||||
|
# there so a boot with the NFS server unreachable doesn't hang instead.
|
||||||
|
automountOpts = if config.boot.isContainer then [ "nofail" ] else [ "x-systemd.automount" ];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
fileSystems.${vars.nfsShares.proxmoxPxeImages.mountpoint} = {
|
||||||
|
device = "${nfsServer}:${vars.storageRoot}/${vars.nfsShares.proxmoxPxeImages.subpath}";
|
||||||
|
fsType = "nfs";
|
||||||
|
options = [
|
||||||
|
"nfsvers=4.2"
|
||||||
|
"_netdev"
|
||||||
|
"noatime"
|
||||||
|
] ++ automountOpts;
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user