fix(python/robomp): ensured omp run directory remained writable for all slots

- Added entrypoint setup for `/srv/agent-home/.omp/run` to enforce `omp` group ownership, group-write access, and setgid permissions so any sandbox slot can create or enter daemon state directories.
- Updated worker startup to skip generic home normalization on `.omp/run` and added a root-only run-dir preparation pass that reasserts `omp` ownership and writable, setgid permissions before launching subprocesses.
This commit is contained in:
can1357
2026-07-14 17:56:37 +02:00
parent 3047c27c33
commit 465f463ada
2 changed files with 47 additions and 0 deletions
+11
View File
@@ -69,6 +69,17 @@ chown -R root:root /srv/agent-home || true
find /srv/agent-home -type d -exec chmod 0755 {} +
find /srv/agent-home -type f -exec chmod 0644 {} +
# omp registers daemon project presence under ~/.omp/run at startup, nesting
# per-project dirs (daemons/<hash>/clients) that any slot user must be able to
# create and enter regardless of which slot first made them: setgid + group
# omp keeps the whole tree group-writable (entrypoint umask 0002 carries into
# slot processes, so new entries stay group-writable too).
mkdir -p /srv/agent-home/.omp/run
chgrp -R omp /srv/agent-home/.omp/run
chmod -R g+rwX /srv/agent-home/.omp/run
find /srv/agent-home/.omp/run -type d -exec chmod g+s {} +
chmod 2770 /srv/agent-home/.omp/run
touch /data/robomp.sqlite
chown root:root /data/robomp.sqlite
chmod 0600 /data/robomp.sqlite
+36
View File
@@ -13,6 +13,7 @@ scheduled onto the parent loop complete (`asyncio.run_coroutine_threadsafe`).
from __future__ import annotations
import asyncio
import grp
import logging
import os
import shutil
@@ -149,6 +150,10 @@ def _stage_agent_home() -> None:
chown_to_root = os.geteuid() == 0
for root, dirs, files in os.walk(_AGENT_HOME):
root_path = Path(root)
if root_path == _AGENT_HOME / ".omp":
# ~/.omp/run is slot-writable daemon presence state, not template
# config; keep it out of the read-only normalization below.
dirs[:] = [d for d in dirs if d != "run"]
try:
root_path.chmod(0o755)
if chown_to_root:
@@ -175,6 +180,36 @@ def _stage_agent_home() -> None:
log.warning("Failed to normalize agent home file %s: %s", path, exc)
def _ensure_agent_run_dir() -> None:
"""Keep ``~/.omp/run`` writable by every sandbox slot.
omp registers daemon project presence under ``~/.omp/run`` at startup,
nesting per-project dirs (``daemons/<hash>/clients``) that any slot user
must be able to create or enter regardless of which slot made them first.
The tree stays group ``omp``, setgid, group-writable; slot subprocesses
spawn with umask 0002 so their entries inherit group write.
"""
if os.geteuid() != 0:
return
run_dir = _AGENT_HOME / ".omp" / "run"
try:
gid = grp.getgrnam("omp").gr_gid
except KeyError:
return
try:
run_dir.mkdir(parents=True, exist_ok=True)
for root, dirs, files in os.walk(run_dir):
root_path = Path(root)
os.chown(root_path, -1, gid)
root_path.chmod(0o2770)
for name in files:
file_path = root_path / name
os.chown(file_path, -1, gid)
file_path.chmod(0o660)
except OSError as exc:
log.warning("Failed to prepare agent run dir %s: %s", run_dir, exc)
def _build_extra_env(settings: Settings) -> dict[str, str]:
"""Build the env overlay passed to the omp subprocess.
@@ -184,6 +219,7 @@ def _build_extra_env(settings: Settings) -> dict[str, str]:
"""
del settings # kept for future hooks (model-specific env, etc.)
_stage_agent_home()
_ensure_agent_run_dir()
env = dict.fromkeys(_SCRUBBED_ENV_KEYS, "")
if _AGENT_HOME.is_dir():
env["HOME"] = str(_AGENT_HOME)