767e67ca's per-channel mechanics were correct; they were aimed at the wrong binary. Each one is restored here pointed at punktfunk-encode-worker, and every host-side removal from #136 stays verbatim. All grants remain best-effort — an uncapped worker still encodes, at default priority, so a failed setcap must never fail an install. * Arch: setcap in post_install AND post_upgrade (a replaced binary is a new inode). * RPM: %caps(cap_sys_nice=ep) in %files, never a %post setcap — %caps applies, restores and verifies, and covers Fedora as well as Bazzite via rpm-ostree layering. * Bazzite + Arch sysext: setcap on the staging tree before mksquashfs, which does record security.capability. The assertion is amended, not removed: host EMPTY is still a hard fail, and the worker must carry exactly cap_sys_nice=ep — missing is fine, anything else is not. * deb: setcap in postinst. * NixOS: security.wrappers for the WORKER plus PUNKTFUNK_ENCODE_WORKER in the unit. A file capability cannot live on a store path, and an ambient grant is right here precisely because nothing ever identifies the worker. The host's ExecStart stays on the store path. * Steam Deck: setcap the worker; the .desktop the script writes stays valid this time. Four things the plan's channel table missed: * packaging/arch/build-sysext.sh had no capability handling at all, and a sysext can never run a pacman scriptlet — the SteamOS image would have shipped the lever permanently inert. * scripts/steamdeck/update.sh had none either. It rebuilds both binaries, so a new inode drops the grant, and it is the documented steady-state path: the lever would have died on the first update. It also never healed a Deck already capped by 0.26.0-1. * A capped worker is AT_SECURE, and glibc drops $ORIGIN-expanded RPATH entries for secure binaries unless they normalise into a trusted system dir. Copying the host's rpath under BUNDLE_FFMPEG=1 would have left the capped worker unable to find libavcodec on exactly the channel that bundles it. Absolute DT_RPATH instead. * Nix crane scopes by -p, so the worker would not have been built at all, and it needs its own addDriverRunpath. scripts/ci/assert-cap-matrix.sh mechanizes the lesson from 0.26.0-1 — verify the PACKAGE, never the board. It unpacks the built Arch package, the deb, the rpm and the mounted sysext raw and asserts one matrix: the host carries NOTHING (hard fail), the worker exactly cap_sys_nice=ep. The sysext reader first proves it can round-trip a capability through mksquashfs/unsquashfs at all, so an unreadable artifact fails rather than issuing a blind PASS, and --self-test red-teams the assertions themselves. Red-teaming the leg found a real bug: setcap originally ran BEFORE the assertion, so "the worker arrived carrying something unexpected" was unreachable and a stray %caps would have been silently overwritten. Both sysext scripts now assert, then grant, then assert again.
68 lines
4.1 KiB
Docker
68 lines
4.1 KiB
Docker
# bootc / OCI image layer that bakes punktfunk into a Bazzite-based atomic image.
|
|
#
|
|
# Bazzite is already a bootc image (Fedora Atomic + gamescope + PipeWire + the NVIDIA
|
|
# stack), so we layer punktfunk on top: enable RPM Fusion (for the NVENC ffmpeg) and unom's
|
|
# Gitea RPM registry (NOT COPR — only the registry carries the punktfunk-web subpackage),
|
|
# install the packages, and pre-enable the udev rule. Build + push this image, then
|
|
# `bootc switch` (or rebase) a Bazzite host onto it for an image-based, atomic install —
|
|
# no per-host `rpm-ostree install` drift.
|
|
#
|
|
# podman build -t ghcr.io/<you>/bazzite-punktfunk -f packaging/bootc/Containerfile .
|
|
# podman push ghcr.io/<you>/bazzite-punktfunk
|
|
# # on the target Bazzite host:
|
|
# sudo bootc switch ghcr.io/<you>/bazzite-punktfunk # then reboot
|
|
#
|
|
# Pick the base tag that matches your hardware (NVIDIA shown). See ublue-os/bazzite tags.
|
|
ARG BASE_IMAGE=ghcr.io/ublue-os/bazzite-nvidia:stable
|
|
FROM ${BASE_IMAGE}
|
|
|
|
# punktfunk's RPMs come from unom's Gitea RPM registry (the recommended path — see
|
|
# packaging/rpm/README). Use it rather than COPR specifically because it carries the
|
|
# punktfunk-web management console subpackage, which COPR's mock chroot can't build (no `bun`).
|
|
# Group "bazzite" == the Fedora 43 base; override for a different base. Gitea signs the repo
|
|
# metadata (repo_gpgcheck=1) and the packages are GPG-signed (gpgcheck=1, the packages@unom.io key).
|
|
ARG PUNKTFUNK_RPM_GROUP=bazzite
|
|
|
|
# RPM Fusion nonfree provides the NVENC-capable ffmpeg-libs punktfunk records/encodes with.
|
|
# (Bazzite usually has RPM Fusion enabled already; this is belt-and-suspenders.)
|
|
RUN dnf5 -y install \
|
|
https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \
|
|
https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm \
|
|
|| true
|
|
|
|
# Add the Gitea RPM repo and install the host + the web console (punktfunk-web pulls nodejs).
|
|
RUN printf '%s\n' \
|
|
'[gitea-unom-punktfunk]' \
|
|
'name=punktfunk (unom)' \
|
|
"baseurl=https://git.unom.io/api/packages/unom/rpm/${PUNKTFUNK_RPM_GROUP}" \
|
|
'enabled=1' 'gpgcheck=1' 'repo_gpgcheck=1' \
|
|
'gpgkey=https://git.unom.io/api/packages/unom/rpm/repository.key https://git.unom.io/api/packages/unom/generic/punktfunk-keys/1/RPM-GPG-KEY-punktfunk' \
|
|
> /etc/yum.repos.d/punktfunk.repo \
|
|
&& dnf5 -y install punktfunk punktfunk-web \
|
|
&& dnf5 clean all
|
|
|
|
# The udev rule + systemd *user* units ship in the RPMs; nothing else to enable at image build
|
|
# time (host + console run per-user in the graphical session, enabled after first boot with
|
|
# `systemctl --user enable --now punktfunk-host punktfunk-web`).
|
|
|
|
# NO `setcap` here — deliberately, in BOTH directions, and this file must stay that way.
|
|
#
|
|
# /usr/bin/punktfunk-host must carry NO capability. A capability-carrying process
|
|
# cannot have its /proc/<pid>/exe read, so KWin cannot identify
|
|
# it, never advertises zkde_screencast_unstable_v1, and every
|
|
# KDE desktop session dies. That is the 0.26.0-1 incident; a
|
|
# layered/bootc image is as unrepairable in place as a sysext.
|
|
# /usr/bin/punktfunk-encode-worker carries cap_sys_nice=ep, declared with %caps in
|
|
# packaging/rpm/punktfunk.spec. rpm applies file capabilities
|
|
# from package metadata during the dnf5 install above and the
|
|
# ostree commit preserves the security.capability xattr — so it
|
|
# arrives correctly without anything to do here, and the
|
|
# capability matrix is asserted on the .rpm in CI
|
|
# (scripts/ci/assert-cap-matrix.sh, .gitea/workflows/rpm.yml).
|
|
#
|
|
# If a capability is ever wanted in this image, change the SPEC, never this file: a setcap here
|
|
# would apply to one channel and drift from the other four.
|
|
|
|
# bootc image hygiene: the container build must leave a clean ostree commit.
|
|
RUN ostree container commit
|