Webapp: iPXE page stages the dual-signed shim, knows snponly and per-MAC folders
Staging fetches ipxe-shimx64.efi from the ipxe/shim release (sha256 from GitHub), and uses it instead of the tarball's 2011-only shim when it is signed under the Microsoft UEFI CA 2023 and trusts the same iPXE CA; otherwise the tarball shim stays, with the reason shown. snponly.efi and snponly-shim.efi are managed alongside ipxe.efi and ipxe-shim.efi, each iPXE build says whether it drives USB itself (kills USB keyboards), and tftp-unique-root per-MAC folders are listed read-only.
This commit is contained in:
@@ -37,6 +37,8 @@
|
||||
{% endif %}
|
||||
{% if info.trusts %}<div class="sub">Starts second stages signed by <span class="mono">{{ info.trusts }}</span></div>{% endif %}
|
||||
{% if info.sbat %}<div class="sub">SBAT: <span class="mono">{{ info.sbat }}</span></div>{% endif %}
|
||||
{% if info.drivers == 'native' %}<div class="notice"><span>This iPXE build drives USB itself: it takes the USB controller from the firmware, so a USB keyboard stops working at the iPXE menu. <span class="mono">snponly.efi</span> uses the firmware's drivers instead.</span></div>
|
||||
{% elif info.drivers == 'firmware' %}<div class="sub">Uses the firmware's network and USB drivers, so USB keyboards keep working.</div>{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% block extra_head %}
|
||||
@@ -162,11 +164,14 @@
|
||||
<details>
|
||||
<summary class="disc"><span class="chev" aria-hidden="true">▾</span>What this page manages</summary>
|
||||
<p class="sub" style="margin: 4px 0 0; max-width: 900px; line-height: 1.6">
|
||||
The Secure Boot chain that dnsmasq hands out over TFTP: the Microsoft-signed shim (<span class="mono">ipxe-shim.efi</span>)
|
||||
starts <span class="mono">ipxe.efi</span>, which is signed by the iPXE Secure Boot CA. Updates come from the upstream
|
||||
iPXE release tarball on GitHub; its sha256 is checked against GitHub's published digest and each file's signer chain is
|
||||
read with <span class="mono">sbverify</span> before it can be installed. <span class="mono">ipxe-bcm.efi</span>,
|
||||
<span class="mono">dnsmasq.conf</span> and the Blancco files are never changed here.
|
||||
The Secure Boot chain that dnsmasq hands out over TFTP: the Microsoft-signed shim (<span class="mono">ipxe-shim.efi</span>,
|
||||
and the same file as <span class="mono">snponly-shim.efi</span>) starts <span class="mono">ipxe.efi</span>, which is signed by the
|
||||
iPXE Secure Boot CA. <span class="mono">snponly.efi</span> is the same release built on the firmware's drivers (USB keyboards
|
||||
keep working). Updates come from the upstream iPXE release tarball on GitHub, plus the dual-signed shim (Microsoft UEFI CA
|
||||
2011 and 2023) from the separate ipxe/shim release; each download's sha256 is checked against GitHub's published digest and
|
||||
each file's signer chain is read with <span class="mono">sbverify</span> before it can be installed.
|
||||
<span class="mono">ipxe-bcm.efi</span>, the per-client folders, <span class="mono">dnsmasq.conf</span> and the Blancco files are
|
||||
never changed here.
|
||||
</p>
|
||||
</details>
|
||||
|
||||
@@ -203,6 +208,42 @@
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
{# ---- Per-client folders ---------------------------------------------------- #}
|
||||
{% if mac_folders %}
|
||||
<section class="panel ui-list" aria-labelledby="mac-h">
|
||||
<div class="sec-h"><div><h2 id="mac-h" class="sec">Per-client folders</h2>
|
||||
<div class="sub">With <span class="mono">tftp-unique-root=mac</span>, a client whose MAC has a folder gets every file from it, with no fallback to the root. Read-only here: installs do not update them.</div></div></div>
|
||||
{% for m in mac_folders %}
|
||||
<div class="rel-h"><strong class="mono">{{ m.mac }}</strong><span class="sub mono">/srv/tftp/{{ m.dir }}/</span>
|
||||
<span class="sub">{{ m.files | length }} file{{ 's' if m.files | length != 1 }}</span></div>
|
||||
{% for f in m.files %}
|
||||
{% set i = f.info %}
|
||||
{% set chain_ok = i.chain_ok if i.chain_ok is defined else none %}
|
||||
<div class="ui-row i-row">
|
||||
<details>
|
||||
<summary class="igrid">
|
||||
<span class="name"><span class="mono">{{ f.name }}</span>{% if i.drivers == 'firmware' and f.name == 'ipxe.efi' %}<span class="sub">snponly build</span>{% endif %}</span>
|
||||
<span class="tip c-ver" data-tip="{{ i.version or '-' }}"><span class="trunc mono">{{ i.version or '-' }}</span></span>
|
||||
<span>{% if chain_ok == false %}{{ ui.chip('Shim will refuse', 'err') }}{% else %}{{ sig_chip(i) }}{% endif %}</span>
|
||||
<span class="chev" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
<div class="ibody">
|
||||
{{ sig_text(i, chain_ok) }}
|
||||
<dl class="kv">
|
||||
<dt>Path</dt><dd class="mono">/srv/tftp/{{ m.dir }}/{{ f.name }}</dd>
|
||||
<dt>Size</dt><dd>{{ "{:,}".format(i.size) }} B</dd>
|
||||
<dt>Modified</dt><dd class="mono">{{ i.modified | timestamp_fmt }}</dd>
|
||||
<dt>sha256</dt><dd class="mono">{{ i.sha256 }}</dd>
|
||||
</dl>
|
||||
{{ chain(i) }}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{# ---- Upstream ------------------------------------------------------------ #}
|
||||
<section class="panel ui-list" aria-labelledby="up-h">
|
||||
<div class="sec-h"><div><h2 id="up-h" class="sec">Upstream release</h2>
|
||||
@@ -240,7 +281,7 @@
|
||||
{% if staged %}
|
||||
<section class="panel ui-list" aria-labelledby="stg-h">
|
||||
<div class="sec-h"><div><h2 id="stg-h" class="sec">Downloaded and verified</h2>
|
||||
<div class="sub">Tick the files to install. ipxe-shim.efi is only installed together with ipxe.efi from the same release; ipxe.pxe is for legacy BIOS clients.</div></div></div>
|
||||
<div class="sub">Tick the files to install. A shim is only installed together with ipxe.efi from the same release; ipxe.pxe is for legacy BIOS clients.</div></div></div>
|
||||
{% for s in staged %}
|
||||
<form method="post" action="{{ url_for('ipxe.install') }}" data-tag="{{ s.tag }}" style="margin: 0"
|
||||
onsubmit="var n=[].slice.call(this.querySelectorAll('input[name=names]:checked')).map(function(c){return c.value;}); if(!n.length){alert('Select at least one file.');return false;} return confirm('Install ' + n.join(', ') + ' from ' + this.dataset.tag + ' into the TFTP root? The current files are backed up first and every client that boots from now on gets the new ones.');">
|
||||
@@ -266,8 +307,9 @@
|
||||
</summary>
|
||||
<div class="ibody picked">
|
||||
{% if not f.ok %}<div class="errbox">{{ f.error }}</div>{% elif f.kind %}{{ sig_text(f) }}{% endif %}
|
||||
{% if f.note %}<div class="notice">{{ f.note }}</div>{% endif %}
|
||||
<dl class="kv">
|
||||
<dt>From tarball</dt><dd class="mono">{{ f.source or '-' }}</dd>
|
||||
<dt>Source</dt><dd class="mono">{{ f.source or '-' }}</dd>
|
||||
{% if f.sha256 %}<dt>sha256</dt><dd class="mono">{{ f.sha256 }}</dd>{% endif %}
|
||||
</dl>
|
||||
{% if f.signers is defined %}{{ chain(f) }}{% endif %}
|
||||
@@ -288,6 +330,7 @@
|
||||
<section class="panel ui-list" aria-labelledby="rt-h">
|
||||
<div class="sec-h"><div><h2 id="rt-h" class="sec">Who gets which file</h2>
|
||||
<div class="sub">Read from the dnsmasq configuration; never changed here.</div></div></div>
|
||||
{% if routing.unique_root %}<div class="notice info" style="margin: 0 24px 10px"><span>dnsmasq has <span class="mono">tftp-unique-root=mac</span>: a client with its own folder (see Per-client folders) is served from it instead of the root, whatever file name this list shows.</span></div>{% endif %}
|
||||
{% if routing.boots %}
|
||||
{% for b in routing.boots %}
|
||||
<div class="ui-row">
|
||||
@@ -375,11 +418,14 @@
|
||||
<li><strong>Find the release.</strong> The latest release is at
|
||||
<a href="{{ releases_page }}" target="_blank" rel="noopener" class="mono">{{ releases_page }}</a>. The signed boot files are the
|
||||
asset <span class="mono">ipxeboot.tar.gz</span>; GitHub shows its sha256 next to the asset (or ask the API:
|
||||
<span class="mono">curl -s https://api.github.com/repos/ipxe/ipxe/releases/latest | grep -B2 -A2 '"digest"'</span>).</li>
|
||||
<span class="mono">curl -s https://api.github.com/repos/ipxe/ipxe/releases/latest | grep -B2 -A2 '"digest"'</span>).
|
||||
The tarball's shim is signed by Microsoft's 2011 CA only; the dual-signed shim (2011 and 2023) is the asset
|
||||
<span class="mono">ipxe-shimx64.efi</span> of the separate <a href="https://github.com/ipxe/shim/releases" target="_blank" rel="noopener" class="mono">ipxe/shim</a> release.</li>
|
||||
<li><strong>Download and check the sha256.</strong> It must equal the digest on the release page.
|
||||
<pre class="cmd mono">cd /tmp
|
||||
curl -fLO {{ tarball_url }}
|
||||
sha256sum ipxeboot.tar.gz</pre>
|
||||
curl -fLO https://github.com/ipxe/shim/releases/latest/download/ipxe-shimx64.efi
|
||||
sha256sum ipxeboot.tar.gz ipxe-shimx64.efi</pre>
|
||||
<span class="sub">GitHub goes through Zscaler. A certificate error from curl means the Zscaler root CA is missing from
|
||||
<span class="mono">/usr/local/share/ca-certificates/</span> (the playbook installs it).</span></li>
|
||||
<li><strong>Unpack.</strong> The Secure Boot files are in <span class="mono">ipxeboot/x86_64-sb/</span>:
|
||||
@@ -389,27 +435,34 @@ sha256sum ipxeboot.tar.gz</pre>
|
||||
<pre class="cmd mono">tar xzf ipxeboot.tar.gz
|
||||
ls -l ipxeboot/x86_64-sb/</pre></li>
|
||||
<li><strong>Check the signatures.</strong>
|
||||
<pre class="cmd mono">sbverify --list ipxeboot/x86_64-sb/shimx64.efi
|
||||
sbverify --list ipxeboot/x86_64-sb/ipxe.efi</pre>
|
||||
The shim must be issued by <span class="mono">CN=Microsoft Corporation UEFI CA 2011</span> or, on a newer build,
|
||||
<span class="mono">CN=Microsoft UEFI CA 2023</span> (prefer a 2023-signed shim when upstream ships one).
|
||||
<pre class="cmd mono">sbverify --list ipxe-shimx64.efi
|
||||
sbverify --list ipxeboot/x86_64-sb/ipxe.efi
|
||||
sbverify --list ipxeboot/x86_64-sb/snponly.efi</pre>
|
||||
The dual-signed shim shows <span class="mono">CN=Microsoft UEFI CA 2023</span> (sbverify lists the first signature only;
|
||||
the 2011 one is nested inside it). Fall back to the tarball's <span class="mono">shimx64.efi</span>
|
||||
(<span class="mono">CN=Microsoft Corporation UEFI CA 2011</span>) only if the dual-signed one is unavailable.
|
||||
<span class="mono">ipxe.efi</span> must show <span class="mono">CN=iPXE Secure Boot Intermediate ...</span> issued by
|
||||
<span class="mono">CN=iPXE Secure Boot CA</span>. The shim's <span class="mono">warning: data remaining ... gaps between PE/COFF sections?</span>
|
||||
is normal. Do not rely on <span class="mono">sbverify --cert</span>: it reports OK even for the wrong certificate.</li>
|
||||
<li><strong>Back up the current files.</strong> A backup made in this folder also shows in the Backups list above, with Restore.
|
||||
<pre class="cmd mono">B=/var/lib/pxe-webapp/updates/ipxe/backups/$(date +%Y%m%d-%H%M%S)
|
||||
mkdir -p "$B" && cp -p /srv/tftp/ipxe-shim.efi /srv/tftp/ipxe.efi "$B"/</pre></li>
|
||||
mkdir -p "$B" && cp -p /srv/tftp/ipxe-shim.efi /srv/tftp/snponly-shim.efi /srv/tftp/ipxe.efi /srv/tftp/snponly.efi "$B"/</pre></li>
|
||||
<li><strong>Install both files, from the same release.</strong> The shim only starts an <span class="mono">ipxe.efi</span> signed under the
|
||||
CA it carries, and it always loads <span class="mono">ipxe.efi</span> from the TFTP root, so the name must stay
|
||||
<span class="mono">ipxe.efi</span>. Copy to a temporary name and rename, so a client that is booting never reads half a file:
|
||||
<pre class="cmd mono">install -m 0644 ipxeboot/x86_64-sb/ipxe.efi /srv/tftp/.ipxe.efi.new && mv -f /srv/tftp/.ipxe.efi.new /srv/tftp/ipxe.efi
|
||||
install -m 0644 ipxeboot/x86_64-sb/shimx64.efi /srv/tftp/.ipxe-shim.efi.new && mv -f /srv/tftp/.ipxe-shim.efi.new /srv/tftp/ipxe-shim.efi</pre>
|
||||
<span class="sub">No dnsmasq restart is needed. Never replace <span class="mono">ipxe-bcm.efi</span>; it serves every other UEFI client.</span></li>
|
||||
<pre class="cmd mono">install -m 0644 ipxeboot/x86_64-sb/ipxe.efi /srv/tftp/.ipxe.efi.new && mv -f /srv/tftp/.ipxe.efi.new /srv/tftp/ipxe.efi
|
||||
install -m 0644 ipxeboot/x86_64-sb/snponly.efi /srv/tftp/.snponly.efi.new && mv -f /srv/tftp/.snponly.efi.new /srv/tftp/snponly.efi
|
||||
install -m 0644 ipxe-shimx64.efi /srv/tftp/.ipxe-shim.efi.new && mv -f /srv/tftp/.ipxe-shim.efi.new /srv/tftp/ipxe-shim.efi
|
||||
install -m 0644 ipxe-shimx64.efi /srv/tftp/.snponly-shim.efi.new && mv -f /srv/tftp/.snponly-shim.efi.new /srv/tftp/snponly-shim.efi</pre>
|
||||
<span class="sub">No dnsmasq restart is needed. Never replace <span class="mono">ipxe-bcm.efi</span>; it serves every other UEFI client.
|
||||
A per-client folder keeps its own copies: update its <span class="mono">ipxe.efi</span> (the snponly build) and shim the same way.</span></li>
|
||||
<li><strong>Who gets it.</strong> dnsmasq decides, in <span class="mono">/etc/dnsmasq.conf</span>: clients listed as
|
||||
<span class="mono">dhcp-host=<mac>,set:ipxe2</span> get the shim through
|
||||
<span class="mono">dhcp-boot=tag:ipxe2,tag:!ipxe,tag:!bios,ipxe-shim.efi</span>; every other UEFI client gets
|
||||
<span class="mono">ipxe-bcm.efi</span> through <span class="mono">dhcp-boot=tag:!bios,tag:!ipxe2,ipxe-bcm.efi</span>. Who gets which file
|
||||
above shows the live lines. Test on one tagged machine before tagging more.</li>
|
||||
<span class="mono">ipxe-bcm.efi</span> through the untagged <span class="mono">dhcp-boot=tag:!bios,...,ipxe-bcm.efi</span> line. Clients tagged
|
||||
<span class="mono">ipxesnp</span> get <span class="mono">snponly-shim.efi</span>; since the shim asks for <span class="mono">ipxe.efi</span>
|
||||
whatever it is called, each of them also needs a per-client folder whose <span class="mono">ipxe.efi</span> is the snponly build.
|
||||
Who gets which file above shows the live lines. Test on one tagged machine before tagging more.</li>
|
||||
<li><strong>Roll back.</strong> Use Restore in the Backups list, or copy the backed-up files back the same way (temporary name, then
|
||||
<span class="mono">mv -f</span>).</li>
|
||||
<li><strong>Normal log noise.</strong> While booting, the shim asks TFTP for optional files such as
|
||||
@@ -433,7 +486,7 @@ install -m 0644 ipxeboot/x86_64-sb/shimx64.efi /srv/tftp/.ipxe-shim.efi.new &
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (j) {
|
||||
if (j.state !== 'queued' && j.state !== 'running') { location.reload(); return; }
|
||||
var step = j.step === 'download' ? 'Downloading' : j.step === 'verify' ? 'Verifying' : 'Starting';
|
||||
var step = j.step === 'download' ? 'Downloading' : j.step === 'verify' ? 'Verifying' : j.step === 'shim' ? 'Fetching the dual-signed shim for' : 'Starting';
|
||||
if (text) text.textContent = step + ' ' + (j.tag || '') + (j.step === 'download' && j.percent != null ? ' (' + j.percent + '%)' : '') + '...';
|
||||
if (bar) bar.style.width = (j.step === 'verify' ? 100 : (j.percent || 0)) + '%';
|
||||
setTimeout(tick, 2000);
|
||||
|
||||
Reference in New Issue
Block a user