<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Pxe on Carlos Vaz</title>
    <link>https://carlosvaz.com/tags/pxe/</link>
    <description>Recent content in Pxe on Carlos Vaz</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-US</language>
    <managingEditor>carlos@carjorvaz.com (Carlos Vaz)</managingEditor>
    <webMaster>carlos@carjorvaz.com (Carlos Vaz)</webMaster>
    <lastBuildDate>Mon, 07 Aug 2023 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://carlosvaz.com/tags/pxe/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>iPXE booting with NixOS</title>
      <link>https://carlosvaz.com/posts/ipxe-booting-with-nixos/</link>
      <pubDate>Mon, 07 Aug 2023 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/ipxe-booting-with-nixos/</guid>
      <description>&lt;p&gt;When you only manage a few systems, it is usually enough to install NixOS on them using a live installer on a USB drive.&#xA;When dealing with almost a hundred systems, however, using USB drives becomes unfeasable and solutions like PXE booting become necessary.&lt;/p&gt;&#xA;&lt;p&gt;Thankfully, Pixiecore makes it easy to set up an iPXE server on NixOS, a task that could otherwise be quite complex.&lt;/p&gt;&#xA;&lt;p&gt;The simplest iPXE server we can set up on NixOS can look like the following:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>When you only manage a few systems, it is usually enough to install NixOS on them using a live installer on a USB drive.
