Let build.sh sign the kernel for Secure Boot - #130
Conversation
FOS kernels ship unsigned, so a site running UEFI Secure Boot has to sign them itself. Doing that after the fact means the published .sha256 no longer matches the file anyone actually boots, and it has to be redone by hand on every build. --sign-key/--sign-cert (or FOS_SIGN_KEY/FOS_SIGN_CERT for CI) sbsign the artifact between the copy into dist/ and the checksum, so the hash covers the signed image. With neither set the build is byte-for-byte what it was. Half a pair is refused rather than silently producing an unsigned kernel someone believes is signed -- that failure would otherwise surface much later, at a client, as a Security Policy Violation. Grouping the finalize step also fixes a latent bug: the old "a && b || cp && sha256sum" chain ran sha256sum even when the compiled kernel was missing, printing "File not found." and then writing an empty .sha256 for a file that was not there.
|
Heads-up from running this by hand while closing FOGProject/fogproject#960:
fog-docs#84 documents Line ~83 passes if ! sbsign --key "$signKey" --cert "$signCert" \Cheapest robust fix is to normalise once after the key/cert pair is validated — if the file parses as DER, convert it to a PEM in a temp path and use that for Everything else here held up in practice — signing before |
Two things this page got wrong, one of them blocking.
**Blocking: sbsign and sbverify cannot read a DER certificate.** Step 1
produced only MOK.der, and the signing steps then handed that file to
sbsign/sbverify, which load certificates with PEM_read_bio_X509 and
reject DER outright:
$ sbsign --key MOK.priv --cert MOK.der --output out.efi in.efi
Can't load certificate from file 'MOK.der'
error:0480006C:PEM routines:get_name:no start line
mokutil and MokManager want the opposite. Neither tool says which format
it wanted, so anyone following this page hit an OpenSSL error that never
mentions the problem. Step 1 now produces MOK.pem alongside MOK.der and
says which tool takes which; the sbverify and build.sh examples use the
PEM. The installer's --secure-boot-cert accepts either and converts
internally, so this only bites when running the tools by hand.
**Out of date: the manual download steps.** /tftpboot/secureboot/ is
staged on every install from FOG 1.6.0 -- shim, signed snponly.efi,
MokManager, autoexec.ipxe and a MANIFEST, hash- and signer-verified when
the fog-ipxe release is built. So the curl/tar/rename/chown/restorecon
block is replaced by what is already there and how to check it. The
--secure-boot-stage flag it mentioned no longer exists; staging is
unconditional.
The explanatory notes are kept, because they are still why the files are
shaped the way they are -- in particular the -shim filename mechanism and
why the shim must come from the ipxe/shim release rather than
ipxeboot.tar.gz. The dual-signature point is now enforced at release time
rather than left as advice.
Also:
- The autoexec.ipxe section described two locations and one hard link.
There are six directories an EMBED-less binary can boot from and the
installer links all six; added an inode check to confirm it.
- Removed the standalone restorecon warning. The installer sets the
SELinux context itself now (FOGProject/fogproject#963) -- it was
default_t, which no confined tftpd may read, and it presented as a bare
file-not-found with nothing in FOG's logs.
- New "Verified" section. The chain has now been run end to end with
Secure Boot enforcing through to a completed 42 GB deploy, so the
"MOK-signed bzImage has never been confirmed booting" limit is gone.
States explicitly that no shim command or ShimRetainProtocol handling
is needed -- shim's verification protocol survives into iPXE, and a
reader could reasonably fear the opposite.
- New limit: automatic_next_path() is called only from shim's netboot and
httpboot paths, so a shim booted from USB or an ESP ignores the -shim
rename and falls back to ipxe.efi. Anyone building a Secure Boot USB
from this page would have hit that with no obvious cause.
- fog-sign-kernel lives under $fogprogramdir, not a hardcoded /opt/fog.
Refs FOGProject/fogproject#960, FOGProject/fogproject#961,
FOGProject/fogproject#963, FOGProject/fos#130
--sign-cert went straight to sbsign, which reads certificates with
OpenSSL's PEM_read_bio_X509 and rejects DER outright:
Can't load certificate from file 'MOK.der'
error:0480006C:PEM routines:get_name:no start line
mokutil and MokManager -- the tools that enrol the same certificate on a
client -- want the opposite. Anyone following the Secure Boot how-to
therefore ends up holding one of each with nothing saying which tool
takes which, and picking wrong failed with an error that never mentions
the format.
Worse, sbsign's stderr was going to /dev/null, so that line was thrown
away and the operator got only "sbsign could not sign bzImage". The
error above is the whole diagnosis and it was being discarded.
So: accept either format and convert once up front, matching how
--secure-boot-cert now behaves in the installer, and capture sbsign's
stderr to print on failure. That second change is worth as much as the
first -- it covers unreadable keys and malformed images too, not just
this one mistake.
Also removes the partial .signed file when signing fails, rather than
leaving an unsigned-but-suspiciously-named artifact next to the kernel.
Verified: DER and PEM inputs both produce a kernel sbverify reports as
"Signature verification OK"; a non-certificate is refused up front
naming both formats tried; an unreadable key now prints sbsign's actual
complaint; no .signed artifact survives a failure; and the temporary PEM
is removed on exit.
Refs FOGProject/fogproject#960, FOGProject/fog-docs#84
|
Pushed
|
| case | result |
|---|---|
| DER cert | converts, signs, Signature verification OK |
| PEM cert | signs, Signature verification OK |
| non-certificate | refused up front, naming both formats tried |
| unreadable key | sbsign's actual complaint now printed |
| failed signing | no .signed artifact left behind |
| temp PEM | removed on exit via trap |
The temp file is world-readable, which is fine — the certificate is public; it is the private key beside it that matters. There was no existing trap in build.sh to clobber.
sha256sum still runs after signing, so the published hash covers the signed image.
This was the last open instance of the DER bug — the installer side landed in FOGProject/fogproject#961 and the docs in FOGProject/fog-docs#84.
Companion to FOGProject/fogproject#961 (server-side signing and the enrolment kit) and the guide rewrite in FOGProject/fog-docs. This is the piece for people who build FOS themselves rather than using the released kernels.
The problem
FOS kernels ship unsigned, so a site running UEFI Secure Boot has to sign them itself. Doing that after the fact means the published
.sha256no longer matches the file anyone actually boots, and it has to be redone by hand on every build.What changed
--sign-key/--sign-cert(orFOS_SIGN_KEY/FOS_SIGN_CERT, which is easier in CI)sbsignthe artifact inbuildKernel()between the copy intodist/and thesha256sum, so the published hash covers the signed image.With neither set the build is byte-for-byte what it was. Half a pair is refused rather than silently producing an unsigned kernel someone believes is signed — that failure would otherwise surface much later, at a client, as a
Security Policy Violationwith nothing on the server to explain it. A signing failure is fatal for the same reason.Drive-by fix
Grouping the finalize step also fixes a latent bug. The old chain:
parses as
((A && B) || C) && D, so when the compiled kernel was missing it printedFile not found.and then still ransha256sum, writing an empty.sha256for a file that was not there. Reproduced against the original line before changing it.buildFilesystem()has the same shape, but it is untouched here — out of scope for this change.Verified
bash -n build.sh.sbsignacross three cases: no key produces a byte-identical artifact and a checksum that verifies; with a key the artifact is signed and the checksum covers the signed file; a missing compiled kernel now produces neither file.Not verified
No kernel was compiled — the signing step is verified in isolation, not through a real
./build.sh -nka x64. Worth one real build with a throwaway key before merging.🤖 Generated with Claude Code
https://claude.ai/code/session_01HZ2jZ7qgen6pXrbtmxShaw
Generated by Claude Code