{"id":2300,"date":"2021-03-11T18:58:45","date_gmt":"2021-03-12T00:58:45","guid":{"rendered":"https:\/\/mrguitar.net\/?p=2300"},"modified":"2021-03-16T16:39:34","modified_gmt":"2021-03-16T22:39:34","slug":"uefi-http-boot-with-libvirt","status":"publish","type":"post","link":"https:\/\/mrguitar.net\/?p=2300","title":{"rendered":"UEFI HTTP Boot with Libvirt"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"480\" src=\"https:\/\/mrguitar.net\/wp-content\/uploads\/2021\/03\/UEFI-HTTP.png\" alt=\"\" class=\"wp-image-2309\" srcset=\"https:\/\/mrguitar.net\/wp-content\/uploads\/2021\/03\/UEFI-HTTP.png 640w, https:\/\/mrguitar.net\/wp-content\/uploads\/2021\/03\/UEFI-HTTP-300x225.png 300w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Background<\/h2>\n\n\n\n<p>I\u2019ve been a big proponent of network based provisioning pretty much my entire career. My second job out of college involved imaging ~800 computers multiple times a week. When I was hired, my predecessors used floppy disks to load a small OS, matching NIC driver, and imaging client (remember Ghost?!). The bottom line was it was very time\/labor intensive and a horrible process. Imaging a group of systems took about 30-60 min. Long story short we reduced that time to about 5 min after we leveraged a combination of<a href=\"https:\/\/en.wikipedia.org\/wiki\/Preboot_Execution_Environment\"> PXE<\/a>, wake-on-lan, UNDI drivers, vlans, and IGMP snooping. My second iteration of the solution took the total attended time to less than 30 seconds. Anyway, it\u2019s amazing technology for provisioning, and I even got hired at Red Hat by giving a presentation on PXE. Needless to say, I\u2019m a huge fan!<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Anyway, the problem here is PXE dates back to the 90s and is quite limited by its reliance on technologies such as TFTP. Intel has been threatening to deprecate PXE for years now and they are finally doing it. Even though PXE is pervasive today, it&#8217;s likely that over the next 2-10 years UEFI HTTP boot will become the default for most environments. Other benefits and technical details are outlined<a href=\"https:\/\/uefi.org\/sites\/default\/files\/resources\/FINAL%20Pres4%20UEFI%20HTTP%20Boot.pdf\"> here<\/a>. The TL;DR is PXE relies on DHCP &amp; TFTP and UEFI HTTP boot needs DHCP &amp; HTTP. Sound easy? That\u2019s because it is. You might even say it\u2019s as <strong>T<\/strong>rivial as <strong>F<\/strong>ile <strong>T<\/strong>ransfer <strong>P<\/strong>rotocols get. \u2026.sorry network booting humor is pretty difficult to come by.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Switching from PXE to HTTP Boot<\/h2>\n\n\n\n<p>The best documentation I can find on this topic currently is from the good folks at Suse<a href=\"https:\/\/en.opensuse.org\/UEFI_HTTPBoot_Server_Setup\"> here<\/a>. Essentially, we drop the need for TFTP and need to tweak the DHCP options to fetch the NBP from a HTTP\/HTTPS endpoint. I\u2019ll be using GRUB as the NBP in this post. Environments using dhcpd can simply drop-in the configuration they provide and everything should just work assuming you tweak the NBP for your environment:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">   class \"pxeclients\" {\n     match if substring (option vendor-class-identifier, 0, 9) = \"PXEClient\";\n     next-server 192.168.111.1;\n     filename \"\/bootx64.efi\";\n   }\n   class \"httpclients\" {\n     match if substring (option vendor-class-identifier, 0, 10) = \"HTTPClient\";\n     option vendor-class-identifier \"HTTPClient\";\n     filename \"<a href=\"http:\/\/www.httpboot.local\/sle\/EFI\/BOOT\/bootx64.efi\">http:\/\/www.httpboot.local\/sle\/EFI\/BOOT\/bootx64.efi<\/a>\";\n   }<\/pre>\n\n\n\n<p>This example is nice as it allows UEFI systems loading a legacy PXE rom to also boot. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Let\u2019s Apply this to Libvirt<\/h2>\n\n\n\n<p>I had originally planned to set this up on my home network, but pfSense doesn\u2019t yet support this nor does it allow the dhcpd config to be modified directly. I\u2019ve opened an <a href=\"https:\/\/redmine.pfsense.org\/issues\/11659\">issue<\/a> and hopefully this will be added in the future. The next simplest option I have for testing is using Libvirt and VMs w\/ the OVMF (Tianocore) UEFI firmware. Libvirt works great for creating a self-contained environment for experimenting with technology like this and it\u2019s super easy to replicate. All we really need to do is provide the correct DHCP options.<\/p>\n\n\n\n<p>The default libvirt network will look something similar to this:<\/p>\n\n\n<pre>&lt;network&gt;<br>  &lt;name&gt;default&lt;\/name&gt;<br>  &lt;uuid&gt;75f4d9cd-9af2-4df5-afcc-f8f9145f7e34&lt;\/uuid&gt;<br>  &lt;forward mode='nat'\/&gt;<br>  &lt;bridge name='virbr0' zone='trusted' stp='on' delay='0'\/&gt;<br>  &lt;mac address='52:54:00:95:95:84'\/&gt;<br>  &lt;ip address='192.168.122.1' netmask='255.255.255.0'&gt;<br>    &lt;dhcp&gt;<br>      &lt;range start='192.168.100.128' end='192.168.100.254' \/&gt;<br>    &lt;\/dhcp&gt;<br>  &lt;\/ip&gt;<br>&lt;\/network&gt;<\/pre>\n\n\n<p>Enabling legacy PXE is well documented, but I&#8217;m including the changes here in case it&#8217;s helpful for anyone reading. We basically define the next-server and filename DHCP options.<\/p>\n\n\n<pre>&lt;network&gt;<br>  &lt;name&gt;default&lt;\/name&gt;<br>  &lt;uuid&gt;75f4d9cd-9af2-4df5-afcc-f8f9145f7e34&lt;\/uuid&gt;<br>  &lt;forward mode='nat'\/&gt;<br>  &lt;bridge name='virbr0' zone='trusted' stp='on' delay='0'\/&gt;<br>  &lt;mac address='52:54:00:95:95:84'\/&gt;<br>  &lt;ip address='192.168.122.1' netmask='255.255.255.0'&gt;<br>  &lt;tftp root='\/var\/lib\/tftpboot'\/&gt;<br>    &lt;dhcp&gt;<br>      &lt;range start='192.168.100.128' end='192.168.100.254' \/&gt;<br>      &lt;bootp file='pxelinux.0'\/&gt;<br>    &lt;\/dhcp&gt;<br>  &lt;\/ip&gt;<br>&lt;\/network&gt;<\/pre>\n\n\n<p>Unfortunately the libvirt schema doesn&#8217;t support a lot of available config options for dnsmasq. Luckily, recent versions of libvirt support an XML namespace that will append options directly to the bottom of the generated config. <a href=\"https:\/\/lists.thekelleys.org.uk\/pipermail\/dnsmasq-discuss\/2018q3\/012451.html\">This email shows<\/a> the only working dnsmasq config that I was able to find that works as http boot is not documented well for the project. For now I&#8217;m leaving the tftp info in place so VMs using bios can continue to do network installs. <strong>Note:<\/strong> don&#8217;t ignore the first line of this example!<\/p>\n\n\n<pre>&lt;network xmlns:dnsmasq='http:\/\/libvirt.org\/schemas\/network\/dnsmasq\/1.0'&gt;<br>  &lt;name&gt;default&lt;\/name&gt;<br>  &lt;uuid&gt;75f4d9cd-9af2-4df5-afcc-f8f9145f7e34&lt;\/uuid&gt;<br>  &lt;forward mode='nat'\/&gt;<br>  &lt;bridge name='virbr0' zone='trusted' stp='on' delay='0'\/&gt;<br>  &lt;mac address='52:54:00:95:95:84'\/&gt;<br>  &lt;ip address='192.168.122.1' netmask='255.255.255.0'&gt;<br>  &lt;tftp root='\/var\/lib\/tftpboot'\/&gt;<br>    &lt;dhcp&gt;<br>      &lt;range start='192.168.100.128' end='192.168.100.254' \/&gt;<br>      &lt;bootp file='pxelinux.0'\/&gt;<br>    &lt;\/dhcp&gt;<br>  &lt;\/ip&gt;<br>  &lt;dnsmasq:options&gt;<br>    &lt;dnsmasq:option value='dhcp-vendorclass=set:efi-http,HTTPClient:Arch:00016'\/&gt;<br>    &lt;dnsmasq:option value='dhcp-option-force=tag:efi-http,60,HTTPClient'\/&gt;<br>    &lt;dnsmasq:option value='dhcp-boot=tag:efi-http,&amp;quot;http:\/\/192.168.122.1\/rhel8\/EFI\/BOOT\/BOOTX64.EFI&amp;quot;'\/&gt;<br>  &lt;\/dnsmasq:options&gt;<br>&lt;\/network&gt;<\/pre>\n\n\n<p>With the network config in place, a simple <code>sudo virsh net-destroy default &amp;&amp; sudo virsh net-start default<\/code> will load the new config. Next we need a web server. I&#8217;m simply running httpd on my system as you can see it&#8217;s defined in the above example as <code>192.168.122.1<\/code> . One of the major benefits of this setup is the web end point can be anywhere so please use whatever makes sense for your setup. For reasons I don&#8217;t understand <a href=\"https:\/\/silverblue.fedoraproject.org\/\">Silverblue<\/a> includes httpd; all I needed to do on my system was run <code>systemctl start httpd<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up the boot menu.<\/h2>\n\n\n\n<p>Since I&#8217;m working with the RHEL 8.4 Beta, all that&#8217;s necessary is to download the minimal boot.iso, mount it, and copy the contents to \/var\/www\/html\/rhel8\/. We won&#8217;t mount the ISO in this location as we need to tweak the GRUB config and will need to write the file. Next we need to tweak the kernel and initrd paths in the default grub config found under <code>\/var\/www\/html\/rhel8\/EFI\/BOOT\/grub.cfg<\/code> as this won&#8217;t support relative paths. For my setup it&#8217;s only necessary to add <code>\/rhel8<\/code> on each line. Pass any other option needed and you&#8217;re good to go. <\/p>\n\n\n<pre>menuentry 'Install Red Hat Enterprise Linux 8.4' --class fedora --class gnu-linux --class gnu --class os {<br>      linuxefi \/rhel8\/images\/pxeboot\/vmlinuz inst.stage2=http:\/\/192.168.122.1\/rhel8 inst.ks=http:\/\/192.168.122.1\/ks\/84.ks quiet<br>      initrdefi \/rhel8\/images\/pxeboot\/initrd.img<br>}<\/pre>\n\n\n<h2 class=\"wp-block-heading\">Enough already, let\u2019s boot!<\/h2>\n\n\n\n<p>Now we&#8217;re ready to create and boot a VM which leads to the annoying part. By default a network boot with OVMF will attempt an IPv4 PXE -&gt; IPv6 PXE -&gt; IPv4 HTTP -&gt; IPv6 HTTP in that order. It takes a long to let them fail so we&#8217;ll want to disrupt the standard boot process by quickly pressing the escape key repeatedly once the VM console comes up to manually select the IPv4 HTTP. In the screencast below I&#8217;m pressing the escape key quickly to select the correct option. This is less than ideal and I&#8217;m sure there&#8217;s a better way to either configure the firmware to disable the legacy option(s). Please let&nbsp;me&nbsp;know if you know how&nbsp;to&nbsp;do this.<\/p>\n\n\n<pre>sudo virt-install \\\n  --name=8.4-uefi-httpboot \\\n  --ram=2048 \\\n  --vcpus=1 \\\n  --os-type=linux \\\n  --os-variant=rhel8.4 \\\n  --graphics=vnc \\\n  --pxe \\\n  --disk=path=\/var\/home\/bbreard\/data\/distros\/uefi.qcow2 \\\n  --check path_in_use=off \\\n  --network=network=default,model=virtio \\\n  --boot=uefi<\/pre>\n\n\n<p><a><\/a><\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"872\" style=\"aspect-ratio: 1024 \/ 872;\" width=\"1024\" controls loop src=\"https:\/\/mrguitar.net\/wp-content\/uploads\/2021\/03\/84_httpinstall.mp4\"><\/video><\/figure>\n\n\n\n<p>That&#8217;s all there is to it! I hope you\u2019ll join me in a world that\u2019s free from the limitations of TFTP.<\/p>\n\n\n\n<p>References:<\/p>\n\n\n\n<p><a href=\"https:\/\/community.theforeman.org\/t\/enabling-httpboot-plugin-for-foreman-1-20\/13058\">https:\/\/community.theforeman.org\/t\/enabling-httpboot-plugin-for-foreman-1-20\/13058<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/en.opensuse.org\/UEFI_HTTPBoot_Server_Setup\">https:\/\/en.opensuse.org\/UEFI_HTTPBoot_Server_Setup<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/bitbin.de\/blog\/2019\/04\/uefi_http_boot\/\">https:\/\/bitbin.de\/blog\/2019\/04\/uefi_http_boot\/<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/lists.thekelleys.org.uk\/pipermail\/dnsmasq-discuss\/2018q3\/012451.html\">https:\/\/lists.thekelleys.org.uk\/pipermail\/dnsmasq-discuss\/2018q3\/012451.html<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/thekelleys.org.uk\/dnsmasq\/docs\/dnsmasq-man.html\">https:\/\/thekelleys.org.uk\/dnsmasq\/docs\/dnsmasq-man.html<\/a><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Background I\u2019ve been a big proponent of network based provisioning pretty much my entire career. My second job out of college involved imaging ~800 computers multiple times a week. When I was hired, my predecessors used floppy disks to load a small OS, matching NIC driver, and imaging client (remember Ghost?!). The bottom line was &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/mrguitar.net\/?p=2300\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;UEFI HTTP Boot with Libvirt&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[6],"tags":[113,114,112],"class_list":["post-2300","post","type-post","status-publish","format-standard","hentry","category-open-sourcenerd-stuff","tag-http-boot","tag-pxe","tag-uefi"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mrguitar.net\/index.php?rest_route=\/wp\/v2\/posts\/2300","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mrguitar.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mrguitar.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mrguitar.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mrguitar.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2300"}],"version-history":[{"count":14,"href":"https:\/\/mrguitar.net\/index.php?rest_route=\/wp\/v2\/posts\/2300\/revisions"}],"predecessor-version":[{"id":2322,"href":"https:\/\/mrguitar.net\/index.php?rest_route=\/wp\/v2\/posts\/2300\/revisions\/2322"}],"wp:attachment":[{"href":"https:\/\/mrguitar.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2300"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mrguitar.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2300"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mrguitar.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2300"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}