webapp: fix review findings (imaging types, NIC status, iPXE gates)
- imaging: coerce client payload and stored session fields to str/int, drop lists/dicts; tiles template guards pctype/current_stage; POST rejects non-object bodies and non-string serials. - nic_status: normalise every check-file entry; load nic.conf as-is with a [nic] fallback; split interface= lists; keep the check's binding while fresh, resolve wildcard sockets from the dnsmasq pin (never True for an excluded NIC); mark vanished NICs down; include role bridges; stale or future-dated files downgrade to warnings and drop evidence. - Overview renders the network pill in its own try and shows 'Network: unknown' when status fails. - ipxe_update: read every Authenticode signature (all WIN_CERTIFICATE entries plus nested SPC_NESTED_SIGNATURE) with openssl; shim swap and install need both Microsoft UEFI CA 2011 and 2023; install re-reads each file (snponly ipxe.efi only); staged.json schema version, older records shown stale and refused; uhci counts as native USB. - backups describe their files; risky rollbacks need a confirm flag. - mac_folders: guarded listdir, cached describe, configured TFTP root, warning for a shim without ipxe.efi. - evidence pack: years 2000..2100, 400 days inclusive. - a11y role=group on quick ranges; progress bar stays full during 'shim'. - tests/test_review_fixes.py: 29 unittest cases incl. both real shims.
This commit is contained in:
@@ -60,18 +60,22 @@
|
||||
{% set track_kind = {'failed': 'fail', 'action': 'act', 'running': 'run', 'done': 'ok'} %}
|
||||
|
||||
{% macro floor_row(s) %}
|
||||
{# Sessions are sanitised on load; these guards keep a row rendering if a
|
||||
non-string still slips through (a lookup or `in` on a list raises). #}
|
||||
{% set pctype = s.pctype if s.pctype is string else '' %}
|
||||
{% set cur_stage = s.current_stage if s.current_stage is string else '' %}
|
||||
{% set is_inferred = (s.source == 'inferred') %}
|
||||
{% set is_blancco = (s.source == 'blancco' or s.pctype == 'blancco') %}
|
||||
{% set is_blancco = (s.source == 'blancco' or pctype == 'blancco') %}
|
||||
{% set stage_idx = (s.stage_index or 0) | int(0) %}
|
||||
{% set stage_total = (s.stage_total or 0) | int(0) %}
|
||||
{% set state = s.floor_state or 'running' %}
|
||||
{% set state = s.floor_state if s.floor_state in state_chip else 'running' %}
|
||||
{% set drive_pct = None %}
|
||||
{% if is_blancco and stage_idx == 4 and s.current_stage and '%]' in s.current_stage %}
|
||||
{% set drive_pct = s.current_stage.split('[')[-1].split('%')[0] | int(0) %}
|
||||
{% if is_blancco and stage_idx == 4 and cur_stage and '%]' in cur_stage %}
|
||||
{% set drive_pct = cur_stage.split('[')[-1].split('%')[0] | int(0) %}
|
||||
{% endif %}
|
||||
{% set warn = False %}
|
||||
{% if is_inferred %}
|
||||
{% set friendly = inferred_stage_labels.get(s.current_stage, ('Inferred from logs', s.current_stage or '')) %}
|
||||
{% set friendly = inferred_stage_labels.get(cur_stage, ('Inferred from logs', cur_stage)) %}
|
||||
{% elif is_blancco %}
|
||||
{% if s.status == 'failed' %}
|
||||
{% set friendly = ('Erasure FAILED', 'Blancco reported a failed erasure step. Check the log tail and the report.') %}
|
||||
@@ -80,18 +84,18 @@
|
||||
{% endif %}
|
||||
{% if stage_idx == 4 and s.status != 'failed' %}{% set warn = True %}{% endif %}
|
||||
{% else %}
|
||||
{% set friendly = (desk_stage_labels if s.pctype in desk_kind else stage_labels).get(stage_idx, ('Stage ' ~ stage_idx, '')) %}
|
||||
{% if stage_idx == 1 and s.current_stage and 'bios update staged' in s.current_stage|lower %}
|
||||
{% set friendly = (desk_stage_labels if pctype in desk_kind else stage_labels).get(stage_idx, ('Stage ' ~ stage_idx, '')) %}
|
||||
{% if stage_idx == 1 and cur_stage and 'bios update staged' in cur_stage|lower %}
|
||||
{% set friendly = ('Applying image - BIOS update staged',
|
||||
'WinPE staged a firmware update for this model. It flashes during the reboot after the image is applied: do NOT power off then.') %}
|
||||
{% set warn = True %}
|
||||
{% elif stage_idx == 1 and s.current_stage and 'bios' in s.current_stage|lower %}
|
||||
{% elif stage_idx == 1 and cur_stage and 'bios' in cur_stage|lower %}
|
||||
{% set friendly = ('Updating BIOS: do not power off',
|
||||
'WinPE detected a firmware update for this model. Do NOT power off until the next reboot. Imaging continues afterward.') %}
|
||||
{% set warn = True %}
|
||||
{% endif %}
|
||||
{% if stage_idx == 7 and s.current_stage %}
|
||||
{% set _cs = s.current_stage|lower %}
|
||||
{% if stage_idx == 7 and cur_stage %}
|
||||
{% set _cs = cur_stage|lower %}
|
||||
{% if 'ready for lockdown' in _cs or 'request lockdown' in _cs %}
|
||||
{% set friendly = ('Ready - request lockdown',
|
||||
'Phase 1-4 all complete (Registration, Device Config, Software Deploy, Credentials). Click "ARTS request" to initiate the lockdown workflow.') %}
|
||||
@@ -112,11 +116,11 @@
|
||||
{% if s.hostname_target and s.serial %}{% set _ = ident.append(s.hostname_target) %}{% endif %}
|
||||
{% if s.ip %}{% set _ = ident.append(s.ip) %}{% endif %}
|
||||
{% set title = s.serial or s.hostname_target or s.mac or s.ip or '(unknown bay)' %}
|
||||
{% set kind = 'Blancco wipe' if is_blancco else desk_kind.get(s.pctype, s.pctype or '') %}
|
||||
{% set kind = 'Blancco wipe' if is_blancco else desk_kind.get(pctype, pctype) %}
|
||||
<div class="ui-row floor-row imaging-card" data-state="{{ state }}"
|
||||
data-serial="{{ s.serial or '' }}" data-key="{{ card_key }}" data-mac="{{ s.mac or '' }}"
|
||||
data-ip="{{ s.ip or '' }}" data-source="{{ s.source or 'client' }}"
|
||||
data-filter="{{ (s.serial or '')|lower }} {{ (s.hostname_target or '')|lower }} {{ (s.pctype or '')|lower }} {{ (s.machinenumber or '')|lower }} {{ (s.intune_device_id or '')|lower }} {{ friendly[0]|lower }} stage-{{ stage_idx }} {{ (s.status or 'in_progress')|lower }} {{ (s.mac or '')|lower }} {{ (s.ip or '') }} {{ s.source or 'client' }}">
|
||||
data-filter="{{ (s.serial or '')|lower }} {{ (s.hostname_target or '')|lower }} {{ pctype|lower }} {{ (s.machinenumber or '')|lower }} {{ (s.intune_device_id or '')|lower }} {{ friendly[0]|lower }} stage-{{ stage_idx }} {{ (s.status or 'in_progress')|lower }} {{ (s.mac or '')|lower }} {{ (s.ip or '') }} {{ s.source or 'client' }}">
|
||||
<details class="imaging-toggle">
|
||||
<summary class="frow">
|
||||
<span class="g-st">{{ ui.chip(state_chip[state][0], state_chip[state][1]) }}</span>
|
||||
|
||||
50
webapp/templates/_nic_status.html
Normal file
50
webapp/templates/_nic_status.html
Normal file
@@ -0,0 +1,50 @@
|
||||
{# Overview network pill and its problem notice. Rendered on its own by
|
||||
the dashboard view (part = 'pill' or 'notice') inside a try, so a broken
|
||||
status file can never take the Overview down. #}
|
||||
{% import '_ui.html' as ui %}
|
||||
{% if part == 'pill' %}
|
||||
{% set nic_dot = {'ok': 'var(--ok)', 'warn': 'var(--act)', 'bad': 'var(--fail)'}.get(nic.state, 'var(--off-edge)') %}
|
||||
{% set role_name = {'imaging': 'Imaging', 'ge': 'GE network', 'other': 'Other'} %}
|
||||
{% set verdict_chip = {'isolated': ('Isolated', 'ok'), 'ge': ('GE network', 'neutral'), 'foreign-dhcp': ('Other DHCP server', 'err'),
|
||||
'no-link': ('No link', 'off'), 'error': ('Check failed', 'warn')} %}
|
||||
<details class="more">
|
||||
<summary class="fbtn" style="background: var(--surface); min-height: 36px">
|
||||
<span class="dot" style="background: {{ nic_dot }}"></span>
|
||||
{{ {'ok': 'Network OK', 'warn': 'Network: check', 'bad': 'Network: DANGER'}.get(nic.state, 'Network: unknown') }}
|
||||
<span class="chev" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
<div class="menu" style="width: min(460px, calc(100vw - 32px)); padding: 12px 14px; gap: 10px; font-size: 13px">
|
||||
{% for i in nic.interfaces %}
|
||||
{% set bad_here = (i.role == 'imaging' and i.verdict in ('ge', 'foreign-dhcp')) or (i.role != 'imaging' and i.dnsmasq_bound) %}
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<span class="dot" style="background: {{ 'var(--fail)' if bad_here else ('var(--ok)' if i.link == 'up' else 'var(--act)') }}"></span>
|
||||
<strong>{{ role_name.get(i.role, i.role) }}</strong>
|
||||
<span class="mono muted">{{ i.iface }}</span>
|
||||
{% if i.verdict and i.verdict in verdict_chip %}{% set vc = verdict_chip[i.verdict] %}{{ ui.chip(vc[0], 'err' if bad_here else vc[1]) }}{% endif %}
|
||||
</div>
|
||||
<div class="muted" style="padding-left: 18px; white-space: normal">
|
||||
{{ 'Up' if i.link == 'up' else 'No link' }}{% if i.speed_mbps is number and i.speed_mbps > 0 %}, {% if i.speed_mbps >= 1000 %}{{ (i.speed_mbps // 1000) if i.speed_mbps % 1000 == 0 else (i.speed_mbps / 1000) | round(1) }} Gb/s{% else %}{{ i.speed_mbps }} Mb/s{% endif %}{% endif %}
|
||||
{% if i.addrs and i.addrs is not string %}<span class="mono">{{ i.addrs | join(', ') }}</span>{% endif %}
|
||||
{% if i.dnsmasq_bound is not none %}<br>DHCP/PXE {{ 'served here' if i.dnsmasq_bound else 'not served here' }}{% endif %}
|
||||
{% for e in (i.evidence if i.evidence is iterable and i.evidence is not string else []) %}<br>{{ e }}{% endfor %}{% for n in (i.notes if i.notes is iterable and i.notes is not string else []) %}<br><span class="sub">{{ n }}</span>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="muted">{{ 'Interfaces unknown.' if nic.source == 'error' else 'No network interfaces found.' }}</div>
|
||||
{% endfor %}
|
||||
<div class="sub" style="white-space: normal; border-top: 1px solid var(--line); padding-top: 8px">
|
||||
{% if nic.source == 'check' and not nic.stale %}Checked {{ nic.checked_at | local_dt }} by pxe-nic-check.
|
||||
{% elif nic.source == 'check' %}The network check is stale (last {{ nic.checked_at | local_dt }}): showing link and addresses only.
|
||||
{% elif nic.source == 'error' %}The network status could not be read{% if nic.error %}: <span class="mono">{{ nic.error }}</span>{% endif %}. See the webapp log.
|
||||
{% else %}Link and addresses only; pxe-nic-check (the GE-network detector) is not installed yet.{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% elif part == 'notice' %}
|
||||
{% if nic and nic.problems %}
|
||||
<div class="notice{{ ' bad' if nic.state == 'bad' else '' }}" role="{{ 'alert' if nic.state == 'bad' else 'status' }}">
|
||||
<span>{% if nic.state == 'bad' %}<strong>Network danger.</strong> {% endif %}{% for level, text in nic.problems %}{{ text }}{% if not loop.last %} {% endif %}{% endfor %}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -5,45 +5,7 @@
|
||||
{% block content %}
|
||||
{% set services_up = services | selectattr('active') | list | length %}
|
||||
{% call ui.head('Overview', 'West Jefferson') %}
|
||||
{% if nic %}
|
||||
{% set nic_dot = {'ok': 'var(--ok)', 'warn': 'var(--act)', 'bad': 'var(--fail)'}[nic.state] %}
|
||||
{% set role_name = {'imaging': 'Imaging', 'ge': 'GE network', 'other': 'Other'} %}
|
||||
{% set verdict_chip = {'isolated': ('Isolated', 'ok'), 'ge': ('GE network', 'neutral'), 'foreign-dhcp': ('Other DHCP server', 'err'),
|
||||
'no-link': ('No link', 'off'), 'error': ('Check failed', 'warn')} %}
|
||||
<details class="more">
|
||||
<summary class="fbtn" style="background: var(--surface); min-height: 36px">
|
||||
<span class="dot" style="background: {{ nic_dot }}"></span>
|
||||
{{ 'Network OK' if nic.state == 'ok' else ('Network: check' if nic.state == 'warn' else 'Network: DANGER') }}
|
||||
<span class="chev" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
<div class="menu" style="width: min(460px, calc(100vw - 32px)); padding: 12px 14px; gap: 10px; font-size: 13px">
|
||||
{% for i in nic.interfaces %}
|
||||
{% set bad_here = (i.role == 'imaging' and i.verdict in ('ge', 'foreign-dhcp')) or (i.role != 'imaging' and i.dnsmasq_bound) %}
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<span class="dot" style="background: {{ 'var(--fail)' if bad_here else ('var(--ok)' if i.link == 'up' else 'var(--act)') }}"></span>
|
||||
<strong>{{ role_name.get(i.role, i.role) }}</strong>
|
||||
<span class="mono muted">{{ i.iface }}</span>
|
||||
{% if i.verdict and i.verdict in verdict_chip %}{% set vc = verdict_chip[i.verdict] %}{{ ui.chip(vc[0], 'err' if bad_here else vc[1]) }}{% endif %}
|
||||
</div>
|
||||
<div class="muted" style="padding-left: 18px; white-space: normal">
|
||||
{{ 'Up' if i.link == 'up' else 'No link' }}{% if i.speed_mbps %}, {% if i.speed_mbps >= 1000 %}{{ (i.speed_mbps // 1000) if i.speed_mbps % 1000 == 0 else (i.speed_mbps / 1000) | round(1) }} Gb/s{% else %}{{ i.speed_mbps }} Mb/s{% endif %}{% endif %}
|
||||
{% if i.addrs %}<span class="mono">{{ i.addrs | join(', ') }}</span>{% endif %}
|
||||
{% if i.dnsmasq_bound is not none %}<br>DHCP/PXE {{ 'served here' if i.dnsmasq_bound else 'not served here' }}{% endif %}
|
||||
{% for e in i.evidence %}<br>{{ e }}{% endfor %}{% for n in i.notes or [] %}<br><span class="sub">{{ n }}</span>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="muted">No network interfaces found.</div>
|
||||
{% endfor %}
|
||||
<div class="sub" style="white-space: normal; border-top: 1px solid var(--line); padding-top: 8px">
|
||||
{% if nic.source == 'check' and not nic.stale %}Checked {{ nic.checked_at | local_dt }} by pxe-nic-check.
|
||||
{% elif nic.source == 'check' %}The network check is stale (last {{ nic.checked_at | local_dt }}): showing link and addresses only.
|
||||
{% else %}Link and addresses only; pxe-nic-check (the GE-network detector) is not installed yet.{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
{{ nic_pill }}
|
||||
<details class="more">
|
||||
<summary class="fbtn" style="background: var(--surface); min-height: 36px">
|
||||
<span class="dot" style="background: {{ 'var(--ok)' if services_up == services|length else 'var(--fail)' }}"></span>
|
||||
@@ -59,11 +21,7 @@
|
||||
{% endcall %}
|
||||
|
||||
<div class="ui-stack">
|
||||
{% if nic and nic.problems %}
|
||||
<div class="notice{{ ' bad' if nic.state == 'bad' else '' }}" role="{{ 'alert' if nic.state == 'bad' else 'status' }}">
|
||||
<span>{% if nic.state == 'bad' %}<strong>Network danger.</strong> {% endif %}{% for level, text in nic.problems %}{{ text }}{% if not loop.last %} {% endif %}{% endfor %}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ nic_notice }}
|
||||
{% if blancco_todo.total %}
|
||||
{% set parts = [] %}
|
||||
{% if blancco_todo.servicenow %}{% set _ = parts.append(blancco_todo.servicenow ~ ' to tick in ServiceNow') %}{% endif %}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
<label class="sub" style="display: flex; flex-direction: column; gap: 4px">To
|
||||
<input id="evidenceTo" type="date" name="to" value="{{ pack_to }}" class="field" required></label>
|
||||
</div>
|
||||
<div style="display: flex; gap: 6px; flex-wrap: wrap" aria-label="Quick ranges">
|
||||
<div style="display: flex; gap: 6px; flex-wrap: wrap" role="group" aria-label="Quick ranges">
|
||||
<button type="button" class="fbtn" data-range="last-month">Last month</button>
|
||||
<button type="button" class="fbtn" data-range="last-3-months">Last 3 months</button>
|
||||
<button type="button" class="fbtn" data-range="this-month">This month</button>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
{# Signer as a short chip; the long explanation goes in the row body (it is
|
||||
a safety warning, so never hover-only). #}
|
||||
{% macro sig_chip(info) -%}
|
||||
{% if info.kind == 'microsoft-2011' %}{{ ui.chip('MS UEFI CA 2011 only', 'warn') }}
|
||||
{% if info.dual %}{{ ui.chip('MS UEFI CA 2011 + 2023', 'ok') }}
|
||||
{% elif info.kind == 'microsoft-2011' %}{{ ui.chip('MS UEFI CA 2011 only', 'warn') }}
|
||||
{% elif info.kind == 'microsoft-2023' %}{{ ui.chip('MS UEFI CA 2023', 'ok') }}
|
||||
{% elif info.kind == 'microsoft' %}{{ ui.chip('Microsoft, other CA', 'neutral') }}
|
||||
{% elif info.kind == 'ipxe' %}{{ ui.chip('iPXE Secure Boot CA', 'ok') }}
|
||||
@@ -17,7 +18,8 @@
|
||||
|
||||
{% macro sig_text(info, chain_ok=none) -%}
|
||||
{% if info.kind == 'microsoft-2011' %}<div class="notice">Signed only under Microsoft Corporation UEFI CA 2011. That CA's signing period ended in 2026: machines that trust only the 2023 CA, or a DBX/SBAT revocation update, will refuse this file.</div>
|
||||
{% elif info.kind == 'microsoft-2023' %}<div class="sub">Signed under Microsoft UEFI CA 2023.</div>
|
||||
{% elif info.dual %}<div class="sub">Dual-signed: under Microsoft Corporation UEFI CA 2011 and Microsoft UEFI CA 2023.</div>
|
||||
{% elif info.kind == 'microsoft-2023' %}<div class="notice">Signed under Microsoft UEFI CA 2023 only, not dual-signed: machines that trust only the 2011 CA will refuse it.</div>
|
||||
{% elif info.kind == 'ipxe' %}<div class="sub">Chains to the iPXE Secure Boot CA, which the iPXE shim trusts.</div>
|
||||
{% elif info.kind == 'bios' %}<div class="sub">Legacy BIOS has no Secure Boot; the file is unsigned by design.</div>
|
||||
{% elif info.kind == 'unsigned' %}<div class="notice bad">Unsigned: Secure Boot firmware will refuse it.</div>
|
||||
@@ -149,7 +151,7 @@
|
||||
<div class="panel strip" role="status">
|
||||
<span class="pulse" aria-hidden="true"></span>
|
||||
<span id="ipxeJobText">{{ 'Starting' if job.state == 'queued' else (job.step | capitalize) }} {{ job.tag }}...</span>
|
||||
<div class="bar" aria-hidden="true"><div id="ipxeJobBar" style="width: {{ job.percent or 0 }}%"></div></div>
|
||||
<div class="bar" aria-hidden="true"><div id="ipxeJobBar" style="width: {{ 100 if job.step in ('verify', 'shim') else (job.percent or 0) }}%"></div></div>
|
||||
</div>
|
||||
{% elif job.state == 'error' %}
|
||||
<div class="errbox"><strong>Download of {{ job.tag }} failed:</strong> <span class="mono">{{ job.error }}</span></div>
|
||||
@@ -213,8 +215,9 @@
|
||||
<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>
|
||||
<div class="rel-h"><strong class="mono">{{ m.mac }}</strong><span class="sub mono">{{ tftp_root }}/{{ m.dir }}/</span>
|
||||
<span class="sub">{{ m.files | length }} file{{ 's' if m.files | length != 1 }}</span></div>
|
||||
{% for w in m.warnings or [] %}<div class="notice bad" style="margin: 0 24px 8px"><span>{{ w }}</span></div>{% endfor %}
|
||||
{% for f in m.files %}
|
||||
{% set i = f.info %}
|
||||
{% set chain_ok = i.chain_ok if i.chain_ok is defined else none %}
|
||||
@@ -229,7 +232,7 @@
|
||||
<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>Path</dt><dd class="mono">{{ tftp_root }}/{{ 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>
|
||||
@@ -289,6 +292,7 @@
|
||||
<div class="rel-h">
|
||||
<strong class="mono">{{ s.tag }}</strong>
|
||||
<span class="sub">verified {{ s.staged | local_dt }}</span>
|
||||
{% if s.stale %}{{ ui.chip('Stale: download again', 'warn') }}{% endif %}
|
||||
<span class="tip inl" data-tip="{{ s.tarball_sha256 }}"><span class="sub">tarball sha256 <span class="mono">{{ (s.tarball_sha256 or '')[:12] }}</span> matches GitHub</span></span>
|
||||
</div>
|
||||
{% for name, f in s.files.items() %}
|
||||
@@ -380,7 +384,8 @@
|
||||
</summary>
|
||||
<div class="ibody">
|
||||
{% if b.reason %}<div>{{ b.reason }}</div>{% endif %}
|
||||
{% for f in b.files %}<div><span class="mono">{{ f }}</span> <span class="muted">{{ b.versions[f] or '' }}</span></div>{% else %}<div class="muted">Empty backup.</div>{% endfor %}
|
||||
{% for f in b.files %}<div><span class="mono">{{ f }}</span> <span class="muted">{{ b.versions[f] or '' }}</span>{% if b.info and b.info[f] %} {{ sig_chip(b.info[f]) }}{% endif %}</div>{% else %}<div class="muted">Empty backup.</div>{% endfor %}
|
||||
{% for w in b.warnings or [] %}<div class="notice bad"><span>{{ w }}</span></div>{% endfor %}
|
||||
</div>
|
||||
</details>
|
||||
{% if b.files %}
|
||||
@@ -392,6 +397,8 @@
|
||||
onsubmit="return confirm(this.dataset.confirm);">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="backup" value="{{ b.id }}">
|
||||
{% if b.warnings %}<label class="sub" style="display: flex; gap: 6px; align-items: flex-start; white-space: normal; padding: 6px 10px">
|
||||
<input type="checkbox" name="confirm" value="1" required> Restore anyway: {{ b.warnings | join('; ') }}</label>{% endif %}
|
||||
<button type="submit">Restore these files</button>
|
||||
</form>
|
||||
{% endcall %}
|
||||
@@ -437,8 +444,8 @@ ls -l ipxeboot/x86_64-sb/</pre></li>
|
||||
<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>
|
||||
The dual-signed shim shows <span class="mono">CN=Microsoft UEFI CA 2023</span> and <span class="mono">CN=Microsoft Corporation UEFI CA 2011</span>
|
||||
(depending on the sbverify version it lists both signatures or only the outer one; this page reads every signature, nested ones too). 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>
|
||||
@@ -484,7 +491,7 @@ install -m 0644 ipxe-shimx64.efi /srv/tftp/.ipxe-shim.efi.new &
|
||||
if (j.state !== 'queued' && j.state !== 'running') { location.reload(); return; }
|
||||
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)) + '%';
|
||||
if (bar) bar.style.width = ((j.step === 'verify' || j.step === 'shim') ? 100 : (j.percent || 0)) + '%';
|
||||
setTimeout(tick, 2000);
|
||||
})
|
||||
.catch(function () { setTimeout(tick, 5000); });
|
||||
|
||||
Reference in New Issue
Block a user