162 lines
5.2 KiB
Markdown
162 lines
5.2 KiB
Markdown
# NUC Fleet Netboot (Unifi)
|
|
|
|
This runbook provisions x86_64 NUCs from runner netboot artifacts without USB image flashing.
|
|
|
|
Supported modes:
|
|
|
|
- ProxyDHCP mode: recommended when you want automatic stage-1/2 iPXE handling.
|
|
- UniFi-only mode: DHCP options 66/67 in UniFi, no ProxyDHCP.
|
|
|
|
## Prerequisites
|
|
|
|
- Linux boot server on the same VLAN/L2 domain as the NUCs.
|
|
- Unifi network with DHCP enabled.
|
|
- Local DNS record on that VLAN: `boot.every.channel -> <boot-server-ip>`.
|
|
- `curl`, `tar`, `python3`, `dnsmasq` installed on the boot server.
|
|
- For UniFi-only mode with reliable chainloading: `git` and `make` to build embedded iPXE.
|
|
- `openssl` (or equivalent) if you want generated chain tokens.
|
|
- Runner netboot artifact published to Forgejo Releases (or available as local tarball).
|
|
|
|
## Persistent NixOS service (recommended)
|
|
|
|
Instead of running scripts manually, use the exported NixOS module and keep netboot
|
|
staging/serving declarative:
|
|
|
|
```nix
|
|
{
|
|
imports = [ every-channel.nixosModules.ec-netboot ];
|
|
|
|
services.every-channel.netboot = {
|
|
enable = true;
|
|
listenIP = "10.20.30.2";
|
|
interface = "enp195s0";
|
|
hostname = "boot.every.channel";
|
|
tftpBootFilename = "ec-ipxe.efi";
|
|
httpAllowedCIDRs = [ "10.20.30.0/24" ];
|
|
chainTokenFile = "/run/agenix/every-channel-netboot-chain-token";
|
|
|
|
# UniFi-only mode by default (no ProxyDHCP):
|
|
proxyDhcp.enable = false;
|
|
|
|
release.host = "https://git.every.channel";
|
|
release.repo = "every-channel/every.channel";
|
|
# release.tag = "boot-v2026.03.02"; # optional pin
|
|
# release.tokenFile = "/run/agenix/forgejo-api-token"; # optional private repo token
|
|
};
|
|
}
|
|
```
|
|
|
|
Operational commands:
|
|
|
|
```sh
|
|
sudo systemctl start every-channel-netboot-stage.service
|
|
sudo systemctl restart every-channel-netboot.service
|
|
sudo systemctl status every-channel-netboot.service
|
|
```
|
|
|
|
If you prefer ProxyDHCP mode:
|
|
|
|
```nix
|
|
services.every-channel.netboot.proxyDhcp.enable = true;
|
|
services.every-channel.netboot.proxyDhcp.subnet = "10.20.30.0/24";
|
|
```
|
|
|
|
## 1) Build embedded iPXE (UniFi-only mode)
|
|
|
|
This removes iPXE boot loops without requiring ProxyDHCP.
|
|
|
|
```sh
|
|
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
|
EVERY_CHANNEL_NETBOOT_HTTP_PORT=8080 \
|
|
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="$(openssl rand -hex 16)" \
|
|
./scripts/netboot-build-ipxe.sh
|
|
```
|
|
|
|
Output:
|
|
|
|
- `tmp/netboot/tftp/ec-ipxe.efi` (use this as DHCP option 67 filename)
|
|
|
|
## 2) Stage runner netboot artifacts
|
|
|
|
```sh
|
|
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
|
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="<same-token-as-step-1>" \
|
|
EVERY_CHANNEL_NETBOOT_IPXE_EFI_PATH=tmp/netboot/tftp/ec-ipxe.efi \
|
|
EVERY_CHANNEL_NETBOOT_IPXE_EFI_FILENAME=ec-ipxe.efi \
|
|
./scripts/netboot-stage.sh
|
|
```
|
|
|
|
Optional inputs:
|
|
|
|
- `EVERY_CHANNEL_NETBOOT_RELEASE_TAG=boot-v2026.02.28`
|
|
- `EVERY_CHANNEL_NETBOOT_TARBALL=/path/to/ec-runner-x86_64-netboot-....tar.gz`
|
|
- `EVERY_CHANNEL_FORGE_TOKEN=<token>` for private releases
|
|
- `EVERY_CHANNEL_NETBOOT_ALLOW_REMOTE_IPXE=true` only if you intentionally want to download iPXE from URL
|
|
- `EVERY_CHANNEL_IPXE_EFI_SHA256=<sha256>` to pin iPXE binary integrity
|
|
|
|
This stages:
|
|
|
|
- `tmp/netboot/http/{kernel,initrd,netboot.ipxe}`
|
|
- `tmp/netboot/tftp/ec-ipxe.efi`
|
|
|
|
## 3) Serve HTTP + TFTP
|
|
|
|
UniFi-only mode (no ProxyDHCP):
|
|
|
|
```sh
|
|
sudo \
|
|
EVERY_CHANNEL_NETBOOT_LISTEN_IP=10.20.30.2 \
|
|
EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 \
|
|
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
|
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="<same-token-as-step-1>" \
|
|
EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS=10.20.30.0/24 \
|
|
EVERY_CHANNEL_NETBOOT_PROXY_DHCP=false \
|
|
EVERY_CHANNEL_NETBOOT_TFTP_BOOT_FILENAME=ec-ipxe.efi \
|
|
./scripts/netboot-serve.sh
|
|
```
|
|
|
|
ProxyDHCP mode:
|
|
|
|
```sh
|
|
sudo \
|
|
EVERY_CHANNEL_NETBOOT_LISTEN_IP=10.20.30.2 \
|
|
EVERY_CHANNEL_NETBOOT_INTERFACE=eth0 \
|
|
EVERY_CHANNEL_NETBOOT_PROXY_SUBNET=10.20.30.0/24 \
|
|
EVERY_CHANNEL_NETBOOT_HOSTNAME=boot.every.channel \
|
|
EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN="<same-token-as-step-1>" \
|
|
EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS=10.20.30.0/24 \
|
|
EVERY_CHANNEL_NETBOOT_PROXY_DHCP=true \
|
|
EVERY_CHANNEL_NETBOOT_TFTP_BOOT_FILENAME=ec-ipxe.efi \
|
|
./scripts/netboot-serve.sh
|
|
```
|
|
|
|
## 4) UniFi settings (you do this)
|
|
|
|
UniFi-only mode:
|
|
|
|
- `Network Boot`: enabled
|
|
- `Server`: `boot.every.channel` (or boot server IP)
|
|
- `Filename`: `ec-ipxe.efi`
|
|
- `TFTP Server`: `boot.every.channel`
|
|
|
|
ProxyDHCP mode:
|
|
|
|
- leave UniFi boot/TFTP options unset.
|
|
|
|
NUC BIOS:
|
|
|
|
- Enable UEFI PXE boot.
|
|
- Disable Legacy/CSM where possible.
|
|
- Put network boot first for initial install.
|
|
|
|
## Security hardening
|
|
|
|
- Keep provisioning on an isolated VLAN.
|
|
- Allow only required ports from NUC VLAN to boot server: UDP 69, TCP 8080 (and DHCP if ProxyDHCP mode).
|
|
- Keep provisioning services up only during rollout, then stop them.
|
|
- Use `EVERY_CHANNEL_NETBOOT_HTTP_ALLOWED_CIDRS` to limit HTTP artifact access to NUC subnet(s).
|
|
- Use `EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN` so only tokened iPXE chain requests receive `netboot.ipxe`.
|
|
- Use checksum verification in `netboot-stage.sh` (enabled by default when release has `SHA256SUMS.txt`).
|
|
- `netboot-stage.sh` now defaults to local iPXE binaries; remote URL download requires explicit opt-in.
|
|
- Prefer embedded `ec-ipxe.efi` with fixed chain target over generic unsigned internet binaries.
|
|
- If Secure Boot is required, use signed boot chain and keys for your environment (outside this basic runbook).
|