Skip to content

Commit 51365ef

Browse files
author
Damien
committed
feat(sandbox): enable LibreOffice in nsjail for document skills
LibreOffice (soffice) needs three things to work in nsjail that the default sandbox doesn't provide: 1. Access to its config and font discovery directories → bind-mount /etc/libreoffice, /etc/fonts, /usr/share/fonts (read-only) 2. /proc visibility — soffice hard-fails with "ERROR: /proc not mounted - LibreOffice is unlikely to work well if at all" otherwise. Extend the /proc-keeping language allowlist from {java, rs, bash} to also include {py, python}. nsjail's PID namespace still restricts /proc visibility to sandbox processes; only /proc/cpuinfo and /proc/meminfo leak host info, acceptable in the trusted-tenant model. 3. bind(2) syscall — LibreOffice's oosplash and soffice.bin communicate via AF_UNIX sockets. The original BUG-006c blocked bind to prevent server sockets, but network namespace isolation (--iface_no_lo) already prevents external connections, so AF_UNIX bind is safe. Extend the allowlist from {bash} to {py, python, java, bash}. Same /proc handling applied to pool.py (Python REPL) and programmatic.py (PTC) since they also drive document-processing skills. Add XDG_CONFIG_HOME=/tmp/.config to the Python env so LibreOffice can write its first-run profile to a writable location.
1 parent c4573c0 commit 51365ef

5 files changed

Lines changed: 48 additions & 14 deletions

File tree

docker/nsjail-base.cfg

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,32 @@ mount {
211211
mandatory: false
212212
}
213213

214+
# LibreOffice runtime configuration (required by soffice headless)
215+
mount {
216+
src: "/etc/libreoffice"
217+
dst: "/etc/libreoffice"
218+
is_bind: true
219+
rw: false
220+
mandatory: false
221+
}
222+
223+
# Font configuration and font files (required for document rendering)
224+
mount {
225+
src: "/etc/fonts"
226+
dst: "/etc/fonts"
227+
is_bind: true
228+
rw: false
229+
mandatory: false
230+
}
231+
232+
mount {
233+
src: "/usr/share/fonts"
234+
dst: "/usr/share/fonts"
235+
is_bind: true
236+
rw: false
237+
mandatory: false
238+
}
239+
214240
# Writable tmpfs for temporary files
215241
mount {
216242
dst: "/tmp"

src/services/programmatic.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,10 @@ async def start_execution(
198198
f"mount -t tmpfs -o size=1k tmpfs /app/ssl && "
199199
f"mount -t tmpfs -o size=1k tmpfs /app/dashboard && "
200200
f"mount -t tmpfs -o size=1k tmpfs /app/src && "
201-
f"mount --bind /var/lib/code-interpreter/empty_proc /proc && "
201+
# /proc kept accessible: PTC may invoke LibreOffice (soffice)
202+
# for document-processing skills. soffice hard-fails without
203+
# /proc. PID namespace inside nsjail still restricts visibility
204+
# to sandbox processes.
202205
# BUG-007: Ephemeral /tmp with noexec,nosuid,nodev
203206
f"mount -t tmpfs -o {noexec_tmpfs}size={tmpfs_size}m,mode=1777 tmpfs /tmp && "
204207
# BUG-008: Lock down other writable paths

src/services/sandbox/executor.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,17 @@ async def execute_command(
9090
# Some languages need /proc to function:
9191
# - Java needs /proc/self/exe to locate libjli.so.
9292
# - Rust needs /proc/self/exe to locate its own binary path.
93-
# - Bash sandboxes are the typical entry point for skills (e.g.,
94-
# the Anthropic pptx/docx/xlsx skills) that shell out to
95-
# LibreOffice (`soffice`) for PDF/image conversion. soffice
96-
# hard-fails with "ERROR: /proc not mounted - LibreOffice is
97-
# unlikely to work well if at all" without /proc.
93+
# - Python and Bash are typical entry points for skills that
94+
# shell out to LibreOffice (`soffice`) for document conversion
95+
# (DOCX/XLSX/PPTX → PDF). soffice hard-fails with "ERROR:
96+
# /proc not mounted - LibreOffice is unlikely to work well if
97+
# at all" without /proc.
9898
# nsjail still creates a separate PID namespace so the visible
9999
# /proc is restricted to the sandbox's own processes — main host
100100
# info disclosure risk is /proc/cpuinfo and /proc/meminfo, which
101101
# is acceptable in the trusted-tenant model these languages run in.
102102
lang = sandbox_info.language.lower().strip()
103-
if lang in ("java", "rs", "bash"):
103+
if lang in ("java", "rs", "py", "python", "bash"):
104104
proc_mask = ""
105105
else:
106106
proc_mask = (
@@ -212,6 +212,7 @@ def _build_sanitized_env(self, language: Optional[str]) -> Dict[str, str]:
212212
"PYTHONPATH": f"{deps_root}/python:/mnt/data",
213213
"MPLCONFIGDIR": "/tmp/mplconfig",
214214
"XDG_CACHE_HOME": "/tmp/.cache",
215+
"XDG_CONFIG_HOME": "/tmp/.config",
215216
"MPLBACKEND": "Agg",
216217
}
217218
)

src/services/sandbox/nsjail.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,14 @@ def build_args(
203203
# Seccomp policy: block dangerous syscalls
204204
# - ptrace: prevents process inspection/debugging (BUG-006a)
205205
# - bind: was originally blocked to prevent server sockets even with
206-
# network access (BUG-006c), but bash sandboxes need it for tools
207-
# like LibreOffice which use AF_UNIX sockets internally for IPC.
208-
# Bash has the looser sandboxing model (also gets /proc), so allow
209-
# bind there. For other languages, keep blocking.
206+
# network access (BUG-006c), but the languages that drive
207+
# document-processing skills (Python, Java, Bash) need it for tools
208+
# like LibreOffice which use AF_UNIX sockets internally for IPC
209+
# between oosplash and soffice.bin.
210+
# Network namespace isolation (--iface_no_lo) already prevents
211+
# external connections, so allowing bind on AF_UNIX is safe.
210212
# Using ERRNO(1) so the process gets EPERM rather than SIGSYS
211-
if normalized_lang == "bash":
213+
if normalized_lang in ("py", "python", "java", "bash"):
212214
seccomp_policy = (
213215
"POLICY policy { ERRNO(1) { ptrace } } USE policy DEFAULT ALLOW"
214216
)

src/services/sandbox/pool.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,10 @@ async def _start_repl_process(
407407
f"mount -t tmpfs -o size=1k tmpfs /app/ssl && "
408408
f"mount -t tmpfs -o size=1k tmpfs /app/dashboard && "
409409
f"mount -t tmpfs -o size=1k tmpfs /app/src && "
410-
# BUG-003: Hide /proc (REPL is Python-only, always safe to mask)
411-
f"mount --bind /var/lib/code-interpreter/empty_proc /proc && "
410+
# /proc kept accessible: Python REPL may invoke LibreOffice
411+
# (soffice) for document-processing skills. soffice hard-fails
412+
# without /proc. PID namespace inside nsjail still restricts
413+
# visibility to sandbox processes.
412414
# BUG-007: Ephemeral /tmp with noexec,nosuid,nodev
413415
f"mount -t tmpfs -o {noexec_tmpfs}size={tmpfs_size}m,mode=1777 tmpfs /tmp && "
414416
# BUG-008: Lock down other writable paths

0 commit comments

Comments
 (0)