Netboot: Difference between revisions

From NixOS Wiki
No edit summary
m Format the recently-added example with `nixfmt-rfc-style`.
 
Line 65: Line 65:


=== Another example ===
=== Another example ===
File <code>netboot.nix</code>:
{{file|netboot.nix|nix|
<syntaxHighlight lang=nix copy>
<nowiki>
{ name ? "netboot", arch ? "x86_64-linux"
{
, configuration ? _: {} # --arg configuration 'import ./netboot-config.nix'
  name ? "netboot",
, legacy ? false # variation with pxelinux and dnsmasq for older systems
  arch ? "x86_64-linux",
, cmdline ? []
  configuration ? _: { }, # --arg configuration 'import ./netboot-config.nix'
, loglevel ? 4
  legacy ? false, # variation with pxelinux and dnsmasq for older systems
, pixiecoreport ? 64172
  cmdline ? [ ],
, proxynets ? [ "192.168.0.0" ]
  loglevel ? 4,
, serialconsole ? false
  pixiecoreport ? 64172,
, serialport ? 0
  proxynets ? [ "192.168.0.0" ],
, serialspeed ? 9600
  serialconsole ? false,
, nixpkgs ? import <nixpkgs> {}, ... }:
  serialport ? 0,
with nixpkgs; with lib; let
  serialspeed ? 9600,
  nixpkgs ? import <nixpkgs> { },
  ...
}:
with nixpkgs;
with lib;
let


   example-configuration = {pkgs, config, ...}: with pkgs; {
   example-configuration =
    config = {
    { pkgs, config, ... }:
      environment.systemPackages = [
    with pkgs;
        mtr bridge-utils vlan ethtool jwhois sipcalc
    {
        netcat-openbsd tsocks psmisc pciutils usbutils
      config = {
        lm_sensors dmidecode microcom unar mkpasswd ripgrep
        environment.systemPackages = [
        wget rsync sshfs-fuse iperf3 mc mutt borgbackup
          mtr
        rxvt_unicode.terminfo
          bridge-utils
      ];
          vlan
      # users.users.nixos.openssh.authorizedKeys.keys = [ … ];
          ethtool
      # services.openssh = { ports = [2]; settings.PasswordAuthentication = false; };
          jwhois
      # virtualisation.lxc.enable = true;
          sipcalc
          netcat-openbsd
          tsocks
          psmisc
          pciutils
          usbutils
          lm_sensors
          dmidecode
          microcom
          unar
          mkpasswd
          ripgrep
          wget
          rsync
          sshfs-fuse
          iperf3
          mc
          mutt
          borgbackup
          rxvt_unicode.terminfo
        ];
        # users.users.nixos.openssh.authorizedKeys.keys = [ … ];
        # services.openssh = { ports = [2]; settings.PasswordAuthentication = false; };
        # virtualisation.lxc.enable = true;
      };
     };
     };
  };


   config = import <nixpkgs/nixos/lib/eval-config.nix> {
   config = import <nixpkgs/nixos/lib/eval-config.nix> {
Line 106: Line 135:
   };
   };


   version-module = { config, ... }: {
   version-module =
    system.stateVersion = config.system.nixos.version; # be quiet
    { config, ... }:
    system.nixos.tags = [ name ];
    {
  };
      system.stateVersion = config.system.nixos.version; # be quiet
      system.nixos.tags = [ name ];
    };


   run-pixiecore = writeShellScript "${name}-run-pixiecore" ''
   run-pixiecore = writeShellScript "${name}-run-pixiecore" ''
Line 125: Line 156:
   '';
   '';


   tftp-root = linkFarm "${name}-tftp-root"  
   tftp-root = linkFarm "${name}-tftp-root" (
     ( mapAttrsToList (name: path: { inherit name path; }) {
     mapAttrsToList (name: path: { inherit name path; }) {
       "pxelinux.cfg/default" = pxelinux-cfg;
       "pxelinux.cfg/default" = pxelinux-cfg;
       "pxelinux.0"           = "syslinux/pxelinux.0";
       "pxelinux.0" = "syslinux/pxelinux.0";
       "syslinux"             = "${syslinux}/share/syslinux";
       "syslinux" = "${syslinux}/share/syslinux";
       "bzImage"             = kernel;
       "bzImage" = kernel;
       "initrd"               = initrd;
       "initrd" = initrd;
     } );
     }
  );


   dnsmasq-conf = writeText "${name}-dnsmasq-conf" ''
   dnsmasq-conf = writeText "${name}-dnsmasq-conf" ''
Line 138: Line 170:
     local-service=net
     local-service=net
     dhcp-boot=pxelinux.0
     dhcp-boot=pxelinux.0
     ${ flip concatMapStrings proxynets (net: ''
     ${flip concatMapStrings proxynets (net: ''
       dhcp-range=${net},proxy
       dhcp-range=${net},proxy
     '')}
     '')}
Line 149: Line 181:
   '';
   '';


   cmd-line = concatStringsSep " "
   cmd-line = concatStringsSep " " (
     ([ "init=${build.toplevel}/init" "loglevel=${toString loglevel}" ]
     [
     ++ optional serialconsole
      "init=${build.toplevel}/init"
      "console=ttyS${toString serialport},${toString serialspeed}"
      "loglevel=${toString loglevel}"
     ++ cmdline );
    ]
     ++ optional serialconsole "console=ttyS${toString serialport},${toString serialspeed}"
     ++ cmdline
  );


   pxelinux-cfg = writeText "${name}-pxelinux.cfg" ''
   pxelinux-cfg = writeText "${name}-pxelinux.cfg" ''
     ${ optionalString serialconsole
     ${optionalString serialconsole "serial ${toString serialport} ${toString serialspeed}"}
      "serial ${toString serialport} ${toString serialspeed}" }
     console 1
     console 1
     prompt 1
     prompt 1
Line 172: Line 206:
   initrd = "${build.netbootRamdisk}/initrd";
   initrd = "${build.netbootRamdisk}/initrd";


in if legacy then run-dnsmasq else run-pixiecore
in
</syntaxHighlight>
if legacy then run-dnsmasq else run-pixiecore
 
</nowiki>
}}


