如何构建 dify-agent-runtime(shellctl)镜像并在容器中运行与验证路径隔离
2026/9/10 7:16:08 网站建设 项目流程

如何构建 dify-agent-runtime(shellctl)镜像并在容器中运行与验证路径隔离

【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify

dify-agent-runtime是 dify 仓库中用 Go 实现的 shellctl 服务端与运行时工具集:它在一个容器内以 tmux 托管每个 agent job,并通过 Landlock 沙箱限制每个 job 的文件系统访问。这篇文章对应的任务是:从仓库构建dify-agent-runtime镜像、以容器方式启动shellctl serve,然后通过实际的 job 请求验证路径隔离(Path Isolation)是否按预期生效,以及如何关闭隔离做对照。

前提条件(均来自 dify-agent-runtime/README.md):

  • 宿主机为 Linux,且内核 ≥ 5.13——Landlock 依赖这一版本,在不支持的 kernel 上只会向 stderr 打印警告;
  • 运行时依赖 tmux(Dockerfile 已在构建阶段安装);
  • Go 1.26(仅在不用镜像、改用make build本地编译时需要)。

构建镜像

仓库文档给出的构建命令,在仓库根目录执行(构建上下文是dify-agent-runtime/,Dockerfile 位于其docker/子目录):

docker build -f dify-agent-runtime/docker/Dockerfile \ -t dify-agent-runtime:latest \ dify-agent-runtime/

注意:README 中该命令还带有一个--build-context agent=./dify-agent参数,用于提供 agent 侧构建上下文;如果你只构建 runtime 镜像、不依赖该上下文,可以直接去掉。镜像构建分两个阶段(见 Dockerfile):先用golang:1.26交叉编译出shellctlshellctl-sanitize-ptyshellctl-runner-exitshellctl-runnerdify-agent五个二进制,再基于python:3.12-slim-bookworm安装 bash、git、jq、tmux、ripgrep、tini 等工具,并内置 Node.js 24.20.0、pnpm 11.9.0、uv 0.8.9(构建时需能访问 nodejs.org 下载 Node 发行包并校验 SHASUMS256)。

镜像创建非 root 用户dify,工作目录为/home/dify,暴露 5004 端口,入口命令为:

shellctl serve --listen 0.0.0.0:5004

启动容器并确认服务就绪

README 给出的运行方式:

docker run -d --name dify-agent-runtime \ -p 15004:5004 \ dify-agent-runtime:latest

两个需要说明的点:

  • 服务鉴权 token 通过环境变量SHELLCTL_AUTH_TOKEN注入(见 config.go 中DefaultAuthTokenEnv = "SHELLCTL_AUTH_TOKEN")。仓库的集成测试(Makefile 的integration-up)均以-e SHELLCTL_AUTH_TOKEN=<token>方式启动容器;调用 API 时以Authorization: Bearer <token>携带。
  • 就绪检查使用GET /healthz。Makefile 中的等待逻辑是:每 2 秒curl -sf http://localhost:<host-port>/healthz,60 秒内返回成功即视为就绪;验收测试还确认/healthz返回 200 且响应体status字段为ok。对应的手动检查:
curl -s http://localhost:15004/healthz

未就绪时先docker logs dify-agent-runtime查看容器日志。

用 job 请求验证路径隔离

服务就绪后,通过POST /v1/jobs/run提交脚本来验证隔离行为。以下命令中把15004替换成你实际映射的宿主端口,<token>替换为你注入的SHELLCTL_AUTH_TOKEN值。

隔离默认开启(SHELLCTL_ENABLE_PATH_ISOLATION未设置时即为启用)。用一条 job 同时验证「允许写工作区」与「拒绝写 /tmp」:

curl -s -X POST http://localhost:15004/v1/jobs/run \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <token>' \ -d '{ "script": "touch \"$PWD/iso-test\" && echo workspace_ok; touch /tmp/iso-denied 2>&1; echo tmp_exit=$?", "cwd": "/workspace", "timeout": 10 }'

