百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)

haoteby 2025-03-26 12:20 26 浏览

上一篇:Dify工具使用全场景:dify-web修改编译指南(源码解读篇·第1期)

我的场景:

最近在使用dify的代码节点执行代码时,发现如果想引用第三方的库会报错,目前代码执行只支持python3和javascript脚本,而python3里有很多好用的python依赖库,如果想引用就得修改源码,不然执行代码会报错。

开源地址:
https://github.com/langgenius/dify

首先来看一下,dify在容器里启动一共有几个服务,如下图:

今天来聊聊dify里的沙盒

源码github地址:
https://github.com/langgenius/dify-sandbox

关于沙箱,在dify里主要在代码执行运行在安全的沙盒中,如下图所示:

官方解释

Dify-Sandbox提供了一种在安全环境中运行不受信任代码的简单方法。它被设计用于多租户环境,其中多个用户可以提交要执行的代码。代码在沙盒环境中执行,这限制了代码可以访问的资源和系统调用。

DifySandbox目前只支持Linux,因为它是为docker容器设计的。它需要以下依赖项:

  • libseccomp
  • pkg-config
  • gcc
  • golang

安装步骤

克隆 git clone https://github.com/langgenius/dify-sandbox
运行 ./install.sh 以安装必要的依赖项
运行 ./build/build_[amd64|arm64].sh 构建沙箱二进制文件
运行 ./main 以启动服务器

如果要调试服务器,请首先使用 build script 构建沙盒库二进制文件,然后使用 IDE 根据需要进行调试。

dify沙箱的工作流程


如何在沙箱里跑咱们自己定义的包?

本次咱们以pandas的引入依赖为例子。

首先介绍一下pandas是什么?它是一个基于python的强大数据分析和处理工具库,主要用于数据清洗、数据操作和数据分析。它以直观和高效的方式处理结构化数据,它尤其擅长处理二维数据。

处理流程

我们先进入已运行的docker,先看一下dify-sandbox容器里的结果。

可以看到,容器里关键的两个包为:dependencies 和 conf 包,因此,可以看了,只需要映射这两个包就行了。在docker-compose.yaml中可以如下配置:

因此,我们修改宿主机里的dependencies下的源文件,增加需要支持的依赖,如下图所示:


当重启docker-compose up -d 重启后就会加载进去。如下图:

启动时,会自动拉取依赖。

原型

为什么sandbox启动时会被安装。

主要看一下源码中的初始化依赖的方法,会在沙盒启动时,读取配置文件进行安装,源码如下:

func Run() {
	// 初始化配置
	initConfig()
	//  初始化依赖
	go initDependencies()
 // 初始化服务
	initServer()
}

位置在:
https://github.com/langgenius/dify-sandbox/blob/main/internal/server/server.go

主要的依赖:

func initDependencies() {
	log.Info("installing python dependencies...")
	dependencies := static.GetRunnerDependencies()
	err := python.InstallDependencies(dependencies.PythonRequirements)
	if err != nil {
		log.Panic("failed to install python dependencies: %v", err)
	}
	log.Info("python dependencies installed")

	log.Info("initializing python dependencies sandbox...")
	err = python.PreparePythonDependenciesEnv()
	if err != nil {
		log.Panic("failed to initialize python dependencies sandbox: %v", err)
	}
	log.Info("python dependencies sandbox initialized")

	// start a ticker to update python dependencies to keep the sandbox up-to-date
	go func() {
		updateInterval := static.GetDifySandboxGlobalConfigurations().PythonDepsUpdateInterval
		tickerDuration, err := time.ParseDuration(updateInterval)
		if err != nil {
			log.Error("failed to parse python dependencies update interval, skip periodic updates: %v", err)
			return
		}
		ticker := time.NewTicker(tickerDuration)
		for range ticker.C {
			if err:=updatePythonDependencies(dependencies);err!=nil{
				log.Error("Failed to update Python dependencies: %v", err)
			}
		}
	}()
}

func updatePythonDependencies(dependencies static.RunnerDependencies) error {
	log.Info("Updating Python dependencies...")
	if err := python.InstallDependencies(dependencies.PythonRequirements); err != nil {
		log.Error("Failed to install Python dependencies: %v", err)
		return err
	}
	if err := python.PreparePythonDependenciesEnv(); err != nil {
		log.Error("Failed to prepare Python dependencies environment: %v", err)
		return err
	}
	log.Info("Python dependencies updated successfully.")
	return nil
}

在初始化依赖时,会运行一个执行安装的命令。

包安装后可以运行试试

