Network booting starts a computer from files on a server instead of a local disk. Legacy PXE learns a boot file from DHCP and fetches it over unauthenticated TFTP; UEFI HTTP Boot fetches a URI over HTTP or HTTPS. Neither is safe by default: add HTTPS, Secure Boot, signed scripts and an isolated provisioning network.
This guide goes one level deeper than our posts on how DHCP works and its risks and on MITRE ATT&CK T1542.005, TFTP Boot. This one follows a single boot from power on to a running kernel: which DHCP options carry what, what the firmware checks, how iPXE changes the picture and where Secure Boot stops. Option numbers, architecture codes and directives come from the RFCs, the IANA registry, the iPXE and dnsmasq documentation and the EDK2 source listed under Sources.
Key takeaways
- Network boot has two steps: get boot information from DHCP or DHCPv6, then download a network bootstrap program and run it.
- Legacy PXE sends
PXEClientin option 60 and its firmware type in option 93 (RFC 4578), and gets a TFTP server (option 66 orsiaddr) and file name (option 67 orfile) back (RFC 2132). - TFTP (RFC 1350) has no login, access control or integrity protection. Whoever answers first can hand the client a different program.
- UEFI HTTP Boot sends
HTTPClientand an HTTP architecture code (0x10 for x64) and expects a full URI. EDK2 refuseshttp://unless built to allow it and ships with no TLS trust anchor. - iPXE supports HTTPS and detached CMS signatures, but its default "is always to allow untrusted images". Enforcement needs an embedded
imgtrustscript and a signature check on every file, including the initrd. - Secure Boot checks executables, not DHCP answers, scripts, menus, command lines or a separate initrd. Unified Kernel Images and signed iPXE scripts close most of that gap.
- The largest exposure is often what the boot server hands out: answer files and enrollment tokens with secrets, served to anyone who asks.
What is network booting, and when is it used?
A machine network boots when its firmware fetches the first program it runs from a server. RFC 5970 gives the motivation: where administrators maintain many nodes, "serving all boot and configuration files from a central server" cuts the work.
Bare metal provisioning
The most common use is installing an operating system on servers without touching them. Each machine network boots an installer, which pulls an answer file (kickstart, preseed, autoinstall or cloud-init), partitions the disks and installs the OS. Afterwards the boot order falls through to local disk. Most server lifecycle tooling sits on this pattern.
Labs, classrooms and test benches
Labs reset machines to a known image on every start. A CI hardware lab can boot each test device into a freshly built image without reflashing storage.
Diskless and stateless clients
Thin clients, cluster nodes and kiosks may have no local OS at all, booting a kernel each time and mounting a root file system over NFS or iSCSI. Patching becomes: update the server image, reboot the fleet.
Recovery and diagnostics
Technicians boot memory testers, firmware updaters or rescue systems from a central server when a disk is broken or encrypted.
Network devices
Routers and switches can load their system image from a TFTP server. MITRE ATT&CK technique T1542.005 (created 20 October 2020, last modified 12 May 2026) covers adversaries pointing a device at a malicious TFTP server. Our T1542.005 post covers that angle; this post focuses on servers and PCs.
How does legacy PXE work, step by step?
The Preboot Execution Environment (PXE) was defined by Intel in a specification usually cited as PXE 2.1. We could not retrieve a primary copy, so the steps below come from RFC 4578 (PXE wire format), RFC 2132 (DHCP options), RFC 1350 (TFTP) and the dnsmasq and iPXE documentation.
Step 1: the client broadcasts a DHCPDISCOVER that says "I am PXE"
The network card's option ROM (BIOS) or the UEFI network stack sends a DHCPDISCOVER. Three things make it a PXE request:
- Option 60, vendor class identifier. RFC 2132 defines option 60 as a string clients use "to optionally identify the vendor type and configuration of a DHCP client". PXE clients send a string beginning
PXEClient, which dnsmasq matches when it encapsulates PXE options. - Option 93, client system architecture. RFC 4578 defines option 93 as a list of 16 bit architecture type identifiers that "describes the pre-boot runtime environment(s) of the client machine". A BIOS client sends 0; a 64 bit UEFI client sends 7 in practice (see the warning below).
- Option 94, client network interface identifier, and option 97, client machine identifier. RFC 4578 says option 94 encodes the UNDI interface type and revision (type 1, revision 2.1 for example) and option 97 carries a 16 octet GUID. Both "MUST be present in all DHCP and PXE packets sent by PXE-compliant clients and servers."
Clients must also request options 128 to 135, meant for the downloaded boot program. RFC 4578 warns these were site specific before November 2004 and "may conflict with other uses" on the same network.
Step 2: the server offers an address and a boot file
A DHCP server that understands PXE answers with a DHCPOFFER containing an address and two boot parameters:
- Where to download from. Either the
siaddr(next server) field in the fixed DHCP header, or option 66, "TFTP server name", which RFC 2132 defines for use "when the 'sname' field in the DHCP header has been used for DHCP options." - What to download. Either the 128 byte
filefield in the DHCP header, or option 67, "Bootfile name", which RFC 2132 defines for use "when the 'file' field in the DHCP header has been used for DHCP options."
So options 66 and 67 are strictly overflow fields, but many servers send both forms because clients differ. EDK2's source even notes that RFC 2132 "does not strictly state" option 67 is null terminated, and handles both cases.
Step 3 (optional): proxy DHCP and boot servers
A "proxy DHCP" server can hand out only boot parameters while another server assigns addresses; dnsmasq documents this mode and PXE boot menus through its pxe-service setting. This matters for security: a second responder on the segment is normal PXE behavior, so a rogue one only has to answer the boot question.
Step 4: the client downloads the network bootstrap program with TFTP
The client sends a TFTP read request to UDP port 69. RFC 1350 says TFTP "currently has no provisions for user authentication"; data moves in 512 byte blocks, each acknowledged before the next. RFC 2348 (May 1998) lets the two sides negotiate blocks of 8 to 65464 octets, RFC 2349 adds transfer size and timeout options, and RFC 7440 (January 2015) adds a window of several blocks in flight. None adds authentication, encryption or integrity checks, and RFC 1350's security section tells administrators to limit what the server can read and disallow writes.
Step 5: the network bootstrap program runs
The downloaded network bootstrap program (NBP) is a real mode program such as pxelinux.0 or iPXE's undionly.kpxe on BIOS, or an EFI application such as shimx64.efi, grubx64.efi or ipxe.efi on UEFI. It fetches its own configuration, a kernel and an initrd, and boots the OS.
The RFC and the registry disagree. RFC 4578's table lists type 7 as "EFI BC" and 9 as "EFI x86-64". The IANA Processor Architecture Types registry lists 0x0007 as "x64 UEFI" and 0x0009 as "EBC", and x64 UEFI firmware sends 7. Copy match rules from the registry, not the RFC table.
The DHCP options at a glance
| Option | Name | Defined in | Direction | What it does in network boot |
|---|---|---|---|---|
| 60 | Vendor class identifier | RFC 2132 | Client and server | Client says PXEClient or HTTPClient; server echoes the class it is answering |
| 66 | TFTP server name | RFC 2132 | Server | Boot server when sname is used for options |
| 67 | Bootfile name | RFC 2132 | Server | File name, or a full URI for HTTP Boot, when file is used for options |
| 93 | Client system architecture | RFC 4578 | Client | Firmware type, such as 0 (BIOS), 7 (x64 UEFI), 0x10 (x64 UEFI HTTP) |
| 94 | Client network interface identifier | RFC 4578 | Client | UNDI interface type and revision |
| 97 | Client machine identifier | RFC 4578 | Client | 16 octet GUID of the machine |
| 128 to 135 | PXE requested options | RFC 4578 | Server | Free for the boot program; not used by the boot ROM |
| DHCPv6 59 | OPT_BOOTFILE_URL | RFC 5970 | Server | Boot file URL, for example a tftp:// or http:// URL |
| DHCPv6 60 | OPT_BOOTFILE_PARAM | RFC 5970 | Server | Ordered parameters passed to the boot file |
| DHCPv6 61 | OPTION_CLIENT_ARCH_TYPE | RFC 5970 | Client | Same architecture codes as option 93 |
| DHCPv6 62 | OPTION_NII | RFC 5970 | Client | Network interface identifier, like option 94 |
How does network boot work over IPv6?
RFC 5970 (September 2010) carries PXE into DHCPv6 with one change: the server sends a single URL. Option 59 carries "the URL for the boot file" (literal IPv6 hosts go in square brackets), option 60 carries ordered parameters for the boot file, and options 61 and 62 mirror the IPv4 architecture and interface identifiers. DHCPv6 network boot was never tied to TFTP, and the IANA registry, which now also cites RFC 9915, holds the architecture codes for both IP versions.
RFC 5970's security section still reads as a list of what goes wrong today:
- A rogue server can make the client boot "the wrong operating system that has been provided by a malicious file server."
- Messages are unencrypted, so credentials in a boot URL are exposed.
- Authenticated DHCPv6 "does not protect against attacks on the boot file download channel"; use HTTPS or signed images with a configurable key.
All three apply equally to IPv4 PXE.
What do UEFI PXE and UEFI HTTP Boot change?
UEFI PXE: same protocol, different payload
UEFI firmware has its own network stack and PXE Base Code protocol. On the wire UEFI PXE looks like BIOS PXE, with a UEFI architecture code in option 93 (7 for x64, 0x0B for 64 bit Arm per IANA). The differences are in what is downloaded:
- The NBP is a PE/COFF EFI application, so servers hand out a file per architecture; iPXE's chainloading guide sends
undionly.kpxeto architecture00:00andipxe.efito the rest. - Secure Boot applies: the firmware only starts an NBP signed by a key in
dband not revoked indbx. That is the biggest security difference from BIOS PXE. - iPXE notes UEFI network booting "tends to be substantially slower", one reason sites chainload early into a faster program.
UEFI HTTP Boot: drop TFTP, fetch a URI
The UEFI specification added HTTP Boot in version 2.5, according to iPXE. uefi.org blocked our fetches, so the details below come from EDK2 (the open source UEFI reference code), the IANA registry and the iPXE note.
- The client announces itself as an HTTP client. EDK2 sends an HTTP Boot architecture code and treats an offer carrying "HTTPClient" as an HTTP offer. IANA lists "boot from http" codes per firmware: 0x0F x86 UEFI, 0x10 x64 UEFI, 0x12 and 0x13 Arm UEFI, 0x1A and 0x1C RISC-V UEFI, and others.
- The server answers with
HTTPClientand a URI. Per iPXE, the server must "provide the string HTTPClient as the vendor class identifier, and an HTTP URI as the boot filename"; on DHCPv6 the URI goes in option 59. - The firmware can boot more than one kind of file. EDK2 maps
application/efito an EFI image,application/vnd.efi-isoto a CD image andapplication/vnd.efi-imgto a disk image; images are booted from a RAM disk, so a whole installer ISO can boot over HTTP. - HTTPS is the default posture in EDK2. NetworkPkg sets
PcdAllowHttpConnectionsto FALSE by default ("Only the "https://" URI scheme is permitted"). Vendors can change this at build time, so check what your platform accepts. - There is no trust anchor until you add one. OVMF's README says HTTPS Boot needs HTTP and TLS compiled in and "by default, there is no trusted certificate." You import CAs through a "TLS Auth Configuration" screen or, for VMs, a firmware configuration blob.
Why HTTP Boot matters for security
HTTP Boot runs over TCP, routes across subnets, uses DNS names and, with HTTPS and a configured CA, authenticates the server, which is what RFC 5970 asked for in 2010. It does not fix DHCP. A rogue DHCP server can still point the client elsewhere, but its server then needs a certificate chaining to a CA the firmware trusts. With a private boot CA that is a high bar; with a public CA bundle it is low, since anyone can get a public certificate for a domain they control.
What is iPXE, and how does chainloading work?
iPXE is open source boot firmware that can be burned into a NIC ROM, built into UEFI firmware, or downloaded at boot by an existing PXE or HTTP Boot client. Its documentation covers HTTP, HTTPS, TFTP, iSCSI and AoE booting, scripting and menus, and many provisioning systems use it as their second stage.
Chainloading from PXE
iPXE's chainloading guide: put undionly.kpxe (BIOS) or ipxe.efi (UEFI) on the TFTP server as the boot file; the vendor ROM fetches iPXE, which does the rest over HTTP or HTTPS. Because iPXE sends its own DHCP request, a server that hands out iPXE again creates "the infinite loop". The guide's three fixes:
- An
autoexec.ipxescript beside the binary, run automatically where supported. - A DHCP rule that tells the two requests apart, returning the real boot script to iPXE. iPXE uses option 175 as its own option space (the
crosscertsetting is 175.93). - An embedded script built in with
EMBED=script.ipxe, which ignores the DHCP boot file name.
Only the third puts policy where a network attacker cannot change it.
Chainloading from UEFI HTTP Boot
iPXE's UEFI HTTP note drops TFTP entirely: the firmware's HTTP Boot client downloads ipxe.efi, then iPXE adds HTTPS, scripts, menus and "customisable code signing". Its DHCP rule returns HTTPClient and the ipxe.efi URI to architecture 16 (0x10).
What iPXE's cryptography covers
iPXE's cryptography page documents three features, each behind a build option:
| Feature | Build option | What it protects | What it does not protect |
|---|---|---|---|
| HTTPS | DOWNLOAD_PROTO_HTTPS | Confidentiality of the download and identity of the web server | Anything that came from DHCP, and files fetched over plain HTTP or TFTP |
| Code signing | IMAGE_TRUST_CMD | Integrity and origin of each file you explicitly verify with imgverify | Files you forget to verify, such as an initrd, unless you verify them too |
| File decryption | IMAGE_CRYPT_CMD | Confidentiality of encrypted images at rest on the server | Integrity, unless combined with code signing |
Specifics from the iPXE pages:
- TLS. TLS 1.0 to 1.2 with RSA or ECDSA keys, ECDHE or DHE, AES-GCM or AES-CBC. TLS 1.3 is not listed, so a TLS 1.3 only server will not work.
- Trust. By default iPXE trusts one "iPXE root CA" that cross-signs the Mozilla CA list, so it trusts "the same set of certificates as the Firefox web browser".
TRUST=ca.crtat build time replaces that with your own root; on an isolated network you must also make the full root available, or mirror the cross-signing service plus an OCSP proxy. - Signing. Detached CMS signatures from a certificate with
digitalSignatureandcodeSigningusage.imgverifychecks a file and can pin a signer name with--signer. - Enforcement.
imgtrustrefuses unverified images;imgtrust --permanentcannot be undone in that session. The docs state "there is no way to use trusted images without using an embedded script". - The initrd gap.
imgtrustblocks an untrusted kernel "but will not prevent the use of an untrusted initrd". Verify the initrd explicitly.
The safe pattern: build iPXE yourself with HTTPS, signing and your own root, embed a script that runs imgtrust --permanent first, and verify every script, kernel and initrd against a pinned signer.
How does Secure Boot apply to network boot?
Secure Boot asks one question per step: is this executable signed by a key in db and not revoked in dbx? Our Secure Boot, Trusted Boot and Measured Boot explainer covers the model; here is how it maps onto network boot.
The usual Linux chain: firmware, shim, GRUB, kernel
- Firmware loads the NBP only if its signature checks out. shim's SBAT document notes PC Secure Boot typically trusts two authorities, the Microsoft UEFI CA and the Windows CA; distribution shims are signed through the former.
- shim loads the next stage. Its README calls it "a trivial EFI application" that falls back to a certificate built into shim when firmware keys do not cover the binary. Since 16.1 it overrides the firmware load functions so later stages are checked the same way, and its
netboot.cfetches the second stage from the same server. - GRUB loads the kernel after shim verifies it through the shim lock protocol.
- The kernel runs with its own module signing and lockdown policies.
Where the chain is weakest
Signature checks cover executables. Network boot is full of things that are not:
- DHCP replies. A rogue DHCP server cannot make an unsigned file run, but it can steer the client to any signed file, including an old, vulnerable one still trusted.
- Configuration and menus. A network-fetched
grub.cfg, PXELINUX menu or unsigned iPXE script decides which kernel boots and with what command line, which can disable security features or point an installer at another answer file. - The initrd. In shim plus GRUB the initrd is unsigned. A Unified Kernel Image puts kernel, initrd and command line in one signed file, and shim's README says sections inside a signed UKI are covered by its outer signature.
- Revocation lag. A flawed signed component stays bootable until
dbxor SBAT revokes it. shim's SBAT document records that after BootHole (CVE-2020-10713) in 2020, 3 certificates and 150 hashes went into the x64dbx, about 10 kB of the roughly 32 kB typically available, which is why SBAT revokes by component generation instead. - Early boot parsers. NVD's entry for CVE-2023-40547, published 25 January 2024, describes shim's HTTP boot support trusting attacker controlled values in an HTTP response, exploitable from a man in the middle position or a compromised boot server. Anything parsing network input before the OS exists is attack surface.
Can iPXE run with Secure Boot on?
Only if the firmware trusts its signature. iPXE's documentation says Secure Boot requires binaries signed by Microsoft and calls its signing effort a "work in progress". The options are a distribution's signed shim and boot loader instead, a shim that trusts your own certificate, or your own key in db with ipxe.efi signed yourself (see Secure Boot with your own keys). Do not disable Secure Boot to make iPXE work.
What are the risks of network boot?
Rogue DHCP servers
RFC 2131 says "DHCP in its current form is quite insecure" and "unauthorized DHCP servers may be easily set up". Whoever answers first chooses the client's boot server and file. On a flat network any compromised or misconfigured host can do it.
Unauthenticated TFTP
TFTP has no authentication or encryption. An on-path attacker can substitute blocks, anyone who can reach the server can read every file, and a writable TFTP root lets one write replace the NBP for every machine.
Image tampering on the server
The boot server is a single point of trust. Anyone who can change its files (through a weak pipeline, shared admin account or mirror sync) controls every later boot. Secure Boot limits that to signed binaries, but scripts, menus, initrds and answer files stay in reach. MITRE's T1542.005 mitigations include auditing "the integrity of the running configuration and system image", boot integrity features and restricting unauthenticated protocols.
Exposed provisioning secrets
The most underestimated risk. Answer files and first boot data routinely hold password hashes, domain join credentials, enrollment tokens or SSH keys, served over plain HTTP to any client that asks for the right path. RFC 5970 warns about URLs that "contain sensitive information like user names and passwords".
Network boot left on after provisioning
Installed machines that keep network boot first let any rogue DHCP server redirect every reboot, even into an installer that wipes the disk.
Old, signed boot programs
Secure Boot trusts signatures, not versions, so a controlled boot path can serve an old, flawed but unrevoked shim. Keep dbx and SBAT current on clients, not just the server.
Comparison: legacy PXE, UEFI PXE, UEFI HTTP Boot and iPXE
| Legacy (BIOS) PXE | UEFI PXE | UEFI HTTP Boot | iPXE (chainloaded or built in) | |
|---|---|---|---|---|
| Discovery | DHCPv4, PXEClient, option 93 = 0 | DHCPv4 or DHCPv6, PXEClient, UEFI arch code (7 for x64) | DHCPv4 or DHCPv6, HTTPClient, HTTP arch code (0x10 for x64) | Its own DHCP request, or a fixed URL from an embedded script |
| Boot file location | siaddr/option 66 plus file/option 67 | Same, or OPT_BOOTFILE_URL on IPv6 | Full URI in option 67 or OPT_BOOTFILE_URL | Any URI: HTTP, HTTPS, TFTP, iSCSI and others |
| Transport | TFTP over UDP | TFTP over UDP | HTTP or HTTPS over TCP | HTTP, HTTPS, TFTP, storage protocols |
| Server authentication | None | None | With HTTPS and an imported CA | With HTTPS; default trusts public CAs via cross-signing, or your own root |
| Integrity of the first file | None | Secure Boot signature check | Secure Boot signature check, plus TLS in transit | Depends on how iPXE itself was loaded |
| Integrity of scripts, kernel, initrd | None | Depends on the boot loader | Depends on the boot loader; ISO and disk images boot from a RAM disk | imgverify per file, enforced with imgtrust --permanent in an embedded script |
| Secure Boot | Not applicable | Yes, for EFI executables | Yes, for EFI executables | Only if ipxe.efi is signed by a key the firmware trusts |
| Routing across subnets | Needs DHCP relay and TFTP reachability | Same | DHCP relay plus ordinary HTTP routing | Ordinary HTTP routing after the first hop |
| Typical role today | Older hardware, legacy fleets | Default network boot on UEFI hardware | Modern servers and VMs, installer ISOs | Second stage for flexible, scripted provisioning |
Which should you use?
| If your situation is | A reasonable choice |
|---|---|
| All clients are recent UEFI machines and you control the CA | UEFI HTTP Boot over HTTPS with a private CA, booting a signed shim and boot loader or a signed UKI |
| You need scripting, menus or iSCSI, and can manage your own keys | UEFI HTTP Boot or UEFI PXE to load a self-built, signed ipxe.efi with an embedded script that enforces imgtrust |
| You must support old BIOS clients | BIOS PXE to undionly.kpxe, then HTTPS and signed images inside iPXE, on an isolated VLAN |
| You only need to boot an installer ISO occasionally | UEFI HTTP Boot of the ISO over HTTPS; no TFTP server at all |
Step by step: build a UEFI HTTPS boot lab with dnsmasq and verify the chain
This lab runs on one Linux host: an isolated virtual network, dnsmasq for DHCP, an HTTPS server with a private CA, and a QEMU VM with OVMF firmware that boots over HTTPS into iPXE with enforced signature checks. Use test keys only. Commands assume Debian or Ubuntu; package names, OVMF paths and whether your OVMF build includes HTTP Boot and TLS vary by distribution.
Step 1: install the pieces
sudo apt install dnsmasq nginx qemu-system-x86 ovmf openssl sbsigntool build-essential git
sudo systemctl disable --now dnsmasq # run it only on the lab bridge
Step 2: create an isolated bridge
Boot servers answer broadcasts, so keep the lab off any shared network.
sudo ip link add br-boot type bridge
sudo ip addr add 192.168.100.1/24 dev br-boot
sudo ip link set br-boot up
sudo ip tuntap add dev tap-boot mode tap user "$USER"
sudo ip link set tap-boot master br-boot
sudo ip link set tap-boot up
Never attach a physical interface to br-boot, or your lab becomes a rogue DHCP server.
Step 3: make a private CA, a server certificate and a code signing certificate
The CA is the only trust anchor for both the firmware and iPXE.
mkdir -p ~/bootlab/ca && cd ~/bootlab/ca
openssl req -x509 -newkey rsa:3072 -nodes -days 365 \
-subj "/CN=Boot Lab Root CA" -keyout ca.key -out ca.crt
# Server certificate for the HTTPS boot server, with the IP address as a subject alternative name
openssl req -newkey rsa:3072 -nodes -subj "/CN=192.168.100.1" \
-addext "subjectAltName=IP:192.168.100.1" -keyout server.key -out server.req
openssl x509 -req -in server.req -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 90 -copy_extensions copy -out server.crt
# Code signing certificate for iPXE image signatures
openssl req -newkey rsa:3072 -nodes -subj "/CN=bootlab-signer" -keyout codesign.key -out codesign.req
printf "keyUsage=digitalSignature\nextendedKeyUsage=codeSigning\n" > codesign.ext
openssl x509 -req -in codesign.req -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 90 -extfile codesign.ext -out codesign.crt
openssl x509 -in codesign.crt -noout -ext keyUsage,extendedKeyUsage
These use OpenSSL 3 options. The last command should show Digital Signature and Code Signing, which iPXE requires. In production, keep ca.key and codesign.key on a separate signing host or hardware token, never on the boot server.
Step 4: configure dnsmasq for DHCP only, with per-architecture boot files
# /etc/dnsmasq.d/bootlab.conf
# DHCP only; disable the DNS function
port=0
interface=br-boot
bind-interfaces
dhcp-range=192.168.100.50,192.168.100.99,1h
log-dhcp
# Tag clients by option 93 (client system architecture), using IANA codes
dhcp-match=set:efi-x64-http,option:client-arch,16
# iPXE identifies itself with option 175; tag it so it gets a script, not itself
dhcp-match=set:ipxe,175
# UEFI HTTP Boot: answer with vendor class HTTPClient and an HTTPS URI
dhcp-option-force=tag:efi-x64-http,60,HTTPClient
dhcp-boot=tag:efi-x64-http,tag:!ipxe,https://192.168.100.1/ipxe.efi
# iPXE second DHCP request: hand out the signed boot script URI
dhcp-boot=tag:ipxe,https://192.168.100.1/boot.ipxe
From the dnsmasq manual: port=0 "completely disables DNS function"; dhcp-match tags a client whose option 93 list contains the number (use IANA values); dhcp-option-force sends option 60 even when not requested; dhcp-boot is IPv4 only (on IPv6, send option 59 with the option6: form of dhcp-option); the manual describes option 175 as the one sent to iPXE, and matching 175 in requests is the common way to recognize iPXE's own DHCP request (with the embedded script below, that second answer is only a fallback); log-dhcp shows every option. This lab has no TFTP. If BIOS clients need it, use tftp-root, which rejects .. paths, and tftp-secure, which serves only files owned by the dnsmasq user.
sudo dnsmasq --test --conf-file=/etc/dnsmasq.d/bootlab.conf
sudo dnsmasq --no-daemon --conf-file=/etc/dnsmasq.d/bootlab.conf
Step 5: build iPXE with HTTPS, code signing, your root and an enforcing script
cd ~/bootlab && git clone https://github.com/ipxe/ipxe.git && cd ipxe/src
# Enable HTTPS and the image trust command in the local build configuration
cat > config/local/general.h <<'EOF'
#define DOWNLOAD_PROTO_HTTPS
#define IMAGE_TRUST_CMD
EOF
# Embedded script: require trusted images permanently, then verify everything
cat > ~/bootlab/embed.ipxe <<'EOF'
#!ipxe
imgtrust --permanent
dhcp
imgfetch --name boot https://192.168.100.1/boot.ipxe
imgverify --signer bootlab-signer boot https://192.168.100.1/boot.ipxe.sig
chain boot
EOF
make bin-x86_64-efi/ipxe.efi EMBED=$HOME/bootlab/embed.ipxe \
TRUST=$HOME/bootlab/ca/ca.crt CERT=$HOME/bootlab/ca/ca.crt
TRUST= replaces the public CA trust with your lab root; CERT= embeds the full root, since only its fingerprint is built in by default; EMBED= bakes in the enforcing script. Then write the boot script it will fetch and verify:
cat > ~/bootlab/boot.ipxe <<'EOF'
#!ipxe
kernel https://192.168.100.1/vmlinuz console=ttyS0
imgverify --signer bootlab-signer vmlinuz https://192.168.100.1/vmlinuz.sig
initrd https://192.168.100.1/initrd.img
imgverify --signer bootlab-signer initrd.img https://192.168.100.1/initrd.img.sig
boot
EOF
The imgverify on initrd.img is the line people forget.
Step 6: sign files and publish them on an HTTPS server
sudo mkdir -p /srv/boot
sudo cp ~/bootlab/ipxe/src/bin-x86_64-efi/ipxe.efi ~/bootlab/boot.ipxe /srv/boot/
# Copy a test kernel and initrd from a distribution netboot or installer image
sudo cp /path/to/test/vmlinuz /path/to/test/initrd.img /srv/boot/
cd /srv/boot
for f in boot.ipxe vmlinuz initrd.img; do
sudo openssl cms -sign -binary -noattr -in "$f" \
-signer ~/bootlab/ca/codesign.crt -inkey ~/bootlab/ca/codesign.key \
-certfile ~/bootlab/ca/ca.crt -outform DER -out "$f.sig"
done
This is the signing form iPXE documents. Serve the directory over HTTPS only:
# /etc/nginx/sites-available/bootlab
server {
listen 192.168.100.1:443 ssl;
ssl_certificate /etc/nginx/bootlab/server-chain.crt;
ssl_certificate_key /etc/nginx/bootlab/server.key;
ssl_protocols TLSv1.2;
root /srv/boot;
autoindex off;
types {
application/efi efi;
}
default_type application/octet-stream;
}
sudo mkdir -p /etc/nginx/bootlab
cat ~/bootlab/ca/server.crt ~/bootlab/ca/ca.crt | sudo tee /etc/nginx/bootlab/server-chain.crt >/dev/null
sudo cp ~/bootlab/ca/server.key /etc/nginx/bootlab/
sudo ln -s /etc/nginx/sites-available/bootlab /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
curl --cacert ~/bootlab/ca/ca.crt -sI https://192.168.100.1/ipxe.efi | head -3
TLSv1.2 matches iPXE's documented ceiling, and application/efi is the type EDK2 treats as an EFI executable.
Step 7: give the firmware the CA and boot the VM
OVMF's README documents the etc/edk2/https/cacerts entry for trusted CAs. Build it from the lab CA alone with efisiglist (DER input); the README's p11-kit extract example would import every CA your host trusts.
cd ~/bootlab
openssl x509 -in ca/ca.crt -outform DER -out ca/ca.der
efisiglist -a ca/ca.der -o cacerts.bin
cp /usr/share/OVMF/OVMF_VARS_4M.fd ./vars.fd
qemu-system-x86_64 -machine q35 -m 2048 -nographic \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
-drive if=pflash,format=raw,file=./vars.fd \
-netdev tap,id=n0,ifname=tap-boot,script=no,downscript=no \
-device virtio-net-pci,netdev=n0 \
-fw_cfg name=etc/edk2/https/cacerts,file=cacerts.bin
Pick the HTTP boot entry in the firmware boot manager. If none appears, rebuild OVMF from EDK2 with NETWORK_HTTP_BOOT_ENABLE and NETWORK_TLS_ENABLE, plus NETWORK_ALLOW_HTTP_CONNECTIONS=FALSE to refuse http://, as the OVMF README describes. The dnsmasq log should show architecture 16 and HTTPClient; the nginx log should show ipxe.efi, boot.ipxe and each file with its .sig; imgstat in iPXE marks verified images [TRUSTED].
Step 8: prove the checks work
Watch it fail safely: build cacerts.bin from an unrelated certificate (the firmware should refuse TLS), append a comment to boot.ipxe without re-signing (iPXE should stop), and swap in the wrong initrd.img.sig (boot should stop at the initrd check). Check signatures server side in your release pipeline too:
cd /srv/boot
for f in boot.ipxe vmlinuz initrd.img; do
openssl cms -verify -binary -inform DER -in "$f.sig" -content "$f" \
-CAfile ~/bootlab/ca/ca.crt -purpose any -out /dev/null && echo "OK $f" || echo "BAD $f"
done
Step 9 (optional): turn on Secure Boot and check the EFI signatures
With Secure Boot on, the firmware refuses your ipxe.efi unless its signer is in db. Enroll a lab key per the own keys guide, then sign and check:
sbsign --key db.key --cert db.crt --output ipxe-signed.efi ipxe.efi
sbverify --list ipxe-signed.efi
sbverify --cert db.crt ipxe-signed.efi
For a distribution shim and GRUB, sbverify --list shows which certificate each depends on. Remove the lab afterwards with sudo ip link del tap-boot and sudo ip link del br-boot.
How do you secure network boot in production?
NIST SP 800-147 (April 2011) built firmware integrity on an authenticated update mechanism in which signatures block unauthorized firmware. Network boot needs the same idea one layer up, in four layers of control.
1. Contain the network
- A dedicated provisioning VLAN, holding machines only while they provision.
- DHCP snooping or equivalent switch features, so only designated ports can send DHCP server replies.
- Relay DHCP only from provisioning VLANs to the boot DHCP server.
- Reach TFTP and the boot web server only from the provisioning VLAN, per MITRE's "limit access to resource over network".
2. Authenticate what is downloaded
- UEFI HTTP Boot over HTTPS with a boot-only private CA; disable plain HTTP where firmware allows.
- Secure Boot on, booting signed shims, loaders and, where possible, signed UKIs.
- Self-built iPXE with HTTPS, signing, your own root and an embedded
imgtrust --permanentscript that verifies every file against a pinned signer. - Current
dbxand SBAT on clients, so old signed components cannot be replayed.
3. Protect the boot server and its content
- Treat the boot server as production: restricted admin access, read only TFTP root, web server without write access to boot files.
- Sign in the pipeline, not on the server, and publish only signed artifacts.
- Monitor file integrity of boot directories and alert on changes outside a release.
- Pin what each machine boots with per-machine scripts keyed by MAC or machine GUID (option 97).
4. Keep secrets out of the boot path
- Short lived, single use enrollment tokens instead of long lived credentials in answer files.
- Answer files over HTTPS only, ideally with client certificates, which iPXE supports.
- No passwords in boot URLs, which DHCP carries in clear text.
- After install, move network boot down the boot order and set a firmware setup password.
Timeline: how network boot evolved
| Date | Event | Source |
|---|---|---|
| July 1992 | RFC 1350 publishes TFTP revision 2; no authentication | RFC 1350 |
| March 1997 | RFC 2131 (DHCP) and RFC 2132 (options including 60, 66, 67) published; RFC 2131 calls DHCP "quite insecure" | RFC 2131, RFC 2132 |
| May 1998 | RFC 2348 and RFC 2349 add TFTP block size, transfer size and timeout options | RFC 2348, RFC 2349 |
| Late 1990s | Intel's PXE specification (cited as version 2.1) defines PXE; exact publication date not confirmed for this post | Not confirmed |
| November 2006 | RFC 4578 documents PXE options 93, 94 and 97 | RFC 4578 |
| September 2010 | RFC 5970 defines DHCPv6 network boot options 59 to 62, including the boot file URL | RFC 5970 |
| April 2011 | NIST SP 800-147, BIOS Protection Guidelines, published | NIST SP 800-147 |
| January 2015 | RFC 7440 adds the TFTP windowsize option | RFC 7440 |
| UEFI 2.5 | UEFI HTTP Boot introduced, per the iPXE project | iPXE UEFI HTTP note |
| 2020 | BootHole (CVE-2020-10713) revocation drives the SBAT design in shim | shim SBAT.md |
| 20 October 2020 | MITRE ATT&CK creates T1542.005, TFTP Boot | MITRE ATT&CK |
| 25 January 2024 | NVD publishes CVE-2023-40547 in shim's HTTP boot support | NVD |
Common mistakes
- Copying architecture codes from the RFC 4578 table instead of the IANA registry. Check
log-dhcpfor what clients send. - HTTPS in iPXE without
imgtrustandimgverify, with the default trust store accepting public CAs. - Verifying the kernel but not the initrd.
- Importing a full public CA bundle into firmware, so any publicly trusted certificate passes.
- A writable TFTP root.
- Secrets in answer files readable over plain HTTP by anyone on the VLAN.
- Disabling Secure Boot instead of signing boot programs.
- Network boot left first in the boot order on installed machines.
- A lab DHCP server on a shared network.
- Forgetting client-side revocation: a new shim on the server does not help a client whose
dbxstill trusts an old one.
FAQ
What is the difference between PXE and UEFI HTTP Boot?
PXE gets a server and file name from DHCP and downloads over unauthenticated TFTP. UEFI HTTP Boot gets a full URI and downloads over HTTP or HTTPS, which is faster, routable and, with an imported CA, authenticates the server.
What are DHCP options 66 and 67 used for?
Option 66 is the TFTP server name and option 67 the boot file name (RFC 2132), formally overflow for the sname and file header fields. For UEFI HTTP Boot, option 67 carries a full URI.
What is DHCP option 93?
The client system architecture type (RFC 4578): 0 for BIOS, 7 for x64 UEFI, 0x0B for 64 bit Arm UEFI, 0x10 for x64 UEFI HTTP Boot. The IANA registry is the current reference.
Is PXE boot secure?
Not by itself, because DHCP and TFTP are unauthenticated. Add an isolated network, DHCP snooping, Secure Boot, HTTPS with a private CA, signed scripts and no secrets in the boot path.
Does Secure Boot protect PXE boot?
Partly. It checks downloaded EFI programs and, via shim and GRUB, the kernel, but not DHCP answers, menus, scripts, command lines or a separate initrd. Signed UKIs and signed iPXE scripts cover most of that gap.
What is iPXE chainloading?
An existing PXE or HTTP Boot client downloads iPXE, which takes over with HTTPS, scripts and menus. You stop it reloading itself with autoexec.ipxe, a DHCP rule or an embedded script.
Does iPXE support HTTPS and signed images?
Yes, built with DOWNLOAD_PROTO_HTTPS and IMAGE_TRUST_CMD: TLS up to 1.2, CMS signatures via imgverify, enforcement via imgtrust in an embedded script. Verify the initrd separately.
Can UEFI HTTP Boot boot an ISO file directly?
In EDK2, yes: application/vnd.efi-iso or application/vnd.efi-img responses are booted from a RAM disk. Vendor builds vary.
Does network boot work over IPv6?
Yes. RFC 5970 defines DHCPv6 boot options, and EDK2 has build settings for IPv6 PXE and IPv6 HTTP Boot; vendor support varies.
Why does UEFI HTTPS Boot fail with a certificate error?
Usually the firmware has no trusted CA: EDK2 ships with none, so import yours. Also check that the certificate name or IP matches the URI and the server sends the full chain.
Where Safeguard fits
Safeguard's fit here is indirect. It does not inspect firmware, scan boot images, watch the boot network or manage devices. It covers the software around network boot: provisioning repositories and boot server container images can be scanned for known vulnerable open source dependencies (safeguard scan --dir . or safeguard scan --image <name>), with reachability ranking, a view of which projects carry a component version, and policy gates in CI via safeguard exec. It cannot tell you whether a client's dbx is current, a DHCP server is rogue or an answer file leaks a password.
Network boot looks like a firmware topic, but most of what a boot server runs is ordinary open source software: a DHCP server, a web server, iPXE, boot loaders and the container images and scripts that package them. Those components get vulnerabilities like any other dependency, and a rebuilt fleet pulls whatever versions your boot pipeline last published, so keeping that pipeline current matters.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The demos show Safeguard on ordinary software projects, not firmware or boot traffic; they apply to provisioning repositories, boot server containers and the CI jobs that build and sign boot images.
The first demo is a real Safeguard CLI session. After install, safeguard opens the interactive terminal, you ask it to fix the lodash CVE in a repository, and it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with your approval, prints a summary and pushes nothing. It ends with /permissions, /model and /scan. The same approval-first loop suits boot tooling that reaches a whole fleet.
Click through it at your own pace, or open it full screen.
The local runner demo shows how to set up the runner on your own machine, approve it with its device code, and then scan from the terminal. A runner on a build host inside an isolated provisioning network can scan boot server images where they live.
Click through it at your own pace, or open it full screen.
The third demo shows how to connect Claude to the Safeguard MCP server with OAuth and choose which tools Claude can use, so an engineer can ask about known vulnerabilities in provisioning projects using only the tools you allowed.
Click through it at your own pace, or open it full screen.
To try it on your own repositories, download Safeguard and follow the CLI documentation.
Quick check
A UEFI client boots iPXE over HTTPS, and iPXE runs an embedded script that permanently requires trusted images. The boot script verifies the kernel but not the initrd. Is the initrd protected?
No. Per the iPXE docs, imgtrust blocks an unsigned kernel but not an unsigned initrd, so the initrd needs its own imgverify line.
Your DHCP server sends ipxe.efi to clients whose option 93 is 9, following the RFC 4578 table, but x64 UEFI machines never get it. Why?
RFC 4578's table lists 9 as "EFI x86-64", but the IANA registry lists 7 as x64 UEFI and 9 as EBC. Match on 7 (and 16 for x64 HTTP Boot).
Sources
- RFC 1350: The TFTP Protocol (Revision 2)
- RFC 2131: Dynamic Host Configuration Protocol
- RFC 2132: DHCP Options and BOOTP Vendor Extensions
- RFC 2348: TFTP Blocksize Option
- RFC 2349: TFTP Timeout Interval and Transfer Size Options
- RFC 4578: DHCP Options for the Intel Preboot eXecution Environment (PXE)
- RFC 5970: DHCPv6 Options for Network Boot
- RFC 7440: TFTP Windowsize Option
- IANA: DHCPv6 parameters, Processor Architecture Types registry
- iPXE: Cryptography
- iPXE: imgtrust command
- iPXE: imgverify command
- iPXE: chain command
- iPXE: Chainloading iPXE
- iPXE: Embedded scripts
- iPXE: UEFI HTTP chainloading
- iPXE: crosscert setting
- iPXE: DOWNLOAD_PROTO_HTTPS build option
- iPXE: UEFI Secure Boot signing work in progress
- iPXE: Documentation index
- dnsmasq manual page
- EDK2 NetworkPkg.dec (PcdAllowHttpConnections)
- EDK2 HttpBootDxe: HttpBootDhcp4.c
- EDK2 HttpBootDxe: HttpBootSupport.c
- EDK2 OvmfPkg README (HTTPS Boot section)
- shim README
- shim SBAT.md
- MITRE ATT&CK T1542.005: Pre-OS Boot, TFTP Boot
- NVD: CVE-2023-40547
- NIST SP 800-147: BIOS Protection Guidelines
Last reviewed: 24 October 2026. Firmware HTTP Boot and HTTPS support, default trust anchors and allowed URI schemes vary by vendor and firmware build; check your platform's documentation and test in an isolated lab before changing a production boot network.