Buildroot 2026.08: line bump, bluez backports retired, SSH key persistence - #150
Merged
Conversation
A line bump, done by hand, because allowedVersions deliberately fences Renovate to one line at a time. Renovate is NOT broken here: the manager extracts fine, github-tags sees the tag, and `loose` versioning orders 2026.08 ([2026,8]) above 2026.05.2 ([2026,5,2]) correctly -- the differing component count is not what stops it, and neither is the absent patch component. `allowedVersions` filters the candidate out on purpose, so moving the line stays a human decision. Same route PR #54 took for 2026.02.x -> 2026.05.x. BUILDROOT_SHA256 transcribed from the GPG-clearsigned .sign manifest (`make buildroot-showsig`), never from the tarball; `make buildroot-verify` passes. WHAT THE BUMP MOVED: gcc 14.4.0 -> 15.3.0, glibc 2.43 -> 2.44, gdb 15.2 -> 16.3, bluez 5.79 -> 5.86, Samba 4.24.5 -> 4.24.6, OpenSSL 3.6.3 -> 3.6.4. Read off `make show-info`, not guessed. THE bluez PATCH DIRECTORY IS GONE. board/mister/de10nano/patches/bluez5_utils/ carried five upstream commits only because Buildroot pinned 5.79, and its own headers said to delete it on the first bump whose bluez contained them. 5.86 does. Each was confirmed redundant against the PRISTINE 5.86 tarball with apply-patches.sh:119's exact flags rather than inferred from release notes -- which mattered: 0005's header predicted 5.87 and it is already in 5.86. 0002 is superseded rather than merely redundant: 5.86 calls device_set_cable_pairing() OUTSIDE the CABLE_PAIRING_SIXAXIS branch and adds server_set_cable_pairing(), so re-applying our backport would have NARROWED upstream's behaviour. ci-tests' server_set_cable_pairing gate is KEPT: it asserts behaviour reached the build tree, never which mechanism put it there, so it now guards "the pinned bluez still carries the series" instead. DE25 REGRESSION CAUGHT BY THE GOLDEN HASH, AND IT IS THE INTERESTING PART. Buildroot 2026.08 RETIRED the 7.0 kernel-headers series into Config.in.legacy. A retired symbol still SETS cleanly, so check-config-fragments' "every fragment symbol survives" assertion passed with 0 dropped -- the literal BR2_KERNEL_HEADERS_7_0=y line really was in the resolved config. What silently collapsed was everything downstream: the headers choice fell back to AS_KERNEL and the DE25 libc fell from glibc to uClibc. Only the golden hash moved. Pin is now BR2_KERNEL_HEADERS_7_1 (7.1.13, newest <= our 7.2.3 kernel); glibc restored, BR2_LEGACY clear. Lesson recorded in docs: a Config.in.legacy demotion is invisible to a symbol-presence check, and the resolved-config diff must actually be read on a line bump -- regenerating the hash alone would have shipped it. (The first golden regeneration here DID bake in the broken config; it was redone after the fix.) Golden hashes verified deterministic: resolving the fragments against an unpacked 2026.05.2 tree reproduces the recorded 2026.05.2 hashes exactly. NOT FIXED UPSTREAM, STILL LOAD-BEARING: dhcpcd is still 10.2.4, dhcpcd.mk still passes no --with-hooks, and configure:1880 still guards the host probes with `if ! $HOOKSET`. external.mk's override (PR #139) stays; the build asserts the pinned hook set. FLAGGED, NOT CHANGED: 2026.08's BR2_TARGET_UBOOT_LATEST_VERSION is 2026.07 -- exactly the DE25's custom pin -- so that pin is now redundant in version terms. Left alone: DE25 is not built in CI, and swapping an unbooted board's bootloader source should not ride along on a Buildroot bump. Verified: `make all` exit 0; ci-tests 174/174 (0 skipped -- the two ABI checks now run, a stock MiSTer binary having been placed for them); all 4 config stacks regenerate; renovate-config-validator clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQovqPVshbtdzJnZyr5bc
…rvives an update THE GAP. An OS update replaces linux.img wholesale, and / is a read-only loop-mounted ext4 (`/dev/loop0 on / type ext4 (ro)`). So /root/.ssh cannot be written on the box at runtime AT ALL, and a freshly built image ships no /root/.ssh whatsoever -- only /root/.config/mc/. The stock-parity `AuthorizedKeysFile .ssh/authorized_keys` can therefore only ever be populated by injecting a key into linux.img before flashing, and the next update discards it. That is the "persistent state must live on /media/fat" trap, and it had been costing a manual re-push on every single flash. THE FIX. sshd accepts multiple AuthorizedKeysFile paths and tries each, so the shipped config now lists the stock path plus /media/fat/linux/authorized_keys. The FAT partition is never reflashed, so a key there is picked up after every update. No init script, no bind-mount, no user-startup.sh hook, no new persistence image -- one config line. WHY NOT ssh.ext4 (ADR 0015's mechanism), which was the obvious candidate: it is an ext4 image INSIDE a file, and that is right for HOST keys because the DEVICE writes them. An authorized_keys file is written by the USER, and an ext4-in-a-file cannot be opened from Windows or macOS with a card reader, while editing it on the box needs the very shell access the key is meant to grant -- circular for anyone setting key auth up for the first time. The split is who writes the file: machine-written state in ssh.ext4, user-supplied state on exFAT, which every OS can write with no tooling. StrictModes STAYS ON (default yes) and the FAT path satisfies it. sshd rejects an authorized_keys whose file or parents are group/world-writable; the initramfs mounts the partition fmask=0022,dmask=0022 with no uid/gid options (initramfs-overlay/init:27), so the file lands root-owned 0755 under 0755 parents. Those mount options are OURS and fixed, so this cannot be quietly invalidated by a card mounted elsewhere. Disabling StrictModes was never needed. VERIFIED ON HARDWARE, not reasoned about: a second sshd on port 2223 configured with ONLY the FAT path and StrictModes yes accepted a key login (OpenSSH 10.5p1, exFAT, real DE10-Nano). The shipped config also passes `sshd -t` and reports both paths under `sshd -T` on the device. CI: two new fail-closed gates assert the FAT path is in the SHIPPED sshd_config (not merely the overlay source -- the overlay only matters if it reaches the rootfs) and that `StrictModes no` is absent. Dropping either would silently return every user to "your key is gone after each update" with nothing else failing. ci-tests 176/176, 0 skipped. User-facing instructions: docs/user/faq.md#ssh-key-persist. Rationale and the rejected alternative: docs/ssh-ftp-parity.md §1.3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQovqPVshbtdzJnZyr5bc
…eys path The previous commit justified /media/fat/linux/authorized_keys partly with "/root/.ssh cannot be written at runtime at all". That is WRONG, and observing a booted rig is what caught it: `/dev/loop0 on / type ext4 (rw,noatime,nodiratime)`. / is mounted read-only at BOOT (`ro` on the cmdline; inittab's remount-rw line deliberately left commented, ADR 0011) and becomes writable on interactive login, because /etc/profile ends with `mount -o remount,rw /`. That is stock parity and is documented as the mechanism by which / ever becomes writable at all (docs/init-parity.md, the /etc/profile row) -- not a regression, and not something this change should have contradicted. So a key CAN be written to /root/.ssh by hand after logging in. THE CONCLUSION IS UNCHANGED, only the reasoning. The durable justification never needed the read-only claim: an update replaces linux.img wholesale, and /root/.ssh lives inside that file, so a key there is destroyed by every update regardless of how / is mounted at the time. The secondary point also survives in weaker form -- a fresh image ships no /root/.ssh at all and / is read-only until you log in, so seeding the stock path by hand requires a login first, which is circular when the key IS the login method. Reworded in all four places that carried the overstatement: the sshd_config comment, ci-tests.sh's gate rationale, docs/ssh-ftp-parity.md §1.3 (which also now records the correction, so the next reader does not re-derive the wrong model), README's security-posture bullet and the user FAQ. Comment-only in the shipped sshd_config, but rebuilt and re-verified rather than assumed: ci-tests 176/176, 0 skipped, and the shipped config still carries the two-path AuthorizedKeysFile line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PFQovqPVshbtdzJnZyr5bc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent changes on one branch: the Buildroot 2026.05.2 → 2026.08 line bump and everything it surfaced, then an unrelated SSH key persistence fix.
1. Buildroot 2026.08
Done by hand because
allowedVersionsdeliberately fences Renovate to one line at a time. Renovate is not broken here — the manager extracts fine,github-tagssees the tag, andlooseversioning orders2026.08([2026,8]) above2026.05.2([2026,5,2]) correctly. Neither the differing component count nor the absent patch component is what stops it;allowedVersionsfilters it out on purpose. Same route PR #54 took for 2026.02.x → 2026.05.x.BUILDROOT_SHA256transcribed from the GPG-clearsigned.signmanifest, never from the tarball.Moved: gcc 14.4.0 → 15.3.0, glibc 2.43 → 2.44, gdb 15.2 → 16.3, bluez 5.79 → 5.86, Samba 4.24.5 → 4.24.6, OpenSSL 3.6.3 → 3.6.4.
The bluez patch directory is gone
board/mister/de10nano/patches/bluez5_utils/carried five upstream commits only because Buildroot pinned 5.79, and its own headers said to delete it on the first bump whose bluez contained them. 5.86 does.Each was confirmed redundant against the pristine 5.86 tarball with
apply-patches.sh:119's exact flags rather than inferred from release notes — which mattered:0005's header predicted 5.87 and it is already in 5.86.0002is superseded, not merely redundant: 5.86 callsdevice_set_cable_pairing()outside theCABLE_PAIRING_SIXAXISbranch and addsserver_set_cable_pairing(), so re-applying our backport would have narrowed upstream's behaviour.The
server_set_cable_pairingCI gate is kept — it asserts behaviour reached the build tree, never which mechanism put it there, so it now guards "the pinned bluez still carries the series".A DE25 regression only the golden hash caught
2026.08 retired the 7.0 kernel-headers series into
Config.in.legacy. A retired symbol still sets cleanly, socheck-config-fragments' "every fragment symbol survives" assertion passed with 0 dropped — the literalBR2_KERNEL_HEADERS_7_0=yreally was in the resolved config. What collapsed was downstream: headers fell back toAS_KERNELand the DE25 libc fell from glibc to uClibc.Pin is now
BR2_KERNEL_HEADERS_7_1(7.1.13, newest ≤ our 7.2.3 kernel); glibc restored,BR2_LEGACYclear. The lesson is recorded in the docs: aConfig.in.legacydemotion is invisible to a symbol-presence check, and the resolved-config diff must actually be read on a line bump. (The first golden regeneration here did bake in the broken config; it was redone after the fix.)Golden hashes verified deterministic — resolving the fragments against an unpacked 2026.05.2 tree reproduces the recorded 2026.05.2 hashes exactly.
Still load-bearing, not fixed upstream
dhcpcd is still 10.2.4,
dhcpcd.mkstill passes no--with-hooks, andconfigure:1880still guards the host probes withif ! $HOOKSET.external.mk's override (#139) stays.Flagged, not changed
2026.08's
BR2_TARGET_UBOOT_LATEST_VERSIONis2026.07— exactly the DE25's custom pin, now redundant in version terms. Left alone: DE25 is not built in CI, and swapping an unbooted board's bootloader source should not ride along on a Buildroot bump.2. SSH key that survives an image update
An update replaces
linux.imgwholesale and/is a read-only loop-mounted ext4, so/root/.sshcannot be written at runtime and a fresh image ships no/root/.sshat all. The stock-parity path could only be populated by baking a key into the image before flashing, and every update discarded it.sshdaccepts multipleAuthorizedKeysFilepaths, so the shipped config now also reads/media/fat/linux/authorized_keys— one config line, no init script, no hook.Why not
ssh.ext4(ADR 0015's mechanism): it is an ext4 image inside a file — right for host keys, which the device writes. Anauthorized_keysis written by the user, and an ext4-in-a-file cannot be opened from Windows/macOS with a card reader, while editing it on the box needs the very shell access the key is meant to grant. The split is who writes the file.StrictModesstays on and the FAT path satisfies it — the initramfs mountsfmask=0022,dmask=0022with no uid/gid options, so the file lands root-owned0755under0755parents. Those options are ours and fixed.Verified on hardware: a second
sshdon port 2223 with only the FAT path andStrictModes yesaccepted a key login (OpenSSH 10.5p1, exFAT, real DE10-Nano). Two new fail-closed CI gates assert the path is in the shipped config and thatStrictModes nois absent.Verification
make allexit 0scripts/ci-tests.sh176 passed, 0 failed, 0 skipped — the two ABI checks now run rather than skip, a stock MiSTer binary having been placed for themscripts/test-initramfs.sh8/8 QEMU boot casesrenovate-config-validatorclean;make buildroot-verifypasses🤖 Generated with Claude Code
https://claude.ai/code/session_01PFQovqPVshbtdzJnZyr5bc