预期在output中看到workspace_ok,以及touch/tmp失败(非零退出,如Permission denied)。这套断言与 acceptance_test.go 中TestLandlockUsesWorkspaceAsTempSpace的判定方式一致。

再看隔离的默认权限边界(README 中的表格,$HOME与 job 的cwd可读写,cwd同时被用作TMPDIR/TMP/TEMP,其余默认拒绝):

访问级别路径(默认值)
Read-Write$HOME和 job 的cwd(同时用作TMPDIRTMPTEMP
Read-Write (dev)/dev/null/dev/zero/dev/urandom/dev/random/dev/tty
Read-Only + Exec/usr/bin/sbin/lib/lib64/etc/proc/opt/dify-agent-tools/opt/homebrew/snap
Denied其余一切(/tmp、其他 agent 的 home、/var/srv等)

一条 job 同时验证「允许写 HOME」和「拒绝写 /opt」:

curl -s -X POST http://localhost:15004/v1/jobs/run \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <token>' \ -d '{ "script": "touch \"$HOME/landlock-test-file\" && echo home_ok; touch /opt/landlock-denied 2>&1; echo exit=$?", "env": {"HOME": "/home/dify"}, "timeout": 10 }'

预期outputhome_ok,且exit=0不出现(写/opt被拒绝)。

另外一点:隔离开关只在容器级生效。验收测试TestLandlockEnvBypassBlocked验证了把SHELLCTL_ENABLE_PATH_ISOLATION: "false"放进 job 的env字段不能绕过沙箱,job 内仍会被拒绝。

可选分支:关闭隔离做对照

若要验证「关闭隔离后 /tmp 可写」,需要单独再起一个带-e SHELLCTL_ENABLE_PATH_ISOLATION=false的容器(Makefile 的integration-up就是这样准备 no-isolation 容器的):

docker run -d --name dify-agent-runtime-noiso \ -p 15005:5004 \ -e SHELLCTL_AUTH_TOKEN=<token> \ -e SHELLCTL_ENABLE_PATH_ISOLATION=false \ dify-agent-runtime:latest

就绪检查同样打http://localhost:15005/healthz,然后提交同样的touch /tmp/...job,这次应成功(测试断言输出含write_ok)。验证完成后清理容器会删除该容器及其数据:docker rm -f dify-agent-runtime-noiso

可选分支:直接跑仓库集成测试

Makefile 提供了完整的自动化验证入口。make integration会构建镜像、随机端口启动隔离与 no-isolation 两个容器、等待/healthz就绪后执行:

go test -tags=integration -v -count=1 -timeout=300s ./tests/...

退出时自动调用integration-down删除两个容器并移除状态文件。若需要分步操作(排查失败时保留容器),用make integration-upmake integration-logsmake integration-down三个目标分别对应启动、看日志、清理;integration-test要求先有integration-up生成的状态文件。

限制与相关配置

  • Landlock 要求 Linux ≥ 5.13;不支持的 kernel 上服务端只会在 stderr 打印警告,路径隔离不会真正生效。
  • 环境变量清单见 internal/envvar/envvar.go:SHELLCTL_ENABLE_PATH_ISOLATION(默认启用,仅值true时视为开启)、SHELLCTL_LANDLOCK_RW_PATHS/SHELLCTL_LANDLOCK_RO_PATHS/SHELLCTL_LANDLOCK_RW_DEV_PATHS(逗号分隔,覆盖默认的额外读写/只读+执行/设备路径)。按 landlock/config.go 的说明,变量一旦设置(即使为空字符串)就会整体替换默认值,设为空即不再授予$HOME之外的额外路径。
  • job 输出文件约定(README):pty模式(默认)合并 stdout/stderr 写入output.logstdio模式 stdout 进output.log、诊断信息进stderr.log,且不接受/input

【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询