linux容器安卓下载,一个基于容器化技术的沙箱

## 起源

本项目原本的目的是用 GO 重构之前参与过的一个 OJ 的评测系统,目前完成了沙箱的部分分享一下。

## 需求

评测系统通常需要对提交的代码进行编译和运行。通常运行的算法代码并不需要特殊的权限和系统访问。沙箱需要限制住恶意代码对于评测系统运行的可能的破坏行为。

一个沙箱的实现包含了:

+ 安全: 沙箱内的程序不允许进行超出计算需求的系统访问。包括网络访问,未授权的文件系统访问。

+ 限制: 沙箱内的程序仅能使用限定的 CPU 时间和 内存

+ 快速: 运行时的额外开销小

## 实现选择

### 基于 `seccomp` `ptrace` + `setrlimit` 的沙箱

利用 linux 提供的 BPF seccomp,允许安全的 `syscall` (如 `write` 运行)。对于文件系统访问的 `syscall` (如 `open`),利用 linux 提供的 `ptrace` 和 `process_vm_readv` 读取沙箱中程序系统调用参数的值获取文件访问的路径。得到路径之后用白名单的形式判断是否为恶意访问。CPU 时间和内存的限制由 `setrlimit` 设置。

优点:实现简单,不需要权限

缺点:对于每一个文件访问系统调用都需要上下文切换,大概 `20%` 额外开销

### 基于 `unshare` `clone` 和 `cgroup` 的沙箱

得益于容器技术的进步,linux 的 `clone` 和 `unshare` 系统调用可以在与宿主机隔离的环境中运行程序的能力。利用 `clone` 系统调用创建新的 `mount`, `IPC`, `net`, `pid`, `user`, `uts` `namespace`。在运行程序之前通过 `bind mount` 和 `pivot_root` 来隔离运行环境的文件系统。用 `cgroup` 的 `cpuacct.usage` 轮询 和 `memory.limit_in_bytes` 实现CPU 时间和内存的限制。

优点:没有上下文切换的开销。`cgroup` 统计数据更准确

缺点:`cgroup` 需要 `root` 权限。`unshare clone` 有 `20 ms` 的额外开销

### 改进后的容器池

为了减少创建容器的开销,用类似 `linux daemon` 的思想创建在容器中运行的 “客户端” 由在宿主机上运行的 “控制端” 控制。这样由 “客户端” 运行的程序就不需要重新创建容器的文件系统。跨进程通信使用了 `socketpair` 创建的 `unix socket` 并由 `gob` 编码。同时为了减少文件由 `socket` 内容传递所产生的多次复制, 利用 `unix socket oob` 可以传送文件描述符的特性直接传送文件 `fd`。

Exec Command

![UML](https://github.com/criyle/criyle.github.io/raw/master/images/sandbox.png)

这样用类似 RPC 的方式实现了程序生命周期的控制。同时提供了 `open`, `delete`, `reset` command 进行文件操作。

优点:减少了创建容器的额外开销

缺点:需要 `root` 权限或者 `privilleged docker`

GitHub: [Sandbox的实现](https://github.com/criyle/go-sandbox)

## Rest API 接口

基于容器化技术的沙箱需要用 `root` 权限或者 `privilleged docker` 来运行,但是评测系统的逻辑并不需要 `root` 权限。基于权限最小化原则,把沙箱单独拆分出来以 REST API 的形式提供服务。

Web 框架使用了 [GIN](https://github.com/gin-gonic/gin)。提供了文件的 CRUD `/file` 和运行单个或者多个程序(用管道链接标准输入输出)的 `/run`。

GitHub: [ExecutorServer的实现](https://github.com/criyle/go-judge)

`go get github.com/criyle/go-judge/cmd/executorserver && sudo ~/go/bin/executorserver`

### 跨平台

设计好 REST API 接口后发现似乎并没有太大的平台相关性。借助于 duck interface 的特性,在 windows 上使用 `low mandatory level token` + `JobObject` 简单的实现了一个沙箱作为跨平台的一个验证。

### 跑分

用 `go test -bench` 测试大概有 `+1ms (2.06ms - 0.99ms)` 额外开销。用 postman 测试 REST API 大概 `+5ms` 额外延迟。

## demo

用 Vue.js 和 WebSocket 简单糊了个小测试站。前端放在 heroku 上,后端部署在性能很菜的树莓派上。[goj.ac](https://goj.ac)。

## 最后

原本的目的是为了写点代码来学习 GO 语言。然后在学习过程中重构了几次沙箱后学到了不少工程方面的设计知识,写一个库和用一个库的心态也有很大区别。

沙箱的部分实现也可以拿出来单独使用。

+ [forkExec](https://github.com/criyle/go-sandbox/tree/master/pkg/forkexec) linux fork exec 核心库,用来创建容器和加载 Seccomp BPF

+ [unixSocket](https://github.com/criyle/go-sandbox/tree/master/pkg/unixsocket) linux unix socket 传递接收文件描述符

+ [memFd](https://github.com/criyle/go-sandbox/tree/master/pkg/memfd) linux memfd 创建内存文件

+ [container](https://github.com/criyle/go-sandbox/tree/master/container) linux 容器池的实现

+ [envExec](https://github.com/criyle/go-judge/tree/master/pkg/envexec) 在环境中运行单个或多个程序的定义和实现

授权协议:开发语言:操作系统:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于SpringBoot+SpringCloud+Vue的在线代码评委系统(OJ)源码+数据库+项目说明.zip # README-CN # OJ系统 开始开发 ## 环境准备 ​ 运行此程序必须要有**Ubuntu**系统, ​ Linux 内核版本 >= 3.10 ​ jdk1.8版本, ​ mysql8.0, ​ redis。 ### go-judge系统运行 **gojudge所需要的端口是5050** <a href="">go_judge手册</a>. 在Docker上运行 ~~~bash docker run -it --rm --privileged --shm-size=256m -p 5050:5050 criyle/executorserver ~~~ #### c/c++语言环境测试 **接下来我们打开postman进行测试已post形式发送请求http://IP:5050/run携带参数** ~~~java { "cmd": [{ "args": ["/usr/bin/g++", "Main.cc", "-o", "a"], "env": ["PATH=/usr/bin:/bin"], "files": [{ "content": "" }, { "name": "stdout", "max": 10240 }, { "name": "stderr", "max": 10240 }], "cpuLimit": 10000000000, "memoryLimit": 104857600, "procLimit": 50, "copyIn": { "Main.cc": { "content": "#include <iostream>\nusing namespace std;\nint main() {\nint a, b;\ncin >> a >> b;\ncout << a + b << endl;\n}" } }, "copyOut": ["stdout", "stderr"], "copyOutCached": ["Main.cc", "a"], "copyOutDir": "1" }] } ~~~ **我们服务器会给我们返回** ~~~bash [ { "status": "Accepted", "exitStatus": 0, "time": 726910000, "memory": 55812096, "runTime": 787566071, "files": { "stderr": "", "stdout": "" }, "fileIds": { "Main.cc": "4EK46KIB", "a": "LR567VHA" } } ] ~~~ **接下来我们根据Main的Id对他进行run** ~~~bash { "cmd": [{ "args": ["a"], "env": ["PATH=/usr/bin:/bin"], "files": [{ "content": "1 1" }, { "name": "stdout", "max": 10240 }, { "name": "stderr", "max": 10240 }], "cpuLimit": 10000000000, "memoryLimit": 104857600, "procLimit": 50, "strictMemoryLi

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值