Usage example:
Usage example:

Latest revision as of 14:48, 30 October 2024

Building and serving a netboot image

Example

This example uses Pixiecore for hosting, which works in an ordinary network environment with an existing DHCP server.

Create file system.nix:

let
  # NixOS 22.11 as of 2023-01-12
  nixpkgs = builtins.getFlake "github:nixos/nixpkgs/54644f409ab471e87014bb305eac8c50190bcf48";

  sys = nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    modules = [
      ({ config, pkgs, lib, modulesPath, ... }: {
        imports = [
          (modulesPath + "/installer/netboot/netboot-minimal.nix")
        ];
        config = {
          ## Some useful options for setting up a new system
          # services.getty.autologinUser = lib.mkForce "root";
          # users.users.root.openssh.authorizedKeys.keys = [ ... ];
          # console.keyMap = "de";
          # hardware.video.hidpi.enable = true;

          system.stateVersion = config.system.nixos.release;
        };
      })
    ];
  };

  run-pixiecore = let
    hostPkgs = if sys.pkgs.system == builtins.currentSystem
               then sys.pkgs
               else nixpkgs.legacyPackages.${builtins.currentSystem};
    build = sys.config.system.build;
  in hostPkgs.writers.writeBash "run-pixiecore" ''
    exec ${hostPkgs.pixiecore}/bin/pixiecore \
      boot ${build.kernel}/bzImage ${build.netbootRamdisk}/initrd \
      --cmdline "init=${build.toplevel}/init loglevel=4" \
      --debug --dhcp-no-bind \
      --port 64172 --status-port 64172 "$@"
  '';
in
  run-pixiecore

Run pixiecore:

# Build pixiecore runner
nix build -f system.nix -o /tmp/run-pixiecore

# Open required firewall ports
sudo iptables -w -I nixos-fw -p udp -m multiport --dports 67,69,4011 -j ACCEPT
sudo iptables -w -I nixos-fw -p tcp -m tcp --dport 64172 -j ACCEPT

# Run pixiecore
sudo $(realpath /tmp/run-pixiecore)

# Close ports
sudo iptables -w -D nixos-fw -p udp -m multiport --dports 67,69,4011 -j ACCEPT
sudo iptables -w -D nixos-fw -p tcp -m tcp --dport 64172 -j ACCEPT

Another example

