radio: run liquidsoap as dedicated non-root user; quiet pulse warnings

- Liquidsoap refuses to start as root (init: security exit). Instead of
  the settings.init.allow_root override, the image now has a dedicated
  'radio' user (audio + pulse-access groups) and the entrypoint drops
  privileges via setpriv after doing its root-only work (volume seeding,
  icecast config render/start, pulse start, chown of the data volume).
  The icecast secret is made group-readable (640 root:radio) because the
  script parses it directly.
- The opam root moved from /root/.opam to /opt/opam so the liquidsoap
  binary AND its stdlib .liq files are readable by the radio user.
  NOTE: this invalidates the cached opam build layer - next build
  recompiles liquidsoap (~15-20 min).
- pulseaudio (PULSE_MODE=internal) now starts with
  --disallow-module-loading: system.pa startup modules still load, only
  later client-requested loads are blocked, and the system-mode warning
  goes away. The 'forcibly disabling SHM mode' notice is inherent to
  system mode and harmless (documented).

Keeps the user's libcurl4-gnutls-dev build-dep fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michal Tuszowski
2026-07-08 17:14:18 +02:00
committed by Michał Tuszowski
parent fae9c76ede
commit f17cf7fdd8
3 changed files with 41 additions and 9 deletions
+13 -1
View File
@@ -272,12 +272,24 @@ Wire-up: listeners tune to `http://RADIO_VM_IP:8000/mp3-stream`; the bot
points at `CONJURER_RADIO_HARBOR=http://RADIO_VM_IP:54321` (harbor `/skip`
lives in the script itself).
**Privileges:** Liquidsoap refuses to run as root (`init: security exit`),
so the main process runs as the dedicated **`radio`** user (member of
`audio`/`pulse-access`) — no `settings.init.allow_root` override. The
entrypoint (root) seeds volumes, renders the icecast config, starts
icecast/pulse, chowns `/srv/betoniarka/data` to `radio` and drops
privileges via `setpriv`. This is also why the opam switch lives in
`/opt/opam` instead of `/root/.opam` (the binary and the liquidsoap stdlib
must be readable by `radio`).
**PulseAudio** (`PULSE_MODE` env):
- `internal` (default) — a system-wide pulse daemon runs inside the container
with a **null sink** (`docker/pulse-system.pa`): no sound hardware needed,
`output.pulseaudio()` plays into the void and the `input.pulseaudio()` mic
path reads silence (which `blank.strip` already gates out). Right choice
for a headless Proxmox VM.
for a headless Proxmox VM. Started with `--disallow-module-loading`
(startup modules from `system.pa` still load; only later client-requested
loads are blocked). The `forcibly disabling SHM mode` notice is inherent
to system mode and harmless.
- `host` — mount the host's pulse socket and set `PULSE_SERVER`, for a VM
with real audio hardware (the Pi's Lexicon Lambda setup).
- `none` — you removed the pulse in/out from the script.