自研沙箱Isolate学习

910 阅读8分钟

自研沙箱学习

step01:深度阅读沙箱的论文

在沙箱中运行程序,使其无法与外界通信,并且其资源受到限制
使用方式:
- isolate --init,初始化沙箱,创建其工作目录并将其名称输出到标准输出,如果沙箱已经存在则失败。

沙箱论文学习:
    1.背景
        OnlineJudge讲究的是用户实时地将执行文件提交,并且自动评分。要保证输入数据和输出数据的正确性。同时还要给出程序的限制时间和内存。以便区分不同效率的解决方案。
        此外,必须采用适当的安全措施避免作弊-例如,不得允许程序访问文件窃取答案,杀死其他进程,也不能通过网络进行通信。
        通过以上内容描述,要满足OJ安全有效的运行,用户提交的程序必须运行在一个可以限制程序内存&时间使用率,同时要保证运行系统的安全性
    2.常用沙盒
        ptrace
            最常见的沙箱使用的是Mares和Kolstad,他们是通过ptrace系统调用用来请求内核停止沙盒程序,它通过监控运行过程系统调用的参数,以及程序影响安全性的时候,会结束调用或者直接杀死程序。(ptrace可以理解为一种使父进程得以监控和控制其他进程的方式,个人理解为aop -> 在执行之前要通必须经过系统调用的拦截进行监控)。
            它的缺点是监控运行程序本身需要较大的开销,也无法同时监控多线程的程序。
        secommp
            TxBox沙盒,Linux提供了一个成为seccomp的安全模块,它也可以限制系统程序可以访问的调用以及参数,同时效率要高于ptrace。但是它的原始版本支持的系统调用参数很少,如果想定制化开发,需要对OS内核进行批量的修改
    3.一个能满足竞赛要求的沙箱应该具备的特点
        - 运行程序不能访问其他文件(打开、读取、写入)。使用传统的文件系统权限可以轻松限制。沙盒程序在其自己用户和组id以及目录下运行,通过disk quota来限制程序磁盘的使用数据量
        - 内存分配,我们必须限制程序使用的内存,一是控制程序的时间复杂度,二是避免程序耗尽使用内存。linux提供setrlimit函数可以控制一个进程使用的最大进程
        - 提供多线程编程,如果提供多线程编程,我们就必须限制程序使用线程的最大数量。Linux内核支持修改某用户下最多可以运行多少进程or线程数,通过max_user_process来控制
        - 发送信号(kill、tkill),signal如何是给自身发是无害的,但是如果给其他进程发那就有害了。linux本身不允许不同用户之间进程发送信号,所以只要保证程序运行的用户Id唯一
        - 进程通信,不允许运行程序进行网络通信。通过ipc namespace阻断进程之间的通信能力
        - 网络,不允许运行程序进行网络通讯
            通过net namespace的网络设备集解决,将进程孤立,计算可以调用网络的api接口,但是也没有网络可以通信。
        - 挂钟,
            也就是sleep,不允许运行的程序wall clcok 、time过长,这样会使进程一直sleep,所以也要进行挂钟时间的限制
            (iso/isolate支持wall clock time limit,但是实现方式未知)
        - 将缓冲区刷新到磁盘,sync&fsync等,如果运行的程序频繁的将缓冲区刷新到磁盘,这样会加大磁盘IO的速度。因为磁盘IO本身cpu处于一个挂钟状态,通过挂钟时间来限制。
        
    4.IOI/ISOLATE
        - namespace
            Linux内核通过了通过使用namespace来隔离进程组的能力,它用于特定内核操作的上下文,例如网络,文件系统访问或者进程控制。Docke的开发也是利用了namespace
            通过namespace,可以隔离进程的网络、文件、进程通信等。
        - cgroups
            namespace可以控制对内核提供资源的访问,并限制进程之间的交互。但是,它们不回强制控制cpu时间或内存资源。
            Linux的cgroups可以实现这个目标,管理员可以定义控制组的层次结构并将进程放在任意点中。新进程可以自动继承其父级的cgroup,对于每个组也可以有最大使用的限制。
            这个功能的意义就是:组内的进程可以绑定cpu处理器和内存节点,从而最大限度的减少上下文切换和提高缓存想搞,降低性能的损耗。
            内存控制器:可以追踪和限制使用的总内存。
            cpu计时控制器:此控制器不会施加任何限制,就是跟踪组内进程使用cpu的时间,定时监控cpu时间,如果超时进行终止。
        - IOI/ISOLATE用于的性能优于ptrace的沙箱,同时支持多线程程序,也满足OJ系统所需的安全性。
        

step02:学习编译命令

编译使用的命令为cd /var/local/lib/isolate/1/box/ && timeout -s 15 -k 5s 10s /usr/local/gcc-7.2.0/bin/g++ -Wl,-rpath,/usr/local/gcc-7.2.0/lib64 main.cpp  2>&1

1.编译main.cpp指定/usr/local/gcc-7.2.0/bin/g++ -Wl,-rpath,/usr/local/gcc-7.2.0/lib64 main.cpp

其中-Wl,-rpath是为程序运行时库文件搜索路径的命令,在使用gcc编译链接时添加即可

