Intro
PXE (Preboot Execution Environment) Booting, or just Network booting in general is very interesting, at least to me, and a few others. As I believe it was Marty Connor in this awesome video "gPXE: Modern FOSS Network Booting" said that some people get really excited over booting machines over networks (including the Internet!) while others... not so much.
Well, I'm one of those people who gets really excited over the idea of booting machines over a network, and I can't really put my finger on why, it's just awesome to me.
So, I wanted to document the netboot setups that I use at my home, and my work. This entry consists of my work setup. My home setup is detailed here
Now, network booting isn't for everyone, and it doesn't fit every situation, so your mileage will vary greatly.
My work setup consists of iPXE, ASP Scripting, Syslinux, and different separate utilities. All of this is detailed below... so lets begin!
What does this page assume?
- You have a working network
- You control your DHCP Server
- You have control of your DNS server
- You have a working webserver
- Basic understanding of ASP
- Have a basic understand of whats involved with PXE Booting, even if it's skimming over the Wikipedia page
- Have a machine that is capable of picking the network card to boot from, via PXE (On most Dell systems, you need to go into the BIOS, Integrated Peripherals, and mark the NIC as "On W/ PXE", not just "On", or "On W/ ImageServer"
My Environment
- Windows Server 2008 R2
- IIS 7
- Classic ASP Scripting
- tftpd32 v4.00 (Service)
- iPXE (current GIT master)
- Syslinux 4.06_Pre11
- Misc Utilities like Drive Fitness Test, SeaTools, Memtest, etc.
The Basic Process
-- My Setup
- Computer powers on, and selects the NIC to boot from, either via interaction, or it being the first device
- The native PXE Stack (iPXE (flashed onto the ROM/BIOS), Intel, Broadom, Realtek, etc) brings up the network card, does a DHCP Request, while also requesting, at least, options 66 and 67
- DHCP Server responds with an IP, and the two options
- The PXE Stack then tried to contact the server provided in option 66, to retrieve the file specified in option 67, which in this case is iPXE (for non-iPXE clients), over TFTP
- iPXE then unloads the native PXE stack (to a degree), and takes over, issuing it's own DHCP Request, again requesting, among other options, 66 and 67
- The DHCP Server responds with (typically) the same IP address, but now detects that the client is iPXE, and passes a different option 67.
- iPXE then boots to the URL passed in option 67 this time (via HTTP), and that script then directs it what to do.
Files
IIS Config
<Place holder for the time being>
TFTPD32 Config
<Place holder for the time being>
preboot.asp
<%
response.contenttype="text/plain"
dim mac
dim plembed
dim clientip
dim code
dim code1
' Set code
code = "onereallylongcodethatsrandomlygenerated"
' Set Code1
code1 = "secondreallylongcodethatsrandomlygenerated"
' Set IP Subnets that do not get prompted for login
dim nologinips(1)
nologinips(0) = "10.80.18" ' Subnet for NetApps
nologinips(1) = "10.80.38" ' Subnet in Technicians Office
' Get the client's IP Address
clientip = request.servervariables("REMOTE_ADDR")
' Check to see if the PXE boot thats hitting us is authorized, the boot will embed two random codes
' this is also used to try and hide the user/pass on the command since it's shown in plain text in the URL
' Check Code0
if not request.querystring("code").count = 0 then
if request.querystring("code") = code then
' Check Code1
if not isEmpty(request.querystring("code1")) then
if not request.querystring("code1") = code1 then
' Fail to boot, don't offer login screen again
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 001 - This PXEBoot is not authorized." & vbcrlf & "exit 1")
response.end
end if
end if
else
' Fail to boot, don't offer login screen again
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 002 - This PXEBoot is not authorized."& vbcrlf & "exit 1")
response.end
end if
else
' Fail to boot, don't offer login screen again
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 003 - This PXEBoot is not authorized."& vbcrlf & "exit 1")
response.end
end if
'Get MAC Address into a variable
' This should always be handed over to this script.
If not isEmpty(request.querystring("MAC")) Then
mac = request.querystring("MAC")
else
' If no MAC found, it's most likely a bad script, or unauthorized netboot that has somehow gotten this far
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 009 - MAC Not listed."& vbcrlf & "exit 1")
response.end
End If
' Find out of Pxelinux is embedded or not
If not isEmpty(request.querystring("plembed")) Then
plembed = request.querystring("plembed")
else
plembed = 0
End If
' Compare the current client IP to IPs we shouldn't prompt for login for
for i=0 to ubound(nologinips)
if instr(clientip, nologinips(i)) then
'Perform functions for clients that don't need to be prompted for login
response.write("#!ipxe"&vbcrlf)
response.write("echo On IP "&clientip&" -- Bypassing Login, Autologging in as CS."&vbcrlf)
response.write("chain -ar http://netboot.example.com/boot.asp?MAC="&mac&"&code="&code&"&user=cs&pass=a12345B&plembed="&plembed&"&code1="&code1&vbcrlf)
response.end
end if
next
response.write("#!ipxe"&vbcrlf)
response.write("echo On IP "&clientip&" -- Forcing Login."&vbcrlf)
response.write("login"&vbcrlf)
response.write("chain -ar http://netboot.example.com/boot.asp?MAC="&mac&"&code="&code&"&user=${username:uristring}&pass=${password:uristring}&plembed="&plembed&"&code1="&code1&vbcrlf)
response.end
%>
boot.asp
<%
response.contenttype="text/plain"
dim mac
dim user
dim pass
dim plembed
dim testing
'Check to see if the PXE boot thats hitting us is authorized, the boot will embed two random codes
'this is also used to try and hide the user/pass on the command since it's shown in plain text
' Check Code0
if not request.querystring("code").count = 0 then
if request.querystring("code") = "onereallylongcodethatsrandomlygenerated" then
' Check Code1
if not isEmpty(request.querystring("code1")) then
if not request.querystring("code1") = "secondreallylongcodethatsrandomlygenerated" then
' Fail to boot, don't offer login screen again
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 001 - This PXEBoot is not authorized." & vbcrlf & "exit 1")
response.end
end if
end if
else
' Fail to boot, don't offer login screen again
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 002 - This PXEBoot is not authorized."& vbcrlf & "exit 1")
response.end
end if
else
' Fail to boot, don't offer login screen again
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 003 - This PXEBoot is not authorized."& vbcrlf & "exit 1")
response.end
end if
'Check to see if user is set, and not empty, if it is, then do the same with the password.
'If set, copy the results to variables
'Otherwise fail over with an error code.
if not request.querystring("user").count = 0 and not isEmpty(request.querystring("user")) then
user = request.querystring("user")
if not request.querystring("pass").count = 0 and not isEmpty(request.querystring("pass")) then
pass = request.querystring("pass")
else
' If no password is set, fail (either blank, or login command was not issued)
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 004 - No Password Set."& vbcrlf & "exit 1")
response.end
end if
else
' If the username is blank, fail (either left blank, or login command was not issued)
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 005 - No Username Set."& vbcrlf & "exit 1")
response.end
end if
'Verify user/pass combination
'Other users can be added here, following the same code (can use the cs or shd variables if returning the same menu
'Otherwise new variables can be added for more functionality)
'First is user 'cs' - Computing Services
if user = "cs" then
' If the password needs to change, change it here
if not pass = "a12345B" then
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 006 - Wrong Password." & vbcrlf & "exit 1")
response.end
else
' Set the proper code for which menu (or other functions) should be returned to the user
cs = 1
shd = 0
end if
'Next user is 'shd' - Student HelpDesk
elseif user = "shd" then
' If the password needs to change, change it here
if not pass = "B54321a" then
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 007 - Wrong Password." & vbcrlf & "exit 1")
response.end
else
' Set the proper code for which menu (or other functions) should be returned to the user
cs = 0
shd = 1
end if
else
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 008 - Invalid Username." & vbcrlf & "exit 1")
response.end
end if
'Get MAC Address into a variable
' This should always be handed over to this script.
If not isEmpty(request.querystring("MAC")) Then
mac = request.querystring("MAC")
else
' If no MAC found, it's most likely a bad script, or unauthorized netboot that has somehow gotten this far
response.write("#!ipxe" & vbcrlf & "echo Boot Error Code: Error 009 - MAC Not listed."& vbcrlf & "exit 1")
response.end
End If
' Find out of Pxelinux is embedded or not
If not isEmpty(request.querystring("plembed")) Then
plembed = request.querystring("plembed")
else
plembed = 0
End If
' Check the MAC, this is where we can specify certain PCs get certain things
' This was changed to #!ipxe on 5/7/12, if gPXE is still in use, it may fail, however most builds are now ipxe
response.write("#!ipxe" & vbCrLF)
Select Case mac
Case "00:0c:29:xx:xx:xx" ' This is a VM for testing the netboot with.
ipxemenu
Case "84:2b:2b:xx:xx:xx" ' Dell 980
ipxemenu
Case Else
' Fall back and set the pxelinux variables and what variables should work with what menus
' Variable here (current) set based on username provided
response.write("set 210:string http://netboot.example.com/" & vbcrlf)
If cs = 1 Then
response.write("set 209:string mainmenu.ipxe" & vbcrlf)
ElseIf shd = 1 Then
response.write("set 209:string mainmenu-shd.ipxe" & vbcrlf)
End If
' If pxelinux is embedded, load it from the embedded image, otherwise pull it from the server
If plembed = 1 Then
response.write("imgload pxelinux.0" & vbcrlf)
response.write("imgexec pxelinux.0" & vbcrlf)
Else
response.write("chain ${210:string}pxelinux.0" & vbcrlf)
End If
End Select
sub ipxemenu()
response.write(":mainmenu" & vbcrlf)
response.write("menu Work Netboot" & vbcrlf)
response.write("item --gap -- ----------- Ghost Boots -----------" & vbcrlf)
response.write("item --key w wimboot Symantec Ghost PE via WIMBoot" & vbcrlf)
response.write("item --gap -- ------ Hardware Diagnostics -------" & vbcrlf)
response.write("item --key m memtest Memtest" & vbcrlf)
response.write("item --key d dft Drive Fitness Test v4.16" & vbcrlf)
response.write("item --key l wddld WD Data Lifegaurd Diagnostics v5.04f" & vbcrlf)
response.write("item --key s seatools Seagate Seatools for DOS v2.23" & vbcrlf)
response.write("item --gap -- --------- Disk Utilities ----------" & vbcrlf)
response.write("item --key q qwipe Quick Wipe (Default Disk: 0)" & vbcrlf)
response.write("item --key a dban Darik's Boot and Nuke (DBAN) v2.2.6" & vbcrlf)
response.write("item --key h shdd Salvation HDD Scan and Restore v3.0" & vbcrlf)
response.write("item --key p pmagic Parted Magic" & vbcrlf)
response.write("item --key g gparted GParted (Gnome Partition Editor)" & vbcrlf)
response.write("item --gap -- ---------- Other Options ----------" & vbcrlf)
response.write("item --key o nboot Other Netboot Systems Menu" & vbcrlf)
response.write("item --key i installers Installers Submenu" & vbcrlf)
response.write("item --key x shell iPXE Shell" & vbcrlf)
response.write("item default Default VesaMenu" & vbcrlf)
response.write("choose label && goto ${label}" & vbcrlf)
response.write(":dft" & vbcrlf)
response.write("sanboot --drive=0x00 dft32_v416_b00_install.IMG" & vbcrlf)
response.write("goto mainmenu" & vbcrlf)
response.write(":wddld" & vbcrlf)
response.write("sanboot --drive=0xa0 Diag504fCD.iso" & vbcrlf)
response.write("goto mainmenu" & vbcrlf)
response.write(":nboot" & vbcrlf)
response.write("menu Work Netboot - Other Netboot Systems" & vbcrlf)
response.write("item --key c citrix Citrix Provisioning Service (Citrix-DP)" & vbcrlf)
response.write("item --key t tstation Thinstation" & vbcrlf)
response.write("item --key r mainmenu Return to Main Menu" & vbcrlf)
response.write("choose label && goto ${label}" & vbcrlf)
response.write(":installers" & vbcrlf)
response.write("menu Work Netboot - Installers Menu" & vbcrlf)
response.write("item --key 3 7x32 Windows 7 32bit Installer" & vbcrlf)
response.write("item --key 6 7x64 Windows 7 64bit Installer" & vbcrlf)
response.write("item --key p esxi51sl ESXi 5.1 Installer via PXELinux" & vbcrlf)
response.write("item --key i esxi51 ESXi 5.1 Installer native iPXE" & vbcrlf)
response.write("item --key r mainmenu Return to Main Menu" & vbcrlf)
response.write("choose label && goto ${label}" & vbcrlf)
response.write(":memtest" & vbcrlf)
response.write("chain memtest.0" & vbcrlf)
response.write("goto mainmenu" & vbcrlf)
response.write(":esxi51sl" & vbcrlf)
response.write("set 210:string http://netboot.example.com/" & vbcrlf)
response.write("set 209:string esxi51/esxi.cfg" & vbcrlf)
If plembed = 1 Then
response.write("imgload pxelinux.0" & vbcrlf)
response.write("imgexec pxelinux.0" & vbcrlf)
Else
response.write("chain ${210:string}pxelinux.0" & vbcrlf)
End If
response.write("goto end" & vbcrlf)
response.write(":esxi51" & vbcrlf)
response.write("chain http://netboot.example.com/esxi51/esxi51.ipxe" & vbcrlf)
response.write("goto end" & vbcrlf)
response.write(":wimboot" & vbcrlf)
response.write("imgfree" & vbcrlf)
response.write("kernel wimboot" & vbcrlf)
response.write("initrd ghostpe/bootmgr.exe bootmgr.exe" & vbcrlf)
response.write("initrd ghostpe/BCD BCD" & vbcrlf)
response.write("initrd ghostpe/fonts/chs_boot.ttf chs_boot.ttf" & vbcrlf)
response.write("initrd ghostpe/fonts/cht_boot.ttf cht_boot.ttf" & vbcrlf)
response.write("initrd ghostpe/fonts/kor_boot.ttf kor_boot.ttf" & vbcrlf)
response.write("initrd ghostpe/fonts/jpn_boot.ttf jpn_boot.ttf" & vbcrlf)
response.write("initrd ghostpe/fonts/wgl4_boot.ttf wgl4_boot.ttf"& vbcrlf)
response.write("initrd ghostpe/boot.sdi boot.sdi" & vbcrlf)
response.write("initrd ghostpe/boot.wim boot.wim" & vbcrlf)
response.write("boot" & vbcrlf)
response.write(":7x64" & vbcrlf)
response.write("sanboot --drive 0xA0 --no-describe http://netboot.example.com/sysinstalls/W7ENTx64.iso" & vbcrlf)
response.write(":7x32" & vbcrlf)
response.write("sanboot --drive 0x81 --no-describe http://netboot.example.com/sysinstalls/W7ENTx32.iso" & vbcrlf)
response.write(":default" & vbcrlf)
response.write("set 210:string http://netboot.example.com/" & vbcrlf)
If cs = 1 Then
response.write("set 209:string mainmenu.ipxe" & vbcrlf)
ElseIf shd = 1 Then
response.write("set 209:string mainmenu-shd.ipxe" & vbcrlf)
End If
If plembed = 1 Then
response.write("imgload pxelinux.0" & vbcrlf)
response.write("imgexec pxelinux.0" & vbcrlf)
Else
response.write("chain ${210:string}pxelinux.0" & vbcrlf)
End If
response.write(":end" & vbcrlf)
end sub
%>