When dealing with almost a hundred systems, however, using USB drives becomes unfeasable and solutions like PXE booting become necessary.</p>
<p>Thankfully, Pixiecore makes it easy to set up an iPXE server on NixOS, a task that could otherwise be quite complex.</p>
<p>The simplest iPXE server we can set up on NixOS can look like the following:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  services<span style="color:#f92672">.</span>pixiecore <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    dhcpNoBind <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    kernel <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://boot.netboot.xyz&#34;</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>In this configuration, we enable the Pixiecore service, set it to work along the DHCP server that already exists on our network and to boot the <a href="https://netboot.xyz">netboot.xyz</a> iPXE image, a common option which gives you a selection of live installers on boot.</p>
<p>This may be enough for your environment but, as we&rsquo;re running NixOS, we&rsquo;d like to an image customized to our needs, having SSH access pre-configured, for example:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> inputs<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span>
</span></span><span style="display:flex;"><span>  sys <span style="color:#f92672">=</span> inputs<span style="color:#f92672">.</span>nixos<span style="color:#f92672">.</span>lib<span style="color:#f92672">.</span>nixosSystem {
</span></span><span style="display:flex;"><span>    system <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;x86_64-linux&#34;</span>;
</span></span><span style="display:flex;"><span>    modules <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>      ({ config<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> modulesPath<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }: {
</span></span><span style="display:flex;"><span>        imports <span style="color:#f92672">=</span> [ (modulesPath <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;/installer/netboot/netboot-minimal.nix&#34;</span>) ];
</span></span><span style="display:flex;"><span>        config <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>          services<span style="color:#f92672">.</span>openssh <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>            enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>            openFirewall <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            settings <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>              PasswordAuthentication <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>              KbdInteractiveAuthentication <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>          };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          users<span style="color:#f92672">.</span>users<span style="color:#f92672">.</span>root<span style="color:#f92672">.</span>openssh<span style="color:#f92672">.</span>authorizedKeys<span style="color:#f92672">.</span>keys <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...&#34;</span>
</span></span><span style="display:flex;"><span>          ];
</span></span><span style="display:flex;"><span>        };
</span></span><span style="display:flex;"><span>      })
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  build <span style="color:#f92672">=</span> sys<span style="color:#f92672">.</span>config<span style="color:#f92672">.</span>system<span style="color:#f92672">.</span>build;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">in</span> {
</span></span><span style="display:flex;"><span>  services<span style="color:#f92672">.</span>pixiecore <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    openFirewall <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    dhcpNoBind <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>; <span style="color:#75715e"># Use existing DHCP server.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    mode <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;boot&#34;</span>;
</span></span><span style="display:flex;"><span>    kernel <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>build<span style="color:#f92672">.</span>kernel<span style="color:#e6db74">}</span><span style="color:#e6db74">/bzImage&#34;</span>;
</span></span><span style="display:flex;"><span>    initrd <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>build<span style="color:#f92672">.</span>netbootRamdisk<span style="color:#e6db74">}</span><span style="color:#e6db74">/initrd&#34;</span>;
</span></span><span style="display:flex;"><span>    cmdLine <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;init=</span><span style="color:#e6db74">${</span>build<span style="color:#f92672">.</span>toplevel<span style="color:#e6db74">}</span><span style="color:#e6db74">/init loglevel=4&#34;</span>;
</span></span><span style="display:flex;"><span>    debug <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>The previous example does exactly that.</p>
<p>This now becomes very powerful and we&rsquo;ve just starting using this on the lab computers we manage.
We set the boot order to boot first from the hard disk and, if it&rsquo;s not bootable, it will automatically boot from iPXE, still allowing remote access from the administrators and allowing us, if necessary, to redeploy its configuration with <a href="https://github.com/numtide/nixos-anywhere">nixos-anywhere</a>, as I&rsquo;ll explain in a future blog post.</p>
<p>But we also want to be able to trigger an iPXE boot remotely, even if the current hard disk has a bootable configuration.</p>
<p>Initially, it wasn&rsquo;t very obvious how we should be able to do this.
One possibility would be to use <code>efibootmgr</code> to change the boot order accordingly, but it would make it more difficult to return to booting from local hard disk after the redeploy.</p>
<p>After some reflection, we settled on creating a special GRUB entry that boots from iPXE.</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  boot<span style="color:#f92672">.</span>loader <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    efi<span style="color:#f92672">.</span>canTouchEfiVariables <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    grub <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      efiSupport <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;nodev&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      extraFiles <span style="color:#f92672">=</span> { <span style="color:#e6db74">&#34;ipxe.efi&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>ipxe<span style="color:#e6db74">}</span><span style="color:#e6db74">/ipxe.efi&#34;</span>; };
</span></span><span style="display:flex;"><span>      extraEntries <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        menuentry &#34;Reinstall via iPXE&#34; {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          chainloader /ipxe.efi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>Presumably, this only works on UEFI systems.</p>
<p>Finding out how to create this entry wasn&rsquo;t trivial at all, I couldn&rsquo;t find anyone else doing it this way,  but it seems to work perfectly fine.</p>
<p>Having this boot entry on its own isn&rsquo;t much different from choosing a different one-time boot option, so I&rsquo;ll look into using the <code>grub-reboot</code> tool to be able to trigger it remotely, although I haven&rsquo;t tested that yet on NixOS.</p>
<p><em>2023/08/24 Update</em>: I can confirm that <code>grub-reboot</code> works as expected on NixOS.</p>
<p>For example, you can get the available entries on your system like so:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">[root@nixos:~]# grep menuentry /boot/grub/grub.cfg
menuentry &#34;NixOS - Default&#34; --class nixos {
menuentry &#34;Windows 10&#34; {
menuentry &#34;iPXE Boot&#34; {
menuentry &#34;NixOS - Configuration 19 (2023-08-24 - 23.05.20230820.475d5ae)&#34; --class nixos {</code></pre><p>From this output, we&rsquo;d run <code>grub-reboot 2</code> to boot to iPXE on next reboot.</p>
<p>There&rsquo;s also a more complicated approach, where the iPXE boot entry won&rsquo;t be visible by default, only when you trigger it remotely.</p>
<p>To do this, we use the following snippet instead:</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  environment<span style="color:#f92672">.</span>shellAliases<span style="color:#f92672">.</span>reboot2PXE <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>grub2<span style="color:#e6db74">}</span><span style="color:#e6db74">/bin/grub-editenv /boot/grub/grubenv set entry=ipxe &amp;&amp; reboot&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  boot<span style="color:#f92672">.</span>loader <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    efi<span style="color:#f92672">.</span>canTouchEfiVariables <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    grub <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      efiSupport <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      device <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;nodev&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      extraFiles <span style="color:#f92672">=</span> { <span style="color:#e6db74">&#34;ipxe.efi&#34;</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>pkgs<span style="color:#f92672">.</span>ipxe<span style="color:#e6db74">}</span><span style="color:#e6db74">/ipxe.efi&#34;</span>; };
</span></span><span style="display:flex;"><span>      extraConfig <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        if [ &#34;</span><span style="color:#ae81ff">&#39;&#39;$</span><span style="color:#e6db74">{entry}&#34; = &#34;ipxe&#34; ]; then
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          set entry=&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          save_env --file /grub/grubenv entry
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          menuentry &#34;Reinstall via iPXE&#34; {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            chainloader /ipxe.efi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        fi
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      &#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>Now, if we call <code>reboot2PXE</code>, the system will immediately reboot to iPXE, while not showing the iPXE boot entry on regular boots.</p>
<h2 id="closing-remarks">Closing remarks</h2>
<p>As you can see by these snippets, setting up an iPXE server doesn&rsquo;t have to be a chore.</p>
<p>As basically every computer built in the last 10 years has proper iPXE booting support, when managing tens of bare metal machines, setups like these drastically make the sysadmins&rsquo; lives easier.</p>
<p>Although I believe it would still be technically possible to remotely configure and recover these systems using lower level tools like AMT, I feel this would create a more complicated setup.
I&rsquo;m still looking to experiment with <a href="https://meshcentral.com">MeshCentral</a> to explore these lower level AMT capibilities, and that may become a future blog post.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/danderson/netboot/tree/main/pixiecore">https://github.com/danderson/netboot/tree/main/pixiecore</a></li>
<li><a href="https://nixos.wiki/wiki/Netboot">https://nixos.wiki/wiki/Netboot</a></li>
</ul>
]]></content:encoded>
    </item>
  </channel>
</rss>
