Harden Docker job workspace isolation#4518
Conversation
Greptile SummaryThis PR replaces the previous whole-workspace bind mount for SJ/CJ containers with a three-layer isolated view: an empty tmpfs at the workspace root (mode 0555), read-only bind mounts for Confidence Score: 4/5Safe to merge; the isolation logic is sound and well-tested, with one low-severity limitation in a secondary security guard. Only P2 findings present. The single issue is that the nvflare/app_opt/job_launcher/docker_launcher.py — the Important Files Changed
Sequence DiagramsequenceDiagram
participant SP as SP/CP Container
participant L as DockerJobLauncher
participant V as _safe_workspace_child_path
participant D as Docker Daemon
participant SJ as SJ/CJ Container
SP->>L: launch_job(job_meta, fl_ctx)
L->>V: validate job_workspace_name (job_id)
V-->>L: host_job_workspace path
L->>V: validate startup (allow_reserved=True)
V-->>L: host_startup_dir path
L->>V: validate local (allow_reserved=True)
V-->>L: host_local_dir path
L->>D: containers.run(mounts=[tmpfs at /ws (0555), bind startup ro, bind local ro, bind job_ws rw], working_dir=/ws/job_id)
D->>SJ: start container with isolated workspace view
SJ-->>SJ: reads startup/ and local/ (read-only)
SJ-->>SJ: writes job outputs to /ws/job_id/ (read-write)
SJ-->>SP: connect via PARENT_URL (Docker DNS)
Reviews (5): Last reviewed commit: "Use job workspace as Docker working dire..." | Re-trigger Greptile |
There was a problem hiding this comment.
Pull request overview
This PR hardens the DockerJobLauncher’s workspace isolation by changing how the NVFlare workspace is exposed to job containers, aiming to limit job visibility to only startup/local (read-only) and the job’s own workspace directory (read-write), and updates unit tests and design documentation to reflect the new contract.
Changes:
- Introduces
_safe_workspace_child_pathand uses it to validate job/startup/local workspace child paths before creating bind mounts. - Replaces the previous “bind-mount entire workspace” behavior with an isolated mount layout (tmpfs workspace root + RO startup/local + RW job directory) for SJ/CJ containers.
- Updates Docker launcher unit tests and the Docker launcher design doc to match the new workspace mount contract.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
nvflare/app_opt/job_launcher/docker_launcher.py |
Adds safe workspace child path validation and switches job container workspace mounts to an isolated tmpfs + selective bind-mount model. |
tests/unit_test/app_opt/job_launcher/docker_launcher_test.py |
Adds tests for _safe_workspace_child_path and updates mount assertions for the new mount layout. |
docs/design/docker_job_launcher_design.md |
Documents the updated isolation model, mount structure, and updated “reserved keys” wording (mounts vs volumes). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
/build |
|
Addressed the latest Greptile P2 note about |
|
/build |
Summary
0555permissions, read-onlystartup/andlocal/bind mounts, and a read-write bind mount for only the current job workspace so job outputs persist on the host.local/is visible read-only, including files such asstudy_data.yaml; sibling job workspaces and the parent/site workspace root are not mounted.Validation
python3 -m py_compile nvflare/app_opt/job_launcher/docker_launcher.py tests/unit_test/app_opt/job_launcher/docker_launcher_test.pyblack==25.9.0 --check nvflare/app_opt/job_launcher/docker_launcher.py tests/unit_test/app_opt/job_launcher/docker_launcher_test.pyisort==5.13.2 --check-only nvflare/app_opt/job_launcher/docker_launcher.py tests/unit_test/app_opt/job_launcher/docker_launcher_test.pyflake8==7.1.1 nvflare/app_opt/job_launcher/docker_launcher.py tests/unit_test/app_opt/job_launcher/docker_launcher_test.pygit diff --checkpytestwas not run locally because this environment does not havepytestinstalled (No module named pytest).