Securing DeepSeek Harness in Multi-Tenant Containers: Removing Filesystem Plugins to Reduce Attack Surface
The author details removing filesystem tool plugins from DeepSeek Harness (DSH) to secure multi-tenant container deployments, covering plugin architecture, overlay-based disabling, impact on 6 tools, real-model attack testing showing bash as a residual channel mitigated by sandbox fail-closed behavior, and resource metrics showing negligible savings.
1. Distinguishing Filesystem Capability Provider from Tool Consumer
Examining packages/bundle/base/cordis.patch.yml reveals DSH splits filesystem into two plugin roles:
Capability Provider (Service Provider) : Implements ctx.fs service, used by Web UI directory picker and other non-model consumers.
Tool Consumer (Tool Consumer) : Exposes filesystem capabilities to the model as tools, constituting the model-facing attack surface:
- id: tool-fs
name: '@deepseek-ai/dsh-tool-fs' # read / write / edit
- id: tool-fs-search
name: '@deepseek-ai/dsh-tool-fs-search' # glob / grep
- id: tool-str-replace-editor
name: '@deepseek-ai/dsh-tool-str-replace-editor'Trimming must delete Consumers, not Providers. Deleting the Provider breaks non-model consumers that declare inject: ['fs']; DSH fails loudly on missing dependencies and the whole bundle fails to start. Deleting only the tool rows severs the "model → filesystem" exposure path. This distinction is fundamental to multi-tenant security: isolate who can use what , not whether the functionality exists .
2. Uninstalling via an Overlay — No Source Changes
The loader's entry syntax treats disabled as a first-class citizen; an overlay can disable any existing row. The entire change is 12 lines of YAML:
# trim-fs.cordis.yml — remove model-facing filesystem tool family
- id: tool-fs
disabled: true
- id: tool-fs-search
disabled: true
- id: tool-str-replace-editor
disabled: trueAt runtime the overlay is mounted via --patch (launcher flags first, application flags after; see DeepSeek Harness plugin development guide at https://mp.weixin.qq.com/s?__biz=MjM5MzgxNzA1Ng==∣=2247484760&idx=1&sn=4bd936f793ff619a61007c6edf585dcb&scene=21#wechat_redirect):
# headless one-off task (tool inventory verification)
dsh --profile headless --patch ./trim-fs.cordis.yml "Reply with exactly: pong"
# web container: pass the same overlay to the container's dsh web
dsh web --patch /patch/trim-fs.cordis.yml --no-open --port 3080No fork, no source modification; upgrades only require maintaining this single YAML overlay.
3. Affected Plugins and Tools
A 20-line audit plugin collected tool inventories before and after trimming by calling ctx.tools.schemas() (the public projection of the tool registry) in apply:
Before trimming (25 tools, headless root scope):
bash, create_goal, edit, exit_plan_mode, get_goal, glob, grep, interrupt_agent,
job_kill, job_list, job_output, list_agents, ralph, read, read_image, send_message,
skill, str_replace_editor, subagent, subagent_fork, todo_write, update_goal,
web_search, workflow, write
After trimming (18 tools):
bash, create_goal, exit_plan_mode, get_goal, interrupt_agent, job_kill, job_list,
job_output, list_agents, ralph, send_message, skill, subagent, subagent_fork,
todo_write, update_goal, web_search, workflowImpact summary:
6 tools directly removed : read, write, edit (tool-fs); glob, grep (tool-fs-search); str_replace_editor.
One side effect : read_image also disappears because it is provided by the same fs plugin family; disabling tool-fs removes it as well. Businesses needing image reading must evaluate this upfront.
Unaffected : ctx.fs capability service runs normally, non-model consumers unaffected; bash, web_search, subagent, workflow and other tool families remain.
Code mode (PTC) coverage : In PTC mode the callable set tools.<name>(args) comes from the same registry projection; tools absent from the projection are equally unreachable in code mode — no bypass exists.
4. Real-Model Attack Path Test
After trimming, a real model was asked to read a container file:
Task: "Read the file /etc/hostname and report its exact content."Model behavior:
FS tools unavailable → falls back to bash attempting cat /etc/hostname.
Bash's workspace-write sandbox backend is unavailable in the container (no bubblewrap, landlock not effective); runtime refuses unconstrained execution — fail-closed, no silent pass-through.
Model attempts to escalate to danger-full-access, which requires approval; headless session has no approver → request fails and closes .
What are the "sandbox backend" and bubblewrap? DSH's bash tool does not run commands bare: the default workspace-write policy requires execution in a restricted environment — read-only system paths, write-only workspace directory. The platform-specific sandbox backend enforces this; DSH probes a detection chain ( packages/sandbox/sandbox-local/src/index.ts):
Linux : prefers bwrap, then landlock.
macOS : sandbox-exec (Seatbelt).
Windows : ACL-based restricted token. bubblewrap (command bwrap) is a Linux sandbox based on kernel namespaces (mount/PID/user) — Flatpak's app sandbox is built on it; install via apt-get install bubblewrap. landlock is a kernel file-access control feature (no privilege, no install, but requires kernel support). DSH actually probes the chain at runtime: it tries to create a restricted environment with bwrap running true; success enables it. When all backends are unavailable, fail-closed handling applies — refuse to execute commands rather than allow them. The experimental container lacked bubblewrap and landlock was ineffective, so bash became "fully denied" — secure but zero usability.
The model ultimately reported inability to read the file. This result is more valuable than "tools were deleted":
First line: tool trimming (read/glob/grep not in directory)
Second line: sandbox fail-closed (backend missing → refuse, not allow)
Third line: approval fail-closed (escalation no responder → refuse, not allow)Direct impact on container deployment: the image must include bubblewrap (add one line in Dockerfile's apt-get install) or confirm the target kernel supports landlock; otherwise bash's sandbox policy becomes "deny all", hurting usability. After fs removal, bash is the model's only filesystem channel, and its sandbox must be real.
5. Collected Notes
Delete Consumers, not Providers (see §1), otherwise the bundle fails to start.
Web session tools come from the preset layer. In dsh web the global tool-fs / tool-bash rows are already disabled; the model sees tools injected by the agent preset (standard, etc., see packages/preset/agent-presets/presets/) per session. To trim in web scenarios, either switch to a smaller preset (e.g., minimal), apply the same disabled in the preset layer, or use ctx.tools.restrict() to filter per session — the overlay method validated on headless works for preset rows too, just at a different mount point.
Watch for read_image collateral removal ; image-reading workloads need separate evaluation.
Resource savings are not the goal of trimming. Measured comparison (same image, same limits, only the overlay differs):
Web idle memory: 304.6 MiB before, 300.1 MiB after
Cold start to ready: 2.76s before, 2.73s after
PIDs: 19 before, 19 after
Headless task peak: 128.5 MiB before, 126.9 MiB after
Differences are noise-level (~2 MiB): the three tool plugins have negligible resident cost; the base runtime (agent-loop, session, LLM pipeline) dominates memory. Trimming trades attack surface, not resources — multi-tenant density optimization relies on container scheduling, not plugin deletion.
Re-verify tool inventory on every DSH upgrade. Row IDs ( tool-fs etc.) are composition-layer identifiers; upstream may reorganize compositions. Include the audit plugin (printing ctx.tools.schemas() list) in upgrade smoke tests; one minute confirms trimming still effective.
6. Summary
12 lines of YAML remove model-facing filesystem capabilities, 25 → 18 tools, zero source changes, only this overlay to maintain on upgrades.
Impact is clear and controlled: 6 tools gone, read_image collateral, capability service and non-model consumers untouched, code mode no bypass.
Real-model bypass attempts (fallback to bash) blocked by two independent fail-closed lines — sandbox and approval — DSH's security components choose deny over degrade when missing, the correct default for multi-tenant cloud.
Two action items: add bash sandbox backend (bubblewrap/landlock) to the image; apply equivalent trimming at the preset layer for web scenarios.
Containers give multi-tenancy "inter-instance" isolation; plugin trimming gives "intra-instance" convergence — both layers together enable a service shape safe to expose behind a unified gateway.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
