Skip to main content

Road to PXE - Day 2

·1196 words·6 mins

…last time on the road to PXE #

In the last post we read about the bootfile dilemma, setup a rudimentary DHCP listener and after fighting with a pesky firewall we were able to see some DHCP traffic.

After getting DHCP working I realised I was putting the cart before the horse a little bit. I can’t solve the bootfile dilemma until I can serve files in the first place…

So with that in mind it was time to implement TFTP.

Implementing TFTP #

After a quick google search I found a rust program called rs-tftpd; However, digging into it, rs-tfptd is synchronous, single threaded and designed to run as a standalone app. What I need is an asynchronous library that works with tokio that I can slap in and call it a day.

Back to google…

async-tftp #

After a little more googling I found a library called async-tftp. This library is just what I needed, async and smol seems to play nicely with tokio.

Reading through the docs and looking at the example it seemed easy enough so I quickly commented out my DHCP code for the time being and got to work adding in TFTP.

After 2 minutes we had the code and it was compiling

let tftpd = TftpServerBuilder::with_dir_ro("./tftp_root")
    .unwrap()
    .bind("0.0.0.0:69".parse().unwrap())
    .block_size_limit(1024)
    .build()
    .await
    .unwrap();

tftpd.serve().await.unwrap();

I remembered my previous errors and quickly added port 69 UDP to the firewall in my nix config… nice!

WHAT THE FUCK DO I DO NOW!!!!

I was expecting TFTP to take a long time but here we are a theoretically working TFTP server that I had to do almost nothing to get working… That’s right bootfiles…

bootfiles #

This is a rabbit hole that I fell really far into. Unfortunately I have wasted hours here and probably will waste many more down the line.

To spare you the details I will skip to iPXE.

iPXE is fucking cool #

Why I love iPXE so far

  • iPXE allows booting from HTTP (yep basically any old web server)
  • iPXE has a script format (.ipxe) to specify how and what it boots
  • iPXE allows you to use things like wimboot to boot into WinPE

So with my little love letter out of the way I did a cheeky curl and dropped undionly.kpxe into my tftp_root folder.

With my bootfile downloaded I quickly added it to my router’s DHCP config and spun up a proxmox vm with no disk or iso to test our PXE…

A PXE boot screen with iPXE and undionly.kpxe… thats a funny order
IT LIVES!
Well would you look at that we can see iPXE and our bootfile being loaded.
thats a funny order - foreshadowing

WinPE? #

One of the things I wanted to get working on my PXE server was WinPE. How hard can that be right?

Screenshot of the Windows ADK - WinPE Installer
Windows Moment

After installing the Windows ADK and WinPE addon I was ready to go, I located the files and my eyes glazed over.

What do I actually need…

Google… as always

I suck at following instructions 😭 #

After finding the article I need I proceed to immediately fuck up the first step and not run the Deployment and Imaging Tools Environment they even bolded it for me 😭

After going as far as starting to reverse engineer the copype.cmd file and starting to work out what environment variables I would need to set to make it work I had a quick moment of clarity and realised what I was doing was insane.

I booted up youtube and found a video of someone making a winpe image and the first step hit me like a bag of bricks.. god I am an idiot sometimes :)

After making the image I zipped and shipped it to my desktop and chucked her in the tftp_root… great now what?

Wrong root ? #

Well you will remember in my little love letter to iPXE earlier I mentioned it supported WinPE. I didn’t really look into the specifics of that before just diving into getting the WinPE files.

After yet another google I found the instructions for WinPE - here

Shit I need a http server too now.. these servers are really getting out of control

Back on home turf #

I have used acticx-web quite a few times and already kinda had an inkling that I would need/want a web server for this at some point. For that reason I had been making sure all my libraries played nicely with tokio for when I inevitably had to add a web server.

I quickly commented out the TFTP server as well and got to work adding acticx…

Firefox at localhost. The text on the page is Hello, world!
AWWW SHIT HERE WE GO AGAIN

I quickly added a static file service and we were good to go.

HttpServer::new(|| {
    App::new()
        .wrap(Logger::default())
        .service(hello)
        .service(fs::Files::new("/", "./http_root"))
})
.bind(("0.0.0.0", 80))?
.run()
.await
.map_err(anyhow::Error::from)

Fortunately I have done background tasks with actix before so it took no time at all to wrap my tftp server and dhcp listener (which I had prettied up the logging for) with acticx_rt::spawn and we were off to the races.

I quickly obvious lie added wimboot and boot.ipxe script to my http_root

#!ipxe
set arch amd64
kernel wimboot
initrd ${arch}/media/Boot/BCD			BCD
initrd ${arch}/media/Boot/boot.sdi		boot.sdi
initrd ${arch}/media/sources/boot.wim	boot.wim
boot

With that done all I had left to do was to work out how to tell iPXE to load our boot.ipxe script via HTTP

All my homies hate DHCP #

So apparently the way to tell iPXE where to get files is DHCP again. This is my reoccuring nightmare, every time I want to do something it’s FUCKING DHCP 😭

Two astronauts looking at earth. Wait.. it’s all DHCP? Always has been…

I will again spare you the hours of google I subjected myself to and instead give you this stupid screenshot of what I had to do in my edgerouter config to get different boottfiles depending on whether it was iPXE or not.

If you are visually impared I am so sorry
DHCP Fuckery

🥁🥁🥁🥁 drumroll please 🥁🥁🥁🥁 #

A screenshot of WinPE booted in Proxmox, the logs below show the files being loaded via HTTP
WOOOOOOOOOOOO!

Yep, we’ve done it fellas. PXE boot solved. My job here is done… right

Remember the foreshadowing earlier about the order being weird for iPXE? well in the above screenshot if you squint really hard you will notice it says TFTP Request: bc:24:11:ca:93:49 (Intelx86PC - iPXE) three times.

NOTE
Even though it says TFTP Request it actually means DHCP PXE request because I didn’t think about how easy the two would be to mix up in this context whoops.

This seemed weird to me, I just spent hours working out how to make my router serve different files based on the “user-class” field. The same one that on every request above just shows iPXE…

My theory here is that SeaBIOS on proxmox has iPXE builtin so it’s jumping straight to the HTTP boot and skipping the TFTP step entirely. If anyone reading this knows if this is the case or why this is happening please hit me up.

Anyway, I tried updating the VM in proxmox to use OVMF and I immediately saw the first requests are not from iPXE and they hit the TFTP server again.

Are we there yet? #

No..
Unfortunately at this point I hit the issue I spent so much time talking about in the first post… the dreaded bootfile dilemma. Our undionly.kpxe file is meant for BIOS machines not UEFI.

Let’s save that for the next one 💤