feat(host/linux): opt-in one-click updates — the pf-update root helper, group-scoped polkit grant, per-PM legs
The U2 leg of planning:host-update-from-web-console.md. A new dep-free root helper (crates/pf-update) runs the distro package manager against the INSTALLED punktfunk packages — apt (index refresh scoped to our list when present), dnf, rpm-ostree (single-transaction re-resolve, reported staged), sysext (the proven signed-feed updater), pacman only behind the explicit PACMAN_FULL_SYSUPGRADE opt-in — then the run-the-binary gate, then a root-written result record. Zero attacker-influenceable parameters end to end: fixed ExecStart oneshot (punktfunk-update.service), polkit rule scoped to that one unit's start verb for the shipped-EMPTY punktfunk-update group (joining it is the auditable opt-in; every postinst creates it, none populate it). The host starts the unit, interprets the record (staged / nothing-newer-yet / changed), and crosses its own restart on the same intent/reconcile machinery as the Windows leg. Status now reports staged results and the opt-in hint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// Members of the `punktfunk-update` group may START exactly one unit: the punktfunk
|
||||
// root-helper oneshot that updates the punktfunk packages (planning:
|
||||
// host-update-from-web-console.md §7). The group ships EMPTY — joining it is the explicit,
|
||||
// auditable opt-in for web-console-triggered updates:
|
||||
//
|
||||
// sudo usermod -aG punktfunk-update <your user>
|
||||
//
|
||||
// Scope notes: `verb == "start"` keeps stop/restart/enable out of the grant, and the unit
|
||||
// name pin keeps every other unit out. The unit's ExecStart is fixed and parameterless, so
|
||||
// this grant authorizes "run the system's normal update for the punktfunk packages" and
|
||||
// nothing else.
|
||||
polkit.addRule(function (action, subject) {
|
||||
if (
|
||||
action.id == "org.freedesktop.systemd1.manage-units" &&
|
||||
action.lookup("unit") == "punktfunk-update.service" &&
|
||||
action.lookup("verb") == "start" &&
|
||||
subject.isInGroup("punktfunk-update")
|
||||
) {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
# The web-console-triggered host update, as a root oneshot (planning:
|
||||
# host-update-from-web-console.md §7). Started — never enabled — by the unprivileged host via
|
||||
# `systemctl start punktfunk-update.service`, authorized by the polkit rule
|
||||
# (49-punktfunk-update.rules) for members of the `punktfunk-update` group. systemd gives us
|
||||
# single-flight (a second start while active fails) and journal capture for free; the helper
|
||||
# writes its machine-readable outcome to /var/lib/punktfunk/update-result.json.
|
||||
[Unit]
|
||||
Description=punktfunk host update (root helper)
|
||||
# Network is a hard prerequisite for every leg (package manager / sysext feed).
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/libexec/punktfunk/pf-update apply
|
||||
# The helper drives the distro package manager, which needs the real system — no sandboxing
|
||||
# directives here on purpose; the constraint is the FIXED ExecStart (no parameters exist).
|
||||
Reference in New Issue
Block a user