给CTFd加上ACM评测功能
闲得蛋疼.jpg
去年参加西电办的中学生CTF嫖了五千块钱,今年就能来西电办中学生CTF.jpg,真实的一批
去年的ACM题是直接起了个hustoj(我记得),这样就需要工作人员不断地检查榜单并且手动发放flag,工作量大且效率低。于是我就来整个CTFdOJ,交的代码AC了自动加分。
前置
CTFd plugin
与其说是“魔改CTFd”不如说“给CTFd整个插件”
CTFd自带的动态积分题目就是一个典型的插件。这个插件给CTFd添加了一种题目。
那么添加“ACM题目类型”理所当然也应该写成一个插件
沙箱的选择
评测沙箱需要用来控制程序的行为,而且是OJ的核心部件,要精确统计程序的运行时间/空间占用信息。
由于有很多现成的,那我就找一个拿来用吧
综合功能和LICENSE等多种因素,最终选择了QDOJ的Judger
一些决定
- 将评测机与CTFd分离开来。
- 考虑到CTFd只是一个题目平台,并不应该负责繁重的计算任务
- 将评测机和平台放在一起有修改成绩的隐患
- 做安全的同学大概对Python更加熟悉,于是支持对Python程序的评测
- 通过配置文件能随时添加新的语言支持
撸代码
plugin
负责添加/展示/设置题目,并在评测正确时为相应队伍加上对应的分数
折腾了半天。。。推翻了以前越写越复杂还要改CTFd自己的数据库的写法以后重做了这个东西:ICPC Plugin
translation layer
主要负责接受来自CTFd plugin的评测请求
缓存测试用例,避免每次都要把40多M的input/output重新发一遍
由于需要不同的功能,还是用flask方便一点。通过不同的URL来定位不同的功能。
于是有了JudgeServer
对于不同的语言支持,可以在worker.json中配置。其中可以配置编译命令,执行命令,并且通过向命令中注入变量来控制细节。
解释型语言不写编译命令就是了。
translation layer::权限控制
ACM沙箱最头疼的就是权限。
首先,用了别人的沙箱,可以丢过给别人。
其次,这货跑在docker里头,断了外网。希望可以一劳永逸。
还有,具体的权限控制假定都能通过命令行参数完成。比如
java的-Djava.security.manager
选项
Python可以跑在venv里头
总体流程
1 | +--------+ +--------+ |
上图中1、2、3分别表示CTFd在接收到一次submission(S)后与Judger可能的三种行为
code | action |
---|---|
1 | 发送代码+题目id+语言,进行评测 |
2 | 返回评测结果与评测过的最后一组数据的运行情况 |
3 | 发送题目测试用例的URLtodo+设定的资源限制,缓存题目评测信息 |
在收到一次Submission(S)后,Plugin首先尝试进行1
。
如果Judger此时并没有缓存过这个题目,则会返回评测错误,此时Plugin会尝试进行3
,Judger则会缓存当前题目
Plugin执行完3
后会再次尝试1
。
真是憨憨,自嗨行为