这里我们会看到一个异常,“error: operation not permitted”异常的意思就是没有权限去执行操作。

从源码中可以看出,其实dify-sandbox是由一种叫做seccomp的技术来完成的,如下图:

这里又得说说什么是seccomp

Secomp(全称:Secure Computing Mode)是Linux内核的一种安全机制,旨在限制进程能够调用的系统调用(syscall)。通过限制系统调用的范围,可以显著降低攻击面,即使程序存在漏洞,攻击者也很难通过恶意输入或代码执行来利用危险的系统调用。

再看看源码,那默认沙盒能调用的系统调用都有哪些?

如图所示:

可以看到:系统调用(System Call)是操作系统提供给应用程序的一组接口,用于请求操作系统内核执行特定的底层操作。它是用户程序和操作系统内核之间的桥梁,使应用程序可以利用操作系统的功能,如文件管理、内存分配、网络通信等。沙盒通过控制这些调用来控制安全的。

再看源码:

设置为ALLOWED_SYSCALLS这个环境变量,然后再去执行python code

沙盒的具体工作原理流程:

code节点->dify沙盒服务->在tmp目录生成python代码->设置环境变量ALLOWED_SYSCALL并执行代码->执行tmp目录生成的python代码->返回结果

其实它执行时,会执行一个python.so的模板文件,如下图:

那这个命令如何来的,我们再看看编译命令:

这里其实调用那个lib下面的python的main.go这个文件,这个文件初始化了一个seccomp,这样就和前面的知识对应起来了。

具体的使用,大家可以跟踪一下代码,好好学习一下。

官方是使用go语言去编译的,我学得太麻烦,还是用容器的方式把修改的内容映射到容器里就行,主要修改两个文件:

conf/config.yaml

app:
  port: 8194
  debug: True
  key: dify-sandbox
max_workers: 4
max_requests: 50
worker_timeout: 5
python_path: /usr/local/bin/python3
enable_network: True # please make sure there is no network risk in your environment
allowed_syscalls: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,28,30,31,32,33,34,35,36,37,39,56,57,60,61,62,63,72,73,80,81,85,86,90,91,105,106,131,186,202,204,217,231,233,234,237,257,262,273,281,291,318,334,435,499,318,334,307,262,16,8,217,1,3,257,0,202,9,12,10,11,15,25,105,106,102,39,110,186,60,231,234,13,16,24,273,274,334,228,96,35,291,233,230,270,201,14,131,318,56,258,83,41,42,49,50,43,44,45,51,47,52,54,271,63,46,307,55,5,72,138,7,281] # please leave it empty if you have no idea how seccomp works
proxy:
  socks5: ''
  http: ''
  https: ''

dependencies/python-requirements.txt

pandas==2.2.3

如果想在容器里运行,可以写个测试文件映射到容器里,测试一下即可,如:

test.py

import ctypes
import json
import os
import sys
import traceback


# setup sys.excepthook
def excepthook(type, value, tb):
    sys.stderr.write("".join(traceback.format_exception(type, value, tb)))
    sys.stderr.flush()
    sys.exit(-1)


sys.excepthook = excepthook

lib = ctypes.CDLL("/var/sandbox/sandbox-python/python.so")
print(lib)
lib.DifySeccomp.argtypes = [ctypes.c_uint32, ctypes.c_uint32, ctypes.c_bool]
lib.DifySeccomp.restype = None

os.chdir("/var/sandbox/sandbox-python")

lib.DifySeccomp(65537, 1001, 1)

# declare main function here
# 测试代码可以写在此处

import pandas as pd

# 此处main 方法不要带具体参数
def main() -> dict:
    s = pd.Series([1, 3, 5, 6, 8])
    return {
        "result": "test",
    }


from base64 import b64decode
from json import dumps, loads

# execute main function, and return the result
# inputs is a dict, and it
inputs = b64decode("e30=").decode("utf-8")
output = main(**json.loads(inputs))

# convert output to json and print
output = dumps(output, indent=4)

result = f"""<>
{output}
<>"""

print(result)

用test.sh来调用

import ctypes
import json
import os
import sys
import traceback


# setup sys.excepthook
def excepthook(type, value, tb):
    sys.stderr.write("".join(traceback.format_exception(type, value, tb)))
    sys.stderr.flush()
    sys.exit(-1)


sys.excepthook = excepthook

lib = ctypes.CDLL("/var/sandbox/sandbox-python/python.so")
print(lib)
lib.DifySeccomp.argtypes = [ctypes.c_uint32, ctypes.c_uint32, ctypes.c_bool]
lib.DifySeccomp.restype = None

