fix(l4d2-web): write sandbox script tmpfile under LEFT4ME_ROOT, not /tmp
The web service unit has PrivateTmp=yes: its /tmp is a per-instance
namespace at /tmp/systemd-private-X-left4me-web.service-Y/tmp/ from
PID 1's perspective. When ScriptBuilder writes /tmp/tmpXXX.sh and
passes that path to the sandbox helper, systemd-run asks PID 1 to set
up BindReadOnlyPaths=${SCRIPT}:/script.sh — but PID 1 lives in the host
namespace and can't resolve the web service's PrivateTmp path. The
unit fails to start with status=226/NAMESPACE and "Failed to set up
mount namespacing: /script.sh: No such file or directory".
Move the tmpfile to ${LEFT4ME_ROOT}/sandbox-scripts/. /var/lib is not
affected by PrivateTmp (only /tmp and /var/tmp are), so PID 1 can
resolve the path. The web service has ReadWritePaths=/var/lib/left4me
already, and the directory is created on demand by Python.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
023cc5c9b0
commit
406f2196f8
1 changed files with 17 additions and 1 deletions
|
|
@ -31,6 +31,18 @@ SCRIPT_SANDBOX_HELPER = "/usr/local/libexec/left4me/left4me-script-sandbox"
|
||||||
DISK_BUDGET_BYTES = 20 * 1024**3
|
DISK_BUDGET_BYTES = 20 * 1024**3
|
||||||
|
|
||||||
|
|
||||||
|
def _sandbox_script_dir() -> Path:
|
||||||
|
"""Where script tmpfiles live before being bind-mounted into the sandbox.
|
||||||
|
|
||||||
|
Cannot live in /tmp because the web service unit has PrivateTmp=yes:
|
||||||
|
its /tmp is a per-instance namespace that PID 1 (which actually performs
|
||||||
|
the BindReadOnlyPaths during sandbox setup) cannot resolve. /var/lib is
|
||||||
|
not affected by PrivateTmp and is visible to PID 1, so the bind-mount
|
||||||
|
succeeds.
|
||||||
|
"""
|
||||||
|
return get_left4me_root() / "sandbox-scripts"
|
||||||
|
|
||||||
|
|
||||||
class BuildError(RuntimeError):
|
class BuildError(RuntimeError):
|
||||||
"""Raised by builders when a build fails for a builder-specific reason
|
"""Raised by builders when a build fails for a builder-specific reason
|
||||||
(e.g. disk-budget exceeded). Distinct from subprocess-level
|
(e.g. disk-budget exceeded). Distinct from subprocess-level
|
||||||
|
|
@ -189,7 +201,11 @@ def run_sandboxed_script(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Write `script_text` to a tmpfile and exec it inside the privileged
|
"""Write `script_text` to a tmpfile and exec it inside the privileged
|
||||||
sandbox helper. Used by ScriptBuilder.build and by the wipe route."""
|
sandbox helper. Used by ScriptBuilder.build and by the wipe route."""
|
||||||
with tempfile.NamedTemporaryFile("w", suffix=".sh", delete=False) as f:
|
script_dir = _sandbox_script_dir()
|
||||||
|
script_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
"w", suffix=".sh", delete=False, dir=str(script_dir)
|
||||||
|
) as f:
|
||||||
f.write(script_text or "")
|
f.write(script_text or "")
|
||||||
script_path = f.name
|
script_path = f.name
|
||||||
# NamedTemporaryFile creates 0600 owned by the web user; the sandbox runs
|
# NamedTemporaryFile creates 0600 owned by the web user; the sandbox runs
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue