feat(packaging/bazzite): the sysext feed is signed, and the client refuses one that isn't
`punktfunk-sysext` checked the SHA256 of every image it downloaded, which sounds like verification but isn't: SHA256SUMS lives on the same registry as the images it describes, so anything able to replace an image could replace its checksum in the same request. The checksum only ever proved the download wasn't corrupted. Each feed now carries SHA256SUMS.asc, a detached OpenPGP signature over the manifest, and the client verifies it before believing a line of it. The key is packages@unom.io (AF245C506F4E4763) — the same one that already signs our RPMs, so boxes have one key to trust and we have one key to rotate. Its public half is baked into the script rather than fetched from the feed, because a key you fetch from the thing you're authenticating authenticates nothing; gpg (present on Bazzite) does the verifying against a throwaway keyring holding only that key, so "good signature" and "signed by us" are the same statement. Rollout: stable feeds only publish on a tag, so a `--seal` mode re-signs an existing manifest without rebuilding an image, and every rpm.yml run seals the OTHER channel of its Fedora major too. Canary pushes are frequent, so all live feeds seal within a day of this landing and a key rotation propagates without republishing anything. Until a feed is sealed the client refuses it and says so, naming PUNKTFUNK_SYSEXT_ALLOW_UNSIGNED=1 as the informed way through. Two things testing changed. The baked-key fingerprint check compared against an empty string — the armor block is a single-quoted shell literal, so the extracted range carried `FEED_KEY='` on its first line and gpg saw no armor at all; every publish would have "mismatched" and, on a tag, failed the release. And `status` captured fetch_manifest's stderr but only printed it on failure, swallowing the ALLOW_UNSIGNED warning precisely when someone was running unverified. Verified end to end on Bazzite 44 against a file:// feed with a throwaway key: unsigned feed refused; ALLOW_UNSIGNED=1 proceeds and says so; wrong signer rejected; tampered manifest rejected; good signature accepted; `update` exits 1 before touching anything on a bad feed. Publisher side: signs when the baked key matches, refuses on mismatch, refuses with no key, and its signature round-trips through the real client. shellcheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,8 @@ sudo bash punktfunk-sysext.sh install
|
||||
```
|
||||
|
||||
This downloads the newest image for your Fedora base (host + tray + **web console**,
|
||||
SHA-256-verified from the feed `…/packages/unom/generic/punktfunk-sysext/f<ver>[-canary]/`),
|
||||
SHA-256-verified against a signed manifest from the feed
|
||||
`…/packages/unom/generic/punktfunk-sysext/f<ver>[-canary]/`),
|
||||
installs it as `/var/lib/extensions/punktfunk.raw`, merges it, and immediately applies what the
|
||||
RPM scriptlets would have (udev reload, sysctl) plus the two `/etc` files a sysext can't carry
|
||||
(the gamescope-session drop-in and the tray autostart entry, staged under
|
||||
@@ -63,6 +64,14 @@ sudo punktfunk-sysext remove # unmerge + delete; ~/.config/punktfunk is left
|
||||
|
||||
Details worth knowing:
|
||||
|
||||
- **The feed is signed.** Each feed carries `SHA256SUMS` plus a detached OpenPGP signature
|
||||
`SHA256SUMS.asc` from `packages@unom.io` (`AF245C506F4E4763`) — the same key that signs our RPMs.
|
||||
`punktfunk-sysext` verifies that signature, with the public key baked into the script, *before*
|
||||
it believes the manifest, and refuses a feed it can't verify. The checksums alone never proved
|
||||
authorship: they sit on the same registry as the images they describe, so whatever could replace
|
||||
an image could replace its checksum in the same breath. If you are on a feed published before
|
||||
signing existed, it is sealed on the next publish to that Fedora major; to install from it
|
||||
meanwhile — accepting that the image is unauthenticated — set `PUNKTFUNK_SYSEXT_ALLOW_UNSIGNED=1`.
|
||||
- The image embeds `ID=fedora` + `VERSION_ID` (matched through Bazzite's `ID_LIKE`), so after a
|
||||
**major Bazzite rebase** (F43 → F44) the old image is **refused** instead of merging
|
||||
soname-broken binaries — `punktfunk-sysext update` then fetches the image built for the new
|
||||
|
||||
Reference in New Issue
Block a user