摘要:ACM/ICPC程序设计竞赛,越来越受到各个高校的重视,是程序设计竞赛中的奥林匹克。Hustoj是搭建在linux系统上的判题系统。能够判断代码的正确性。会及时返回通过或者不通过,如果不通过会返回具体原因。所以这个系统很适合ACM队员在上面做一些练习。每个学校有必要有自己的OJ,hustoj虽说是开源,有搭建教程。但是在搭建过程中还是会有一些问题。所以针对这些问题,给出了我的解决办法。
ACM/ ICPC programming competition, more and more attention by variouscolleges and universities, is the design competition in the OlympicGames. Hustoj is built on the linux system on the problem system.Able to determine the correctness of the code. Will be returned intime or not through, if not passed will return to specific reasons.So this system is very suitable for ACM players in the above to dosome practice. Each school need to have their own OJ, hustoj althoughit is open source, there are structures tutorial. But in the processof building or there will be some problems. So for these questions,given my solution.
关键字:在线判题系统hustoj搭建OJlinux搭建OJ
引言
ACM比赛越来越受到高校重视,这是一个能够提高大学生编程能力的好平台。各个学校的ACM队员都需要在自己的OJ上刷题,做练习,还需要出题,因为出题能够很好的锻炼一个人,思维能力。如果你能出题说明你对这个知识点理解比较透彻,而不是直接敲的模板(照着代码抄上去的)。除此之外,每个高校也会不定期的举办校赛,什么的,举办校赛,很多比赛普遍用的是PC^2,对于用过PC^2的人来讲,还可以。不过这个用软件容易不小心交错文件。在线判题系统这类问题会相对比较少,其次,在线判题系统的搭建过程也比PC2简单一点。流程少,如果懂一点php还可以添加一些小功能。Hustoj是开源项目。举办比赛是没什么问题的。大型比赛也没什么问题。因为他有远程判题,多台机器进行判题任务。虽说网上博客也很多,但是实际在弄的过程也会出现很多问题。所以本文主要说说搭建hustoj过程,以及常见问题。以及简单的说说如何使用。让学校举办比赛变得容易一点。目前没找到hustoj的开发文档,这样不利于二次开发,说一说该项目结构,一边后人开发有所参考。
-
安装流程
1.1系统要求
该项目是运行在linux系统上的,所以必要的环境是需要一台linux电脑。对于没有linux电脑的,可以将一台windows装成ubuntu系统,具体如何装可以参考百度上面的教程,推荐使用hustoj的镜像,方便易用。装系统就不多说了,网上教程很多。随便找一个。
1.2软件环境需要
到此,假设系统已经装好了。现在需要安装一些hustoj的一些必须的软件。因为hustoj的开发者是使用php+mysql,所以这些环境是必须需要的。由于linux文件的权限比较安全,如果是新手会遇到很多问题,如果只是短期比赛,局域网的比赛那么用root账户是没什么大问题的。如果是搭建公网上面的OJ,还是建议将用户的文件权限设置好,文件的所属用户设置好,解决安全性的问题。
下面以ubuntu14.04为例,服务可以选择apache或者nginx。
首先切换到root账户,如果root没有设置,请用你装系统时用的账户修改密码
sudopasswd root
根据提示修改密码后
suroot
切换到root,安装相应软件
apt-getinstall update
apt-getinstall php5.0
apt-getinstall apache2
apt-getinstall mysql-server
apt-getinstall mysql-client
apt-get-f install
我们装好了必要的软件。
1.3下载项目
到github上找到hustoj下载下来。
下载地址https://github.com/zhblue/hustoj[1]。
解压后或得到几个文件
图1
解压后文件如图1。Wiki是该项目的说明。我们需要的文件是trunk这个文件夹里面的。进入文件夹trunk会有图3这些东西。Install里面是安装的脚本,core是判题服务的代码,web是前端的文件。
1.4web配置
因为我们装的是apache,所以我们把web文件夹里面的东西放到/var/www/html/
所以,我们进入删除 html目录
图2
rm-r /var/www/html/
mvweb/ /var/www/html
chown-R www-data:www-data /var/www/html/
用浏览器进入看看web是否安装成功。如果如图2所示就代表前端安装成功了。
图3
下面就是修改配置文件,配置文件在/var/www/html/include/db_info.inc.php
用vim打开他修改它对应的信息。
static$DB_HOST="localhost";数据库的服务器地址
static$DB_NAME="jol";数据库名
static$DB_USER="root";数据库用户名
static$DB_PASS="root";数据库密码//connect db
static$OJ_NAME="HUSTOJ";OJ的名字,将取代页面标题等位置HUSTOJ字样。
static$OJ_HOME="./";OJ的首页地址
static$OJ_ADMIN="root@localhost";管理员email
static$OJ_DATA="/home/judge/data";测试数据所在目录,实际位置。
static$OJ_BBS="discuss";//"bbs"论坛的形式,discuss为自带的简单论坛,bbs为外挂论坛,参考bbs.php代码。
static$OJ_ONLINE=false;是否使用在线监控,需要消耗一定的内存和计算,因此如果并发大建议关闭
static$OJ_LANG="cn";默认的语言,中文为cn
static$OJ_SIM=true;是否显示相似度检测的结果。
static$OJ_DICT=false;是否启用在线英字典
static$OJ_LANGMASK=1008;//1mC2mCPP 4mPascal 8mJava 16mRuby 32mBash 1008 for security reason tomask all other language 用掩码表示的OJ接受的提交语言,可以被比赛设定覆盖。
static$OJ_EDITE_AREA=true;//是否启用高亮语法显示的提交界面,可以在线编程,无须IDE。
static$OJ_AUTO_SHARE=false;//true:自动分享代码,启用的话,做出一道题就可以在该题的Status中看其他人的答案。
static$OJ_CSS="hoj.css";默认的css,可以选择dark.css和gcode.css,具有有限的界面制定效果。
static$OJ_SAE=false;//是否是在新浪的云平台运行web部分
static$OJ_VCODE=true;是否启用图形登录、注册验证码。
static$OJ_APPENDCODE=false;是否启用自动添加代码,启用的话,提交时会参考$OJ_DATA对应目录里是否有append.c一类的文件,有的话会把其中代码附加到对应语言的答案之后,巧妙使用可以指定main函数而要求学生编写main部分调用的函数。
static$OJ_MEMCACHE=false;是否使用memcache作为页面缓存,如果不启用则用/cache目录
static$OJ_MEMSERVER="127.0.0.1";memcached的服务器地址
static$OJ_MEMPORT=11211;memcached的端口
static$OJ_RANK_LOCK_PERCENT=0;//比赛封榜时间的比率,如5小时比赛设为0.2则最后1小时封榜。
static$OJ_SHOW_DIFF=false;//显示WrongAnswer时的对比[2]
1.5判题服务的搭建
进入图3的core目录里面,找到make.sh。首先chmod+x make.sh 然后运行这个文件.接着编译好后会出现两个可执行程序分别把这两个程序放在/usr/bin里面。
#!/bin/bash
#beforeinstall check DB setting in
# judge.conf
# hustoj-read-only/web/include/db_info.inc.php
# and downhere
#and runthis with root
#CENTOS/REDHAT/FEDORAWEBBASE=/var/www/html APACHEUSER=apache
WEBBASE=/var/www/html
APACHEUSER=www-data
DBUSER=root
DBPASS=root
#try installtools
sudo apt-getinstall make flex g++ clang libmysql++-dev php5 apache2 mysql-serverphp5-mysql php5-gd php5-cli mono-gmcs subversion
sudo/etc/init.d/mysql start
sudo yum -yupdate
sudo yum -yinstall php httpd php-mysql mysql-server php-xml php-gd gcc-c++ mysql-devel php-mbstring glibc-static flex
sudo/etc/init.d/mysqld start
sudo svncheckout https://github.com/zhblue/hustoj/trunk/trunkhustoj-read-only
#create userand homedir
sudo /usr/sbin/useradd -m -u 1536 judge
#compile andinstall the core
cdhustoj-read-only/core/
sudo./make.sh
cd ../..
#install weband db
sudo cp -Rhustoj-read-only/web $WEBBASE/JudgeOnline
sudo chmod-R 771 $WEBBASE/JudgeOnline
sudo chown-R $APACHEUSER $WEBBASE/JudgeOnline
sudo mysql-h localhost -u$DBUSER -p$DBPASS < db.sql
#create workdir set default conf
sudo mkdir /home/judge
sudo mkdir /home/judge/etc
sudo mkdir /home/judge/data
sudo mkdir /home/judge/log
sudo mkdir /home/judge/run0
sudo mkdir /home/judge/run1
sudo mkdir /home/judge/run2
sudo mkdir /home/judge/run3
sudo cpjava0.policy judge.conf /home/judge/etc
sudo chown-R judge /home/judge
sudo chgrp-R $APACHEUSER /home/judge/data
sudo chgrp-R root /home/judge/etc /home/judge/run?
sudo chmod775 /home/judge /home/judge/data /home/judge/etc /home/judge/run?
#boot upjudged
sudo cpjudged /etc/init.d/judged
sudo chmod+x /etc/init.d/judged
sudo ln -s/etc/init.d/judged /etc/rc3.d/S93judged
sudo ln -s/etc/init.d/judged /etc/rc2.d/S93judged
sudo/etc/init.d/judged start
sudo/etc/init.d/apache2 restart
sudo/etc/init.d/httpd restart
执行完这些语句,重启之后就可以了。
1.6添加超级管理员
然后在浏览器上进入注册界面。注册一个账户,之后进入数据库修改数据库,添加超级管理员。意思就是将那个用户插入到表privilege。图4是表privilege的结构。在数据库执行执行插入sql语句。
insert intoprivilege(user_id,rightstr) values('admin','administrator');
图4
如果一切正常这就代表你的OJ搭建好了。但是在搭建的过程中总不是一帆风顺的。
2搭建过程中常见问题
2.1搭建好web后在浏览器不能访问
首先有可能是apache没有正常运行,或者没有给web正确的文件权限。看看web配置文件是否修改正确。打开配置文件看看,经常填错的数据库用户名和数据库名,再或者就是将配置文件改坏了,比如删掉了双引号,导致代码编译错误。这些问题解决办法,很简单我就不再多说了。所以在安装apache的时候我们可以一个一个排除问题。先看看apache是否在正常运行。可能是apache没有正常打开用命令查看apache是否正常运行。
/etc/init.d/apache2status
如果出问题了就得去apache官网查查帮助文档。解决了这个问题,看看php和apache有没有弄好,看看apache能不能解析运行php代码。可以在/var/www/html/index.php写上一段测试代码,试试能不能输出helloworld,如果不能说明apache没有加载php模块。
问题一个一个排除,这几个问题排除之后还不能运行,就很可能文件在下载或者解压的过程中文件有所损坏。
2.2不判题可能原因
首先看看判题机的配置文件写好了没,配置文件
目录为:/home/judge/etc/judge.conf
OJ_HOST_NAME=127.0.0.1如果用mysql连接读取数据库,数据库的主机地址
OJ_USER_NAME=root数据库帐号
OJ_PASSWORD=root数据库密码
OJ_DB_NAME=jol数据库名称
OJ_PORT_NUMBER=3306数据库端口
OJ_RUNNING=4judged会启动judge_client判题,这里规定最多同时运行几个judge_client
OJ_SLEEP_TIME=5judged通过轮询数据库发现新任务,轮询间隔的休息时间,单位秒
OJ_TOTAL=1老式并发处理中总的judged数量
OJ_MOD=0老式并发处理中,本judged负责处理solution_id按照TOTAL取模后余数为几的任务。
OJ_JAVA_TIME_BONUS=2Java等虚拟机语言获得的额外运行时间。
OJ_JAVA_MEMORY_BONUS=512Java等虚拟机语言获得的额外内存。
OJ_SIM_ENABLE=0是否使用sim进行代码相似度的检测
OJ_HTTP_JUDGE=0是否使用HTTP方式连接数据库,如果启用,则前面的HOST_NAME等设置忽略。
OJ_HTTP_BASEURL=http://127.0.0.1/JudgeOnline使用HTTP方式连接数据库的基础地址,就是OJ的首页地址。
OJ_HTTP_USERNAME=admin使用HTTP方式所用的用户帐号(HTTP_JUDGE权限),该帐号登录时不能启用VCODE图形验证码,但可以登录成功后启用。
OJ_HTTP_PASSWORD=admin密码
OJ_OI_MODE=0是否启用OI模式,即无论是否出错都继续判剩余的数据,在ACM比赛中一旦出错就停止运行。
OJ_SHM_RUN=0是否使用/dev/shm的共享内存虚拟磁盘来运行答案,如果启用能提高判题速度,但需要较多内存。
OJ_USE_MAX_TIME=1是否使用所有测试数据中最大的运行时间作为最后运行时间,如果不启用则以所有测试数据的总时间作为超时判断依据。[3]
这个是配置文件的注释,按照你想要的,数据库密码正确填写,这里写配置文件不需要添加引号.重启判题机,由于有很多依赖kill命令不能够干掉这个进程,所以需要用到-9这个参数。题目的测试数据如果不能够上传,可能是/home/judge/data/这个目录文件读写权限有点问题,我们可以直接把他设为777
chmod -R 777/home/judge/data/
3 hustoj的文件结构
3.1前台文件结构
图5
后台管理的相关文件admin,管理的相关php文件;
bbs是讨论版的相关代码;
template是前台的一些模板,flat-ui,bs,bs3。
其他是一些插件的包含文件。
3.2判题部分的文件结构
data etc log run0 run1 run2 run3
判题部分,文件,data是放测试数据的文件夹,里面还是文件夹,文件夹名字是题目的id,每个题目一个文件夹,文件夹里面是输入输出,可以是多组测试数据。多组测试数据文件名字相同的看的是后缀.in是输入文件。同样的文件名对应的.out文件是输出文件。如果不是特判就是简单对比文件是否一样,如果是特判,那么特判需要自己写一个特判的规则,放在当前的文件夹。etc/文件夹是放判题的配置文件以及判题的服务程序,和主程序。Run0~3 对应的文件夹是编译用户提交代码并运行的地方。里面是一个小的linux环境,边沟编译运行代码。
4.OJ安全性问题
评测鸡的实现,已经有了很多不考虑安全问题的版本。因此这里只讨论安全问题。
我大概把评测攻击分为两部分:编译时攻击、运行时攻击。
编译时攻击
编译时攻击是很多评测鸡并没有防范的。例如下面几种攻击情况:
编译超时
#include # Linux
#include
这种攻击方式的原理是,让编译器读一个永远也读不完的文件,把评测鸡卡死在编译阶段。BZOJ已经被这段代码卡掉很多次了。
示例:提交记录#3019- PYOJ
解决办法:设置编译的时间限制,或者用vfk的办法,把编译器监控起来。
输出巨大文件
我们在做数论题的时候玩过这种事情:inta[10000000]={1,0},这样编译出来的文件会特别大。在我的机器上,它的大小是39MB。
我们想到,让编译器生成一个极大的文件。下面是一个针对gcc的编译炸弹,从wikicoding.org蒯来的。全文如下:
main[-1u]={1};
编译这段代码之后,会生成16GB的文件。
示例:提交记录#2912- PYOJ
解决办法:设置编译出来的可执行文件的大小限制。
include攻击
include攻击同时被vfleaking(UOJ)和李扬(QDUOJ)描述。向二位致敬。
include攻击是指:用C/C++代码include网站的配置文件,然后通过返回的编译错误信息,读取密码。
例如。对于hustoj,可以采用下面的代码:
#include"/var/www/JudgeOnline/include/db_info.inc.php"
这份代码将会include数据库的配置文件。上交这份代码,将会返回如下的编译信息:
事发后,hustoj的这个漏洞已经在一年前修复。
示例:提交记录#3020- PYOJ
我的解决方法:
评测鸡和服务鸡必须隔离。
设置好文件权限(hustoj就是这样修复漏洞的)。
最好不要告诉用户编译错误的信息。
vfk的解决办法:把编译器监视起来。
另外riteme提出了另一种鬼畜的解决办法。见[漏洞报告]include攻击·Issue #14 · hzxie/voj 。
需要注意的一点:不要想着用检查用户代码的形式规避攻击。C/C++的define功能可以轻易绕过这些检查。
运行时攻击
运行时攻击,已经有比较好而且通用的解决方案。解决这个问题难度并不大。
运行系统指令
system("shutdownnow");
这种攻击方式很早就被解决了。
示例:提交记录#3021- PYOJ
解决方法:用沙箱跑评测,可以采用ptrace。但是我准备用一种更暴力的方案。直接把程序扔进docker里面跑,它爱玩成什么样就玩成什么样吧233。
fork炸弹
曾经有OJ被选手程序不停地fork()搞炸了。
我没有拿到代码,因此这里没法分析。
故意运行超时
while(1):pass
然后突然交一大堆这种程序,以达到类似DDOS的效果……
这种东西很没技术含量。下面是一份针对hustoj的脚本:
https://gist.github.com/Ruanxingzhi/9f0caba5e2d8a690ae1e89bcfb92abba
效果是用1000个账号,每个账号提交10次,每个账号在提交之后休息11s。
体现为:两分钟内,OJ上面多了一万份提交。
解决方法:注册的时候设置验证码,如果一个账号短时间内多次提交,在这个账号下一次提交的时候也需要验证码。
请注意,注册的时候,只用邮箱验证,不用验证码是不行的!
总结
我玩了这么久的OJ,一共也就发现这几种攻击方式……
但是好像很多OJ对编译时攻击防范并不好。
点蜡烛。希望OJ做得越来越好吧。[4]
以上是知乎大神总结的,还有就是通过插件的漏洞进行攻击,比如kindedit之前的漏洞,可以绕过cookie上传文件。对了,最好把配置文件弄成judge用户不可以操作的权限。这样数据库密码免得泄露了。
时间、资源的限制:
内存:我使用了rlimit进行控制,同时也方便在运行结束后获得内存使用情况的数据,不过有一个“缺点”就是如果是声明了一个超大的空间但从未访问使用就不会被统计进来(经过观察发现很多ACM或者OI比赛也都是这么处理的,所以应该不算是一个问题)。
时间:首先同样也是使用rlimit进行CPU时间控制。注意它只能控制CPU时间,不能控制实际运行时间,所以像是sleep或者IO阻塞之类的情况是没有办法的,所以还在额外添加了一个alarm来进行实际时间的限制。按照大部分比赛的管理,最终统计的时间是CPU时间。
文件句柄:同样可以通过rlimit来实现,以保证程序不要打开太多文件。不过其实文件这一块问题是比较多的,如果可行的话最好还是使用stdio然后管道重定向,完全禁止程序的文件IO操作。
访问控制:
通过chroot建立一个jail,将程序限制在指定目录中运行。由于是比赛程序,使用的动态链接库很有限,所以直接静态编译,从而使得运行目录中连.so都不需要。
进行必要的权限控制,例如将输入文件和程序文件本身设置为程序的运行用户只读不可写。
权限控制:
监控程序使用root权限运行,完成必要准备后 fork并切换为受限用户(比如nobody)来运行程序。
rlimit 设置的都是hardlimit ,非root无法修改。
正确设置运行用户之后,之前由root创造的jail受限用户是无法逃出的。
系统调用控制:
上面这些(尤其是第一步)是有很大问题,就算不是root,也还能做到很多事情。且不说fork之类的,光是那个alarm,就可以很轻松的把计时器取消了或者干脆主动接收这个信号。所以最根本的还是需要使用ptrace之类的调试器附着上程序,监控所有的系统调用,进行白名单+计数器(比如exec和open)过滤。这一步其实是最麻烦的(不同平台的系统调用号不一样,我们使用的是strace项目里头整理的调用号)。
更进一步:
如果你对操作系统更熟悉,那么还有一些更有趣的事情可以做。比如Linux下的seccomp功能(seccomp- Wikipedia ,ChromeLinux 版就在沙盒中使用了这个技术),尤其是后期加入了seccomp-bpf之后变得更加易用。还比如SELinux也可以作为defend-by-depth的一环。另外,cgroup其实也可以用得上。
结束语
搭建了hustoj,只会这些还不够,最好自己写一下判题机什么的,要深入了解学习才有收获。如果只是按照教程弄一下,并不会有很大的收获。作为了名ACM理解判题原理是很有用的,这样对解题也是很有用的。学无止境。
参考文献
[1]ZHBLUE,HUSTOJWIKI,https://github.com/zhblue/
[2]LIUXINGDEBLOG,hustoj永久等待或编译并评判解决方法hustoj,https://www.myvary.cn/index.php/2016/05/11/hustoj-ing/,2016-05-11
[3]OMG_BY,在linux上ubuntu搭建hustOJ系统,http://www.cnblogs.com/OMG-By/p/5978831.html
[4]RUANXINGZHI,OJ技术思考:评测安全,https://zhuanlan.zhihu.com/p/26984739