@@ -25,50 +25,49 @@ RUN python -m compileall -q .
2525# Copy the evaluation function to the app directory
2626COPY evaluation_function ./evaluation_function
2727
28- # Command to start the evaluation function with
29- ENV FUNCTION_COMMAND="python"
28+ # How lf_toolkit's runner launches the worker.
29+ ENV FUNCTION_COMMAND="python" \
30+ FUNCTION_ARGS="-m,evaluation_function.main"
3031
31- # Args to start the evaluation function with
32- ENV FUNCTION_ARGS="-m,evaluation_function.main"
33-
34- # The transport to use for the RPC server.
35- # stdio (not ipc): the sandboxed worker runs inside an nsjail mount namespace,
36- # so the /tmp/eval.sock IPC rendezvous shimmy would otherwise use is fragile.
37- # stdio sidesteps it; lf_toolkit sends its logs to stderr, so stdout stays
38- # clean for the RPC framing.
32+ # RPC transport: stdio, not ipc. The sandboxed worker runs in an nsjail mount
33+ # namespace where the /tmp/eval.sock IPC rendezvous is fragile; stdio sidesteps
34+ # it. lf_toolkit logs to stderr, so stdout stays clean for the RPC framing.
3935ENV FUNCTION_RPC_TRANSPORT="stdio"
4036
4137# --- Sandboxed execution of untrusted student code (shimmy + nsjail) ---
42- # Always on for this function. shimmy wraps the worker process -- and every
43- # `python` subprocess it spawns for a submission -- in an nsjail sandbox:
44- # unprivileged uid (nobody:nogroup), a minimal bind-mounted filesystem, and a
45- # seccomp syscall filter.
38+ # Always on. shimmy wraps the worker -- and every `python` subprocess it spawns
39+ # per submission -- in nsjail: run as nobody, a read-only bind-mounted rootfs,
40+ # namespace isolation. Defence in depth on top of the AST gate in
41+ # evaluation_function/security.py, which rejects unsafe imports/builtins before
42+ # any code runs.
4643#
47- # DEPENDS ON THE BASE IMAGE shipping nsjail. shimmy provides the `--sandbox`
48- # feature but not the nsjail binary; `evaluation-function-base/python` currently
49- # copies only the shimmy binary, not `/usr/sbin/nsjail` or its shared libs
50- # (libprotobuf, libnl-route-3, libcap2). Until that is fixed upstream, a build
51- # of this image has shimmy fail to start (missing /usr/sbin/nsjail).
52- # Tracking: lambda-feedback/evaluation-function-base -- add nsjail to the image.
44+ # Requires a base image that ships /usr/sbin/nsjail AND a shimmy build with the
45+ # sandbox fixes (--keep_env, PATH resolution of FUNCTION_COMMAND, --cwd fallback,
46+ # kafel seccomp, namespace toggles). With stock shimmy the worker fails to start.
5347#
54- # RUN-TIME: the container must run with --privileged (or --cap-add SYS_ADMIN)
55- # so nsjail can create its namespaces (shimmy README, "Sandboxed Execution").
48+ # Run-time: the container must run --privileged (or --cap-add SYS_ADMIN) -- nsjail
49+ # needs CAP_SYS_ADMIN for unshare(CLONE_NEWNS). Running as uid 0, nsjail's "auto"
50+ # userns handling then drops CLONE_NEWUSER (a nested userns' unprivileged gid_map
51+ # write fails); --user still drops the worker to nobody. Network stays up so
52+ # matplotlib plots can be uploaded to S3 via boto3.
5653#
57- # Network stays enabled -- matplotlib plots are uploaded to S3 via boto3.
58- # Untrusted network/filesystem use is already rejected before execution by the
59- # AST gate in evaluation_function/security.py (check_code_safety).
54+ # SANDBOX_RO_BINDS / _RW_BINDS: shimmy splits these env vars on COMMA. Bind "/"
55+ # read-only (whole rootfs -- arch-independent, where an explicit list would need
56+ # /lib64 only on x86_64, and a missing bind source is fatal to nsjail), then
57+ # re-mount /tmp read-write. Not SANDBOX_TMPFS: nsjail's tmpfs defaults to 4 MiB,
58+ # too small for matplotlib's font cache + plot output.
6059#
61- # /tmp is a read-write bind of the container's own /tmp (not SANDBOX_TMPFS):
62- # nsjail's tmpfs defaults to 4 MiB, too small for matplotlib's font cache and
63- # plot output. No CPU/memory rlimits -- the RPC worker is long-lived and shared
64- # across requests, so a cumulative RLIMIT_CPU/AS would eventually kill it;
65- # per-run wall-clock limits live in evaluation.py (_TIMEOUT).
60+ # SANDBOX_DISABLE_CLONE_NEWPID: a nested PID namespace breaks worker thread
61+ # creation on some hosts ("pthread_create ... Invalid argument"); the mount and
62+ # user namespaces still isolate the filesystem and privileges.
6663#
67- # The bind list is linux/x86_64 + Debian-specific (matches the CI/prod build
68- # platform). If the worker fails to start, drop SANDBOX_SECCOMP first.
64+ # No seccomp (nsjail has no built-in default policy; the fixed shimmy takes a
65+ # kafel policy via SANDBOX_SECCOMP_STRING / _POLICY_FILE if wanted) and no
66+ # rlimits (the RPC worker is long-lived and shared; per-run limits are the
67+ # _TIMEOUT in evaluation.py).
6968ENV SANDBOX_ENABLED="true" \
70- SANDBOX_SECCOMP= "true " \
71- SANDBOX_RO_BINDS ="/usr:/lib:/lib64:/bin:/sbin:/etc:/app " \
72- SANDBOX_RW_BINDS= "/tmp "
69+ SANDBOX_RO_BINDS= "/ " \
70+ SANDBOX_RW_BINDS ="/tmp " \
71+ SANDBOX_DISABLE_CLONE_NEWPID= "true "
7372
7473ENV LOG_LEVEL="debug"
0 commit comments