Create a dedicated 'gpsd' system user at package-install time (via
USERID) and pin gpsd's built-in privilege-separation identity to it
via the SConstruct 'gpsd_user' option.
Background: gpsd needs to start as root to open the underlying tty /
serial device but then internally setuid()s to a configured
unprivileged identity for the rest of its lifetime -- a built-in
privilege-separation feature of the daemon.
The current package leaves 'gpsd_user' at scons' default 'nobody'.
Sharing 'nobody' across daemons is a hardening anti-pattern:
a compromise of any one 'nobody'-owned process can trivially
interfere with any other, and audit trails become ambiguous.
Group choice: gpsd_group is a device-access knob rather than an
identity knob -- once gpsd has setuid()d away from root it still
needs to open() tty / serial nodes, which OpenWrt exposes as
root:dialout mode 0660 (see procd hotplug.json and base-files
/etc/group). What actually keeps that access working post-drop is
the setgid() target that gpsd_group selects: gpsd calls
setgroups(0, NULL) immediately before dropping privileges, so any
supplementary groups on the target user are discarded and cannot
carry the permission.
scons' gpsd_group default is not 'nobody' but 'dialout' (uucp on
Gentoo build hosts), so on OpenWrt buildbots the daemon already
lands in 'dialout' today. Pin gpsd_group=dialout explicitly so the
resulting binary no longer depends on the builder's /etc/gentoo-release:
a Gentoo builder would otherwise bake in 'uucp', a group base-files
does not ship, and gpsd's getgrnam() fallback silently keeps root's
gid in that case.
USERID's third field ('dialout=20') still adds the gpsd user to the
dialout group in /etc/group. This does not affect the running
daemon (setgroups() strips it) but matches Debian's gpsd packaging
and lets an operator run gpsdctl / gpsmon as 'gpsd' by hand against
device nodes. GID 20 is the value already frozen into base-files.
Giving gpsd its own primary user:
* confines a hypothetical gpsd RCE (there have been NMEA / UBX
parsing bugs historically) to files owned by 'gpsd' rather than
to the shared 'nobody' identity;
* matches the long-standing Debian gpsd packaging convention
(adduser --system gpsd, member of dialout);
* is a no-op for correctly-configured installations that never
referenced 'nobody' as an intentional identity.
No numeric UID is pinned for the primary 'gpsd' user -- dynamic
assignment via USERID is sufficient here since no cross-distro
NFS/sudoers-style muscle memory depends on a specific number.