netboot.nix
{
  name ? "netboot",
  arch ? "x86_64-linux",
  configuration ? _: { }, # --arg configuration 'import ./netboot-config.nix'
  legacy ? false, # variation with pxelinux and dnsmasq for older systems
  cmdline ? [ ],
  loglevel ? 4,
  pixiecoreport ? 64172,
  proxynets ? [ "192.168.0.0" ],
  serialconsole ? false,
  serialport ? 0,
  serialspeed ? 9600,
  nixpkgs ? import &lt;nixpkgs&gt; { },
  ...
}:
with nixpkgs;
with lib;
let

  example-configuration =
    { pkgs, config, ... }:
    with pkgs;
    {
      config = {
        environment.systemPackages = [
          mtr
          bridge-utils
          vlan
          ethtool
          jwhois
          sipcalc
          netcat-openbsd
          tsocks
          psmisc
          pciutils
          usbutils
          lm_sensors
          dmidecode
          microcom
          unar
          mkpasswd
          ripgrep
          wget
          rsync
          sshfs-fuse
          iperf3
          mc
          mutt
          borgbackup
          rxvt_unicode.terminfo
        ];
        # users.users.nixos.openssh.authorizedKeys.keys = [ … ];
        # services.openssh = { ports = [2]; settings.PasswordAuthentication = false; };
        # virtualisation.lxc.enable = true;
      };
    };

  config = import &lt;nixpkgs/nixos/lib/eval-config.nix&gt; {
    # see &lt;nixpkgs/nixos/release.nix&gt;
    system = arch;
    modules = [
      &lt;nixpkgs/nixos/modules/installer/netboot/netboot-minimal.nix&gt;
      version-module
      example-configuration
      configuration
    ];
  };

  version-module =
    { config, ... }:
    {
      system.stateVersion = config.system.nixos.version; # be quiet
      system.nixos.tags = [ name ];
    };

  run-pixiecore = writeShellScript "${name&#125;-run-pixiecore" ''
    exec ${pixiecore}/bin/pixiecore \
      boot ${kernel} ${initrd} \
      --cmdline "${cmd-line}" \
      --debug --dhcp-no-bind --log-timestamps \
      --port ${toString pixiecoreport} \
      --status-port ${toString pixiecoreport} "$@"
  '';

  run-dnsmasq = writeShellScript "${name&#125;-run-dnsmasq" ''
    exec ${dnsmasq}/bin/dnsmasq \
      -d -k --no-daemon -C "${dnsmasq-conf}" "$@"
  '';

  tftp-root = linkFarm "${name&#125;-tftp-root" (
    mapAttrsToList (name: path: { inherit name path; }) {
      "pxelinux.cfg/default" = pxelinux-cfg;
      "pxelinux.0" = "syslinux/pxelinux.0";
      "syslinux" = "${syslinux}/share/syslinux";
      "bzImage" = kernel;
      "initrd" = initrd;
    }
  );

  dnsmasq-conf = writeText "${name&#125;-dnsmasq-conf" ''
    pxe-prompt="Booting NixOS..",1
    local-service=net
    dhcp-boot=pxelinux.0
    ${flip concatMapStrings proxynets (net: ''
      dhcp-range=${net},proxy
    '')}
    dhcp-no-override
    dhcp-leasefile=/dev/null
    log-dhcp
    enable-tftp
    tftp-port-range=6900,6999
    tftp-root=${tftp-root}
  '';

  cmd-line = concatStringsSep " " (
    [
      "init=${build.toplevel}/init"
      "loglevel=${toString loglevel}"
    ]
    ++ optional serialconsole "console=ttyS${toString serialport},${toString serialspeed}"
    ++ cmdline
  );

  pxelinux-cfg = writeText "${name&#125;-pxelinux.cfg" ''
    ${optionalString serialconsole "serial ${toString serialport} ${toString serialspeed}"}
    console 1
    prompt 1
    timeout 37
    default NixOS
    label NixOS
      kernel bzImage
      append initrd=initrd ${cmd-line}
  '';

  build = config.config.system.build;
  kernel = "${build.kernel}/${kernel-target}";
  kernel-target = config.pkgs.stdenv.hostPlatform.linux-kernel.target;
  initrd = "${build.netbootRamdisk}/initrd";

in
if legacy then run-dnsmasq else run-pixiecore

Usage example:

# Build pixiecore runner
nix build -f netboot.nix -o /tmp/run-pixiecore

# Build dnsmasq + pxelinux runner
nix build -f netboot.nix --arg legacy true -o /tmp/run-dnsmasq

# Build for some ancient system with a serial console
nix build -f netboot.nix --arg name '"ancient-netboot"' -o /tmp/run-netboot \
  --arg configuration 'import ./ancient-config.nix' \
  --arg legacy true --arg proxynets '["10.2.1.0"]' \
  --arg serialconsole true --arg serialport 3 --arg serialspeed 115200

See also

NixOS: Pixiecore module.

NixOS manual: PXE booting.

netboot.xyz

There is now official netboot.xyz support. Just select NixOS from Linux installs and you should be ready to go.

Note: Your iPXE must be recent enough to support https:// links