From 75e703e1a473a25ce456802c637b05264863bda5 Mon Sep 17 00:00:00 2001 From: mwiegand Date: Fri, 8 May 2026 15:53:21 +0200 Subject: [PATCH] feat(deploy): left4me-script-sandbox helper + sudoers fragment Privileged bash helper that wraps user-authored scripts in systemd-run --scope (cgroup limits + RuntimeMaxSec=3600) inside a bubblewrap sandbox dropped to the l4d2-sandbox uid. Network is shared with the host so scripts can fetch from Steam / l4d2center / etc.; filesystem is RO except for /overlay (rw bind from /var/lib/left4me/overlays/{id}) and tmpfs /tmp + /run. Adds a sudoers rule allowing the left4me user to invoke this helper without restrictions on its arguments. Strict argument validation is in the helper itself. Co-Authored-By: Claude Opus 4.7 (1M context) --- deploy/files/etc/sudoers.d/left4me | 1 + .../libexec/left4me/left4me-script-sandbox | 55 ++++++++++++++++++ deploy/tests/test_deploy_artifacts.py | 57 +++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100755 deploy/files/usr/local/libexec/left4me/left4me-script-sandbox diff --git a/deploy/files/etc/sudoers.d/left4me b/deploy/files/etc/sudoers.d/left4me index 02cccc6..5aa94eb 100644 --- a/deploy/files/etc/sudoers.d/left4me +++ b/deploy/files/etc/sudoers.d/left4me @@ -2,3 +2,4 @@ Defaults:left4me !requiretty left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-systemctl * left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-journalctl * left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-overlay mount *, /usr/local/libexec/left4me/left4me-overlay umount * +left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-script-sandbox diff --git a/deploy/files/usr/local/libexec/left4me/left4me-script-sandbox b/deploy/files/usr/local/libexec/left4me/left4me-script-sandbox new file mode 100755 index 0000000..625f026 --- /dev/null +++ b/deploy/files/usr/local/libexec/left4me/left4me-script-sandbox @@ -0,0 +1,55 @@ +#!/bin/bash +# Privileged sandbox launcher for left4me script overlays. +# +# Invoked via sudo by the web user with two arguments: +# numeric overlay id; bind-mounts /var/lib/left4me/overlays/ +# read-write at /overlay inside the sandbox. +# absolute path to a bash file already written by the web app; +# bind-mounted read-only at /script.sh inside the sandbox. +# +# The script runs under bubblewrap inside a transient systemd scope so we get +# cgroup-v2 limits (memory / tasks / cpu) and a wallclock kill via +# RuntimeMaxSec. The sandbox drops to the unprivileged l4d2-sandbox UID; +# host filesystems are exposed read-only except /overlay (rw) and tmpfs +# /tmp + /run. Network namespace is *not* unshared — scripts must reach the +# public internet to download workshop / l4d2center / cedapug content. +set -euo pipefail + +[[ $# -eq 2 ]] || { echo "usage: $0