os.chdir("/var/sandbox/sandbox-python")

lib.DifySeccomp(65537, 1001, 1)

# declare main function here
# 测试代码可以写在此处

import pandas as pd

# 此处main 方法不要带具体参数
def main() -> dict:
    s = pd.Series([1, 3, 5, 6, 8])
    return {
        "result": "test",
    }


from base64 import b64decode
from json import dumps, loads

# execute main function, and return the result
# inputs is a dict, and it
inputs = b64decode("e30=").decode("utf-8")
output = main(**json.loads(inputs))

# convert output to json and print
output = dumps(output, indent=4)

result = f"""<>
{output}
<>"""

print(result)

这样咱们就能成功的加载第三方应用了。

相关推荐

别争了,Access数据库才是真正的低代码开发平台

Access数据库是微软公司搞出来的“奇葩”产品。...

Access开发轻松一键将 Access 全库表格导出为 Excel

hi,大家好呀!在日常工作中,Access常常是我们忠实的数据管家,默默守护着项目信息、客户列表或是库存记录。它结构清晰,录入便捷,对于许多中小型应用场景来说,无疑是个得力助手。然而,当我们需要对这...

跟我学:从零开始用Access设计一套完整的系统(一)

序言:Access是一款强大而灵活的数据库软件,可以设计和开发各种类型和规模的数据库应用程序。本文旨在为您提供从零开始设计Access数据库系统的详细指导,并通过实际案例演示如何在Access中设计和...

问卷调查管理程序 Access数据库 功能介绍和VBA代码分享

o本系统包含主要功能有:问卷管理,题目管理,问卷填写,调查结果统计,数据汇总导出o数据库系统包含:表,查询,窗体,VBA代码...

非绑定记录窗体查看管理数据 Access数据库功能模块 VBA代码编程

模块Public成绩IDnumAsLong学生成绩管理PrivateSubCommand更新_Click()DoCmd.SetWarnings(False)...

ACCESS中的DLookUp函数是如何运算的?

一、DLookUp函数介绍1.DLookUp函数的用途:可以用于从指定集合(一个域)中获取符合条件的特定字段的值。2.DLookUp函数的格式为:DLookUp(expr,domain,...

Excel常用技能分享与探讨(5-宏与VBA简介 VBA之用户窗体-一)

用户窗体(UserForm)是VBA中创建交互式界面的核心工具,可用于数据录入、设置参数或展示信息。...

【每日任务计划管理系统】Access数据库管理系统 VBA代码分享

窗体系统主页文本框,组合框,按钮,子窗体OptionCompareDatabase...

VBA高效开发:用用户窗体打造个性化数据录入工具

在日常办公中,Excel的数据录入是否总让你陷入这些困境?手动输入易错、格式混乱难追溯、重复操作耗时费力。今天,我们将突破常规,利用VBA的用户窗体(UserForm)构建一套“智能校验、流程清晰、...

VBA编程(基于Access)第1课:VBA的作用和学习方法

VBA,英文全称VisualBasicforApplications,直接翻译过来叫做“可以直接使用的VB语言”。...

Access数据库宏与VBA代码的使用(精品一)

Access数据库的宏相当于实现某一功能的一系列命令和操作,我们无需写代码,系统已经将主体代码集成一块,我们只需要做一些简单的操作即可,而VBA代码则是实实在在的代码写到程序里面,我们可以自己编写,也...

【每日任务管理系统】(2) VB 管理系统 代码 Visual Basic access数据库

窗体全部任务DimdhAsLong'存储高度差DimdwAsLong'存储宽度差...

VBA连接access数据库开发软件(vba调用数据库连接)

VBA连接access数据库开发小软件虽然VBA(包括VB)已不再流行,但是在某些场合还是比较方便的,尤其对非编程专业人员。灵活使用VBA,可以很十分方便的处理excel,access数据,提高工作效...

九章云极发布全新AI智算云平台:支持秒级生成百万级Token

6月16日,在在九章云极智能计算科技论坛上,九章云极宣布推出全新AI智算云平台“九章智算云AlayaNeWCloud2.0”,该平台基于Serverless技术架构与强化学习技术(RL)的深度融合...

浅谈基于大数据技术下的“云旅游”平台运营策略研究

云旅游体验平台是利用大数据和虚拟技术,构建虚拟旅游环境,能够改变旅游企业的营销模式和旅游者的消费模式。本文从云旅平台多维度数据信息的获取与分析,平台体验质量反馈信息数据构建,云旅游平台服务功能设计方案...