-Wl,这个是gcc的参数,表示编译器将后面的参数传递给链接器id

-rpath,添加一个文件夹作为运行时库的搜索路径,一些动态库的链接了其他动态库,则-rpath选项可用于定位这些链接的动态库

2.timeout命令,timeout可以让程序在指定时间内仍然运行则强制退出 timeout -s 15 -k 5s 10s
-s的意思是发送signal 15,kill -15,当程序接受到SIGTERM的信号,会根据程序自己的设置进行判断,程序可能立刻停止,也可能释放资源停止,程序可能仍然继续运行
-k 5s 10s,程序会现在运行10s后先发送一个信号,如果仍在运行,会直接在5s后发送SIGKILL信号

3.2>&1,2在Linux代表着标准错误,stderr,1是标准输出stdout.
2>&1的意思就是将标准错误重定向到标准输出。

4.运行c++,isolate --cg -s -b 3 -M /var/local/lib/isolate/3/meta.txt -t 2.0 -x 0.5 -w 5.0 -k 64000 -p30 --cg-mem=128000 --no-cg-timing -f 1024 -E HOME=/var/local/lib/isolate/3 -E LANG -E LANGUAGE -E LC_ALL -d '/etc':'noexec' --run -- ./a.out < /var/local/lib/isolate/3/stdin.txt > /var/local/lib/isolate/3/stdout.txt 2> /var/local/lib/isolate/3/stderr.txt

--cg:启用控制组
--s:让沙箱不打印自身日志信息,除了沙箱本身的致命错误外,没有状态消息打印到stderr。
--b 并行运行多个沙箱时,必须通过此选项为其分配唯一ID
--M 将程序运行的元数据输出到指定文件
--t 将程序的运行时间限制为多少秒,操作系统分配任务的时间不进入计算
--x 超过时间限制时,等待的额外的时间,在终止程序。
--w 限制挂钟时间,防止程序一直sleep下去,一般略高于time
--k 进程堆栈限制大小,单位是kb
--p 允许程序创建最多的进程或线程
--cg-mem 将整个控制组的总内存限制为kb字节
--no-cg-timing 使用控制组进行计时,以便--time开关影响控制组中所有进程和线程的总运行时间
-f 将程序创建的文件大小限制为kb
-E 设置变量
-d 绑定目录进行调用者的路径
-- run运行程序,描述程序状态的写入标准错误流



step03:运行沙箱,在沙箱上跑基本程序

在沙箱运行c++文件步骤:
1.isolate --cg -b boxId --init
2.将sourceCode生成在group目录生成main.cpp文件后,进行编译
3.编译完成进行isolate执行
4.获取执行结果的meta.txt

cd /var/local/lib/isolate/0/box/ && timeout -s 15 -k 5s 10s /usr/local/gcc-7.2.0/bin/g++ -Wl,-rpath,/usr/local/gcc-7.2.0/lib64 main.cpp  2>&1

isolate --cg -s -b 0 -M /var/local/lib/isolate/0/meta.txt -t 2.0 -x 0.5 -w 5.0 -k 64000 -p30 --cg-mem=128000 --no-cg-timing -f 1024 -E HOME=/var/local/lib/isolate/0 -E LANG -E LANGUAGE -E LC_ALL -d '/etc':'noexec' --run -- ./a.out < /var/local/lib/isolate/0/stdin.txt > /var/local/lib/isolate/0/stdout.txt 2> /var/local/lib/isolate/0/stderr.txt 


python 运行

isolate --cg -s -b 125 -M /var/local/lib/isolate/125/meta.txt -t 2.0 -x 0.5 -w 5.0 -k 64000 -p30 --cg-mem=128000 --no-cg-timing -f 1024 -E HOME=/var/local/lib/isolate/125 -E LANG -E LANGUAGE -E LC_ALL -d '/etc':'noexec' --run -- /usr/local/python-3.5.3/bin/python3 main.py < /var/local/lib/isolate/125/stdin.txt > /var/local/lib/isolate/125/stdout.txt 2> /var/local/lib/isolate/125/stderr.txt 
 
最后进行clean操作,包含删除文件,执行cleanup


debain服务器要设置cg

GRUB_CMDLINE_LINUX="cgroup_enable=memory"

https://blog.csdn.net/weixin_30878361/article/details/99093584

step03:在Debian上搭建开发环境 搭建完成

step04:进行代码开发

技术设计方案:
    
gantt
dateFormat YYYY-MM-DD
title online-judge
section 初级阶段
准备工作: 2019-12-04, 2d
section 中间阶段
调试阶段: 2019-12-07, 3d
section 后期阶段
严格测试: 2019-12-11, 3d
准备工作:
    1.文件服务类(创建文件、删除文件)
    2.调研processBudiler的使用方法
    3.meta.txt的输出
    
        time:0.130
        time-wall:0.208
        max-rss:14604
        csw-voluntary:9
        csw-forced:4
        cg-mem:13176
        exitsig:11
        status:SG
        message:Caught fatal signal 11

调试阶段
    
    1.需要写入judge文件需要root权限,脚本让/var/local进行chmod -R /var/local 777
    2.删除文件是/var/local/lib/isolate/1/