netbootd

netbootd is a lightweight network boot server, designed for maximum flexibility and with “batteries included” approach in mind, serving as a DHCP, TFTP and HTTP server. It includes a basic templating functionality, designed to allow generating e.g. preseed files for unattended OS installation.

It can be compared to Foreman or Cobbler, as the goal is to PXE-boot a machine into an operating system or installation environment.

Unlike Foreman and Cobbler, netbootd is actually a DHCP, TFTP and HTTP server. It does not require any other software to be used.

netbootd aims to provide maximum flexibility and unlike Foreman or Cobbler makes no attempt to simplify the process of network booting. The results will be only as good as the configuration (the manifest in this case).

netbootd’s configuration consists of a set of manifest, each representing a machine. In order to support automation-based workflow, manifests can be managed via a simple HTTP API.

Note: This software is highly experimental, at proof-of-concept stage. It works but a lot of critical features are missing.

DHCP

netbootd includes a DHCP server that will respond ONLY to MAC addresses found in one of the manifests. It does not implement the concept of leases as IPs are implied to be statically allocated via manifest configuration.

Multiple options are supported, such as router, hostname, domain, DNS, NTP, and naturally NBP.

TFTP and HTTP

netbootd exposes all “mounts” via both TFTP and HTTP simultatenously. Naturally, it’s not a good idea to transfer really large files over TFTP but PXE generally requires use of TFTP in most cases.

TFTP and HTTP content can either be static text (embedded in the manifest), generated content (using Go’s text/template templating engine) or proxied to upstream HTTP(S). This last feature is mainly intended to proxy TFTP to HTTP(S) but very well may be used to reverse-proxy HTTP in otherwise isolated environments and can use a proxy itself (HTTP_PROXY and NO_PROXY is honored automatically by Go).

netbootd cannot serve local files. An exception is a bundled version of iPXE, which allows downloading (typically) kernel and initrd over HTTP instead of TFTP.

Manifests

A manifest represents a machine to be provisioned/served. The behavior of built-in DHCP, TFTP and HTTP server is specific to a manifest, meaning that it varies based on source MAC/IP. Each host may see different content at /something path.

Note that this is not a security feature, and you should not host any sensitive content. MAC and IPs can be easily spoofed. In fact, netbootd includes a convenience feature to spoof source IP for troubleshooting purposes. Append ?spoof=<ip-address> to HTTP request to see the response for a particular host. There is no TFTP counterpart of this feature.

Example manifests are included in the examples/ directory.

Anatomy of a manifest

---
# ID can be anything unique, URL-safe, used to identify it for HTTP API
id: ubuntu-1804

### DHCP options - used for DHCP responses from netbootd
# IP address with subnet (CIDR) to give out
ipv4: 192.168.17.101/24
# Hostname (without domain part) (Option 12)
hostname: ubuntu-machine-1804
# Domain part (used for hostname) (Option 15)
domain: test.local
# Lease duration is used as Option 51
# Note that netbootd is a static-assignment server, which does not prevent IP conflicts.
leaseDuration: 1h
# The MAC addresses which map to this manifest
# List multiple for machine with multiple NICs, if not sure which one boots first
mac:
  - 00:15:5d:bd:be:15
  - aa:bb:cc:dd:ee:fc
# Domain name servers (DNS) in the order of preference (Option 6)
dns:
  - 1.2.3.4
  - 3.4.5.6
# Routers in the order of preference (Option 3), more than one is rare
router:
  - 192.168.17.1
# NTP servers in the order of preference (Option 42), IP address required
ntp:
  - 192.168.17.1
# Whether a bundled iPXE bootloader should be served first (before bootFilename).
# When iPXE is loaded, it does DHCP again and netbootd detects its client string
# to break the boot loop and serve bootFilename instead.
ipxe: true
# The name of NBP file name, server over TFTP from "next server",
# which netbootd automatically points to be itself.
# This should map to a "mount" below.
bootFilename: install.ipxe

