fix(packaging): create the punktfunk group everywhere the udev rule needs it
ci / bun-nix (pull_request) Successful in 29s
ci / docs-site (pull_request) Successful in 1m37s
ci / web (pull_request) Successful in 2m39s
ci / rust-arm64 (pull_request) Successful in 4m10s
ci / rust (pull_request) Successful in 6m50s
nix / flake (pull_request) Failing after 23m28s
ci / bun-nix (pull_request) Successful in 29s
ci / docs-site (pull_request) Successful in 1m37s
ci / web (pull_request) Successful in 2m39s
ci / rust-arm64 (pull_request) Successful in 4m10s
ci / rust (pull_request) Successful in 6m50s
nix / flake (pull_request) Failing after 23m28s
60-punktfunk.rules chgrp's the usbip vhci attach/detach nodes to a dedicated
`punktfunk` group (security-review 2026-08-05 M-4: writing `attach` materialises
an arbitrary emulated USB device, so it must not ride on `input`). Four of the
six install paths shipped that rule in 0.25.0 without ever creating the group.
chgrp then failed, the nodes stayed root:root 0644, and the virtual Steam Deck
pad silently never attached — while `usermod -aG punktfunk` failed outright with
"group 'punktfunk' does not exist".
Affected and fixed:
* arch — post_upgrade() called only _ensure_update_group, so every box that
reached 0.25.0 by `pacman -Syu` missed it; post_install was correct.
* nix — no users.groups.punktfunk at all, though host.users' own description
already promised the usbip/vhci pad. Declares it now and adds
host.users to both groups.
* bazzite sysext — a group is host state and cannot ride an image, and the
deb/rpm scriptlets that would create it never run there.
* steamdeck install.sh/update.sh — handled `input` only. Both now create the
group and join it: running that script IS the statement "make my
Deck a host with native pad passthrough".
deb and rpm were correct throughout (one postinst/%post for install + upgrade).
Also on the Deck path: web.env secret hygiene. install.sh's `chmod 600` sat
inside the create-only branch despite a comment calling it "the idempotent belt
for a pre-existing file", and update.sh never touched the config dir at all — so
an install set up once and only updated since kept web.env world-readable
(0644) with the console password and session secret in it. Both scripts now
harden ~/.config/punktfunk to 0700 and web.env to 0600 on every run, and say so
loudly, because a chmod does not un-leak an already-readable secret: the
password still needs rotating.
Both group blocks are `if ensure_group ...` rather than `ensure_group || true`:
a failed groupadd must not fall through to a usermod against a nonexistent
group, which under `set -e` aborted install.sh after the long build and
update.sh before the service restart (verified: exit 6, no restart).
Docs: the group is now documented where people actually look — the per-distro
guides, install.md, steamos-host.md, a new troubleshooting entry for "pad
arrives as an Xbox 360 controller", and the uninstall pages. The 0.25.0 notes
gain the "group does not exist" caveat and turn the password bullet from
"consider rotating" into a real instruction, and CHANGELOG records the known
issue against the breaking change that introduced it.
Verified: bash -n on all four scripts; the arch scriptlet's post_upgrade driven
in a container (creates the group, idempotent on re-run); the ensure_group
helper and both membership branches, including a control that reproduces the
original bug (chgrp to a missing group leaves the node root:root 0644); the
find -perm /0077 probe across 0644/0640/0604/0600/0400 on GNU findutils;
`nix flake check --no-build` (the exact CI gate) and a NixOS eval showing
alice.extraGroups == ["input","punktfunk"]; docs-site build + typecheck.
This commit is contained in:
@@ -85,7 +85,13 @@ default `pf2`), `PUNKTFUNK_MGMT_PORT` (47990), `PUNKTFUNK_WEB_PORT` (47992).
|
||||
- **System tuning (sudo):** `/etc/sysctl.d/99-punktfunk-net.conf` (32 MB UDP buffers — the #1
|
||||
high-bitrate lever), `/etc/udev/rules.d/60-punktfunk.rules` (`uinput`/`uhid` access),
|
||||
`/etc/modules-load.d/punktfunk.conf` (`vhci-hcd` for the native Deck pad), `$USER` in the `input`
|
||||
group — and `/etc/atomic-update.conf.d/punktfunk.conf`, which registers the three files on
|
||||
group **and in `punktfunk`** — the latter created here if missing, because the udev rule
|
||||
`chgrp`s the vhci `attach`/`detach` nodes to it and a rule that names a nonexistent group fails
|
||||
silently, leaving the native Deck pad unable to attach (the deb/rpm/arch scriptlets `groupadd` it;
|
||||
nothing on this path did until now). It is separate from `input` on purpose: writing `attach`
|
||||
materialises an arbitrary emulated USB device (security-review 2026-08-05 M-4). Drop it with
|
||||
`sudo gpasswd -d "$USER" punktfunk` if you would rather stream without that pad.
|
||||
Plus `/etc/atomic-update.conf.d/punktfunk.conf`, which registers the three files on
|
||||
SteamOS's atomic-update keep list so A/B OS updates carry them over (verified: without it an
|
||||
update silently strips them — pads degrade to Xbox 360, buffers drop to 208 KB).
|
||||
|
||||
@@ -104,8 +110,13 @@ host advertises over mDNS as `_punktfunk._udp`, so clients discover it automatic
|
||||
|
||||
- **distrobox required.** If missing: `curl -sfL https://raw.githubusercontent.com/89luca89/distrobox/main/install | sh -s -- --prefix ~/.local` (then ensure `~/.local/bin` is on PATH).
|
||||
- **First build is slow** (~10–15 min + ~1 GB toolchain/image). Incremental afterwards.
|
||||
- **No passwordless sudo** → the installer skips the sysctl/udev/input steps with a warning; high
|
||||
bitrates will drop packets until you apply `99-punktfunk-net.conf` and join `input` yourself.
|
||||
- **No passwordless sudo** → the installer skips the sysctl/udev/group steps with a warning; high
|
||||
bitrates will drop packets until you apply `99-punktfunk-net.conf` and join `input` (and
|
||||
`punktfunk`, for the native Deck pad) yourself. The script prints the exact commands.
|
||||
- **Installed before 0.25.0?** `web.env` was written at the ambient umask, i.e. world-readable, so
|
||||
the console password and session secret leaked to every local account. `install.sh`/`update.sh`
|
||||
now tighten `~/.config/punktfunk` to `0700` and `web.env` to `0600` on every run and say so —
|
||||
but rotate `PUNKTFUNK_UI_PASSWORD` afterwards, because a chmod does not un-leak a read secret.
|
||||
- **Game Mode auto-suspend** drops the host off the network on idle — disable it (Settings → Power)
|
||||
for a headless host.
|
||||
- **WiFi tx ceiling** ≈ 250 Mbps goodput (a Deck hardware/driver packet-rate limit, band-independent);
|
||||
|
||||
@@ -23,6 +23,13 @@ ok() { printf '\033[1;32m ok\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m !!\033[0m %s\n' "$*" >&2; }
|
||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
# Create a system group if it is missing (needs sudo). Idempotent, and mirrors what the
|
||||
# deb/rpm/arch scriptlets do — a udev rule that chgrp's to a group nobody created fails silently.
|
||||
ensure_group() {
|
||||
getent group "$1" >/dev/null 2>&1 && return 0
|
||||
sudo groupadd --system "$1" 2>/dev/null || return 1
|
||||
ok "created the '$1' system group"
|
||||
}
|
||||
|
||||
# --- options ---------------------------------------------------------------
|
||||
SRC="${PUNKTFUNK_SRC:-$HOME/punktfunk}"
|
||||
@@ -252,8 +259,21 @@ EOF
|
||||
)
|
||||
chmod 600 "$CONFIG/web.env"
|
||||
ok "wrote web.env (generated login password)"
|
||||
else
|
||||
[ "$WITH_WEB" = 1 ] && ok "web.env exists (login password unchanged)"
|
||||
elif [ "$WITH_WEB" = 1 ] && [ -f "$CONFIG/web.env" ]; then
|
||||
# THE belt the comment above promises. It used to live inside the create-only branch, so it
|
||||
# only ever ran on files that had just been written 0600 anyway — every install that predates
|
||||
# the L-19 fix still has its console password and session secret on disk at the Deck's ambient
|
||||
# umask (0644, world-readable). Tighten it here, and say so out loud: a chmod does not un-leak
|
||||
# a secret that was already readable by every local account, so the password needs rotating.
|
||||
if find "$CONFIG/web.env" -maxdepth 0 -perm /0077 2>/dev/null | grep -q .; then
|
||||
chmod 600 "$CONFIG/web.env"
|
||||
warn "web.env was group/world-readable — an older install wrote it at the default umask."
|
||||
warn "Tightened to 0600, but that does NOT un-expose the password it already leaked to every"
|
||||
warn "local account. Rotate it: edit PUNKTFUNK_UI_PASSWORD in $CONFIG/web.env, then"
|
||||
warn " systemctl --user restart punktfunk-web"
|
||||
else
|
||||
ok "web.env exists (login password unchanged, mode already 0600)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 3b. HDR gamescope (punktfunk-gamescope, best-effort) ------------------
|
||||
@@ -264,8 +284,8 @@ fi
|
||||
# host.env only while the binary provably runs on SteamOS.
|
||||
PUNKTFUNK_SRC="$SRC" PUNKTFUNK_BOX="$BOX" bash "$SRC/scripts/steamdeck/build-gamescope.sh"
|
||||
|
||||
# --- 4. system tuning (needs sudo: UDP buffers + gamepad udev rule + vhci-hcd + input group) --------
|
||||
log "System tuning (UDP buffers + gamepad rules + vhci-hcd + input group)"
|
||||
# --- 4. system tuning (needs sudo: UDP buffers + udev rule + vhci-hcd + input/punktfunk groups) -----
|
||||
log "System tuning (UDP buffers + gamepad rules + vhci-hcd + input/punktfunk groups)"
|
||||
# sudo was acquired up front in preflight (SUDO_OK) so this never stalls behind the long build; a
|
||||
# skip here (no password / no TTY) was already reported loudly there.
|
||||
if [ "$SUDO_OK" = 1 ]; then
|
||||
@@ -293,6 +313,34 @@ if [ "$SUDO_OK" = 1 ]; then
|
||||
NEED_RELOGIN=1
|
||||
warn "added $USER to the 'input' group (applies on next login)"
|
||||
fi
|
||||
# The 'punktfunk' group owns the usbip vhci attach/detach nodes (see 60-punktfunk.rules).
|
||||
# Deliberately NOT 'input': writing 'attach' hands the kernel a caller-supplied socket fd and
|
||||
# materialises an arbitrary emulated USB device — a root-only primitive that must not ride on
|
||||
# the group every gamepad guide tells you to join (security-review 2026-08-05 M-4).
|
||||
#
|
||||
# The deb/rpm/arch scriptlets groupadd this; NOTHING on the Deck path did. So the rule we just
|
||||
# installed ran `chgrp punktfunk` against a group that did not exist, the chgrp failed, the
|
||||
# attach/detach files stayed root-only, and the native Steam Deck pad never attached — with no
|
||||
# error anywhere the user would look. Create it and join it here: unlike a general-purpose
|
||||
# host, running THIS script IS the statement "make my Deck a host with native pad passthrough".
|
||||
# `if ensure_group` (not `ensure_group || true`): a failed groupadd must not fall through to a
|
||||
# usermod against a group that does not exist, which under `set -e` would kill the installer
|
||||
# here — after the long build and before the services are installed.
|
||||
if ensure_group punktfunk; then
|
||||
if id -nG "$USER" | grep -qw punktfunk; then
|
||||
ok "already in the 'punktfunk' group (usbip vhci access)"
|
||||
else
|
||||
sudo usermod -aG punktfunk "$USER"
|
||||
NEED_RELOGIN=1
|
||||
warn "added $USER to the 'punktfunk' group — the native Steam Deck pad needs it. That group"
|
||||
warn "can emulate arbitrary USB devices; drop it with 'sudo gpasswd -d $USER punktfunk' if"
|
||||
warn "you would rather stream without the native pad."
|
||||
fi
|
||||
else
|
||||
warn "could not create the 'punktfunk' group — the native Steam Deck pad will not attach"
|
||||
warn "(everything else works; the pad arrives as a generic Xbox 360 controller). By hand:"
|
||||
warn " sudo groupadd --system punktfunk; sudo usermod -aG punktfunk $USER"
|
||||
fi
|
||||
# SteamOS A/B updates rebuild /etc and DROP everything not on Valve's keep list — verified
|
||||
# live: an OS update stripped the udev rule + vhci autoload + UDP sysctl (gamepads silently
|
||||
# degrade to Xbox 360, buffers back to 208 KB). The sanctioned fix is a preserve drop-in in
|
||||
@@ -303,15 +351,18 @@ if [ "$SUDO_OK" = 1 ]; then
|
||||
fi
|
||||
else
|
||||
warn "no usable sudo — SKIPPED system tuning. Gamepad passthrough + clean streaming need root (udev"
|
||||
warn "rule, 'input' group, vhci-hcd, UDP buffers) — there is no user-space way to do these."
|
||||
warn "rule, 'input' + 'punktfunk' groups, vhci-hcd, UDP buffers) — there is no user-space way to do these."
|
||||
warn "A stock SteamOS 'deck' account has NO password, so sudo can't work until you set one:"
|
||||
warn " passwd # set a sudo password once, then re-run this script"
|
||||
warn "Or apply it by hand (then reboot):"
|
||||
warn " sudo install -m644 $SRC/scripts/60-punktfunk.rules /etc/udev/rules.d/ &&"
|
||||
warn " sudo install -m644 $SRC/scripts/punktfunk-modules.conf /etc/modules-load.d/punktfunk.conf &&"
|
||||
warn " sudo usermod -aG input $USER &&"
|
||||
warn " sudo groupadd --system punktfunk;"
|
||||
warn " sudo usermod -aG input,punktfunk $USER &&"
|
||||
warn " printf 'net.core.wmem_max=33554432\\nnet.core.rmem_max=33554432\\n' | sudo tee /etc/sysctl.d/99-punktfunk-net.conf &&"
|
||||
warn " sudo sysctl --system && sudo udevadm control --reload-rules && sudo udevadm trigger"
|
||||
warn "('punktfunk' owns the usbip vhci nodes the native Steam Deck pad attaches through — without"
|
||||
warn " it the pad silently never appears. Omit it if you do not want that pad.)"
|
||||
fi
|
||||
|
||||
# --- 5. systemd user services ---------------------------------------------
|
||||
|
||||
@@ -12,6 +12,13 @@ ok() { printf '\033[1;32m ok\033[0m %s\n' "$*"; }
|
||||
# found") aborted the whole update before the service restarts.
|
||||
warn() { printf '\033[1;33m !!\033[0m %s\n' "$*" >&2; }
|
||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
# Create a system group if it is missing (needs sudo). Idempotent, and mirrors what the
|
||||
# deb/rpm/arch scriptlets do — a udev rule that chgrp's to a group nobody created fails silently.
|
||||
ensure_group() {
|
||||
getent group "$1" >/dev/null 2>&1 && return 0
|
||||
sudo groupadd --system "$1" 2>/dev/null || return 1
|
||||
ok "created the '$1' system group"
|
||||
}
|
||||
|
||||
SRC="${PUNKTFUNK_SRC:-$HOME/punktfunk}"
|
||||
BOX="${PUNKTFUNK_BOX:-pf2}"
|
||||
@@ -81,11 +88,27 @@ EOF
|
||||
ok "punktfunk-rebuild-check.service installed (auto-rebuild after SteamOS updates)"
|
||||
fi
|
||||
|
||||
CONFIG="$HOME/.config/punktfunk"
|
||||
|
||||
# Secret hygiene, retrofitted. install.sh §3 does this for fresh installs — but only install.sh
|
||||
# ever did, so a Deck that was set up once and only ever *updated* since kept the old modes
|
||||
# forever. This directory holds web.env (console login password + session secret), the mgmt token
|
||||
# and the host key; a plain `mkdir -p` left it 0755 at the Deck's ambient umask and web.env itself
|
||||
# 0644, i.e. readable by every local account (2026-08-05 review L-19). Both chmods are idempotent.
|
||||
[ -d "$CONFIG" ] && chmod 700 "$CONFIG" 2>/dev/null || true
|
||||
if [ -f "$CONFIG/web.env" ] && find "$CONFIG/web.env" -maxdepth 0 -perm /0077 2>/dev/null | grep -q .; then
|
||||
chmod 600 "$CONFIG/web.env"
|
||||
warn "web.env was group/world-readable — an older install wrote it at the default umask."
|
||||
warn "Tightened to 0600, but that does NOT un-expose the password it already leaked to every"
|
||||
warn "local account. Rotate it: edit PUNKTFUNK_UI_PASSWORD in $CONFIG/web.env, then"
|
||||
warn " systemctl --user restart punktfunk-web"
|
||||
fi
|
||||
|
||||
# Retrofit config that install.sh now writes but older installs predate (both idempotent):
|
||||
# RADV_PERFTEST — Van Gogh RADV still gates VK_KHR_video_encode_* behind it; without it the
|
||||
# Vulkan backend can't open and sessions silently fall back to libav VAAPI. The KWin .desktop —
|
||||
# KWin only grants the restricted capture/input globals to the exe a .desktop authorizes.
|
||||
HOST_ENV="$HOME/.config/punktfunk/host.env"
|
||||
HOST_ENV="$CONFIG/host.env"
|
||||
if [ -f "$HOST_ENV" ] && ! grep -q '^RADV_PERFTEST=' "$HOST_ENV"; then
|
||||
printf '\n# Van Gogh RADV gates VK_KHR_video_encode_* behind this (Vulkan Video encode).\nRADV_PERFTEST=video_encode\n' >> "$HOST_ENV"
|
||||
ok "host.env: added RADV_PERFTEST=video_encode"
|
||||
@@ -128,6 +151,25 @@ if [ "$SUDO_OK" = 1 ]; then
|
||||
sudo usermod -aG input "$USER"
|
||||
warn "added $USER to the 'input' group — REBOOT (or log out/in) for it to apply"
|
||||
fi
|
||||
# 'punktfunk' owns the usbip vhci attach/detach nodes (60-punktfunk.rules), deliberately NOT
|
||||
# 'input' — writing 'attach' materialises an arbitrary emulated USB device, a root-only kernel
|
||||
# primitive that must not ride on the group every gamepad guide tells you to join
|
||||
# (security-review 2026-08-05 M-4). No Deck install ever created it, so the rule's chgrp failed
|
||||
# and the native Steam Deck pad silently never attached. Retrofit both group and membership.
|
||||
# `if ensure_group` (not `ensure_group || true`): a failed groupadd must not fall through to a
|
||||
# usermod against a group that does not exist — under `set -e` that would abort the update
|
||||
# before the service restarts at the bottom, leaving the host down.
|
||||
if ensure_group punktfunk; then
|
||||
if id -nG "$USER" | grep -qw punktfunk; then :; else
|
||||
sudo usermod -aG punktfunk "$USER"
|
||||
warn "added $USER to the 'punktfunk' group (usbip vhci — the native Steam Deck pad needs it)"
|
||||
warn " — REBOOT (or log out/in) for it to apply. That group can emulate arbitrary USB"
|
||||
warn " devices; 'sudo gpasswd -d $USER punktfunk' drops it if you do not want the native pad."
|
||||
fi
|
||||
else
|
||||
warn "could not create the 'punktfunk' group — the native Steam Deck pad will not attach."
|
||||
warn "By hand: sudo groupadd --system punktfunk; sudo usermod -aG punktfunk $USER"
|
||||
fi
|
||||
# Register the tuning on Valve's atomic-update preserve list (see install.sh §4): without
|
||||
# this, every SteamOS A/B update strips the three files above again (verified live —
|
||||
# gamepads silently degrade to Xbox 360, UDP buffers back to 208 KB).
|
||||
|
||||
Reference in New Issue
Block a user