# Mounts define virtual per-host (per-manifest) paths that are acessible
# over both TFTP and HTTP but only from the IP address of in this manifest.
# Each mount can be either a proxy mount (HTTP/HTTPS proxy) or a content mount (static).
mounts:
  - path: /netboot
    # When true, all paths starting with this prefix use this mount.
    pathIsPrefix: true
    # When proxy is defined, these requests are proxied to a HTTP/HTTPS address.
    proxy: http://archive.ubuntu.com/ubuntu/dists/bionic-updates/main/installer-amd64/current/images/hwe-netboot/ubuntu-installer/amd64/
    # When true, the proxy path defined above gets a suffix to the Path prefix appended to it.
    proxyAppendSuffix: true

  - path: /install.ipxe
    # The templating context provides access to: .LocalIP, .RemoteIP, .HttpBaseUrl and .Manifest.
    # Sprig functions are available: masterminds.github.io/sprig
    content: |
      #!ipxe
      # See https://ipxe.org/scripting for iPXE commands/scripting documentation

      set base {{ .HttpBaseUrl }}/netboot

      {{ $hostnameParts := splitList "." .Manifest.Hostname }}
      kernel ${base}/linux gfxpayload=800x600x16,800x600 initrd=initrd.gz auto=true url={{ .HttpBaseUrl.String }}/preseed.txt netcfg/get_ipaddress={{ .Manifest.IPv4.IP }} netcfg/get_netmask={{ .Manifest.IPv4.Netmask }} netcfg/get_gateway={{ first .Manifest.Router }} netcfg/get_nameservers="{{ .Manifest.DNS | join " " }}" netcfg/disable_autoconfig=true hostname={{ first $hostnameParts }} domain={{ rest $hostnameParts | join "." }} DEBCONF_DEBUG=developer
      initrd ${base}/initrd.gz
      boot

HTTP API

In this preview/development version, this HTTP API does not support authentication.

GET /api/manifests

Returns a dictionary of all manifests keyed by their ID.

Supports Accept header (if provided) that allows selecting a json output (Accept: application/json).

GET /api/manifests/{id}

Returns a single manifest with ID provided in the URL path.

Supports Accept header (if provided) that allows selecting a json output (Accept: application/json).

Returns:

  • 200 for successful response
  • 404 if manifest with provided ID does not exist
PUT /api/manifests/{id}

Accepts a manifest in either JSON (`Content-type: application/json`) or YAML (default) format.

Returns:

  • 201 Created on success
  • 400 for malformed request (invalid manifest)
DELETE /api/manifests/{id}

Ensures that manifest with provided ID does not exist.

Always returns 204, even if manifest already did not exist.

GET|POST /api/self/suspend-boot

Allows a provisioned host to ask not to be booted again. This does not block DHCP, TFTP or HTTP requests, it only removes NBP information from DHCP responses.

This operation looks for a manifest matching the IP address of the requester. It is possible to spoof it with ?spoof=1.2.3.4 query parameter.

GET|POST /api/self/unsuspend-boot

Re-enables booting for a provisioned host.

This operation looks for a manifest matching the IP address of the requester. It is possible to spoof it with ?spoof=1.2.3.4 query parameter.

GET /api/self/manifest

Returns a manifest matching requester’s IP Address.

Supports Accept header (if provided) that allows selecting a json output (Accept: application/json).

This operation looks for a manifest matching the IP address of the requester. It is possible to spoof it with ?spoof=1.2.3.4 query parameter.

Usage

Usage:
  netbootd server [flags]

Flags:
  -a, --address string     IP address to listen on (DHCP, TFTP, HTTP)
  -r, --api-port int       HTTP API port to listen on (default 8081)
  -h, --help               help for server
  -p, --http-port int      HTTP port to listen on (default 8080)
  -i, --interface string   interface to listen on, e.g. eth0 (DHCP)
  -m, --manifests string   load manifests from directory

Run e.g. ./netbootd --trace server -m ./examples/

Roadmap / TODOs

  • API TLS & Authentication
  • Manifest persistence (currently API-configured manifests live in memory only)
  • Pluggable store backends (e.g. Redis, Etcd, files) for Manifests
  • Notifications (e.g. long-polling wait to return when a given host actually booted)
  • Per-manifest logs available over API

GitHub

https://github.com/DSpeichert/netbootd