【发布时间】:2018-05-20 11:06:40
【问题描述】:
我正在尝试构建一个 Python 沙箱,用于在最小且安全的环境中运行学生的代码。我打算将它运行到一个容器中并限制它对该容器资源的访问。因此,我目前正在设计应该运行到容器中并处理对资源的访问的沙箱部分。
目前,我的规范是限制进程使用的时间和内存量。我还需要能够通过stdin 与进程通信,并在执行结束时捕获retcode、stdout 和stderr。
此外,程序可能会进入无限循环并通过stdout 或stderr 填满内存(我有一个学生的程序因此而使我的容器崩溃)。因此,我还希望能够限制恢复的stdout 和stderr 的大小(达到一定限制后,我可以杀死进程并忽略其余的输出。我不关心这些额外的数据,因为它很可能是一个有问题的程序,应该被丢弃)。
目前,我的沙盒几乎可以捕获所有内容,这意味着我可以:
- 根据需要设置超时时间;
- 设置进程使用的内存限制;
- 通过
stdin(现在是给定的字符串)为进程提供数据; - 获取最终的
retcode、stdout和stderr。
这是我当前的代码(我尽量保持较小的示例):
MEMORY_LIMIT = 64 * 1024 * 1024
TIMEOUT_LIMIT = 5 * 60
__NR_FILE_NOT_FOUND = -1
__NR_TIMEOUT = -2
__NR_MEMORY_OUT = -3
def limit_memory(memory):
import resource
return lambda :resource.setrlimit(resource.RLIMIT_AS, (memory, memory))
def run_program(cmd, sinput='', timeout=TIMEOUT_LIMIT, memory=MEMORY_LIMIT):
"""Run the command line and output (ret, sout, serr)."""
from subprocess import Popen, PIPE
try:
proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE,
preexec_fn=limit_memory(memory))
except FileNotFoundError:
return (__NR_FILE_NOT_FOUND, "", "")
sout, serr = "".encode("utf-8"), "".encode("utf-8")
try:
sout, serr = proc.communicate(sinput.encode("utf-8"), timeout=timeout)
ret = proc.wait()
except subprocess.TimeoutExpired:
ret = __NR_TIMEOUT
except MemoryError:
ret = __NR_MEMORY_OUT
return (ret, sout.decode("utf-8"), serr.decode("utf-8"))
if __name__ == "__main__":
ret, out, err = run_program(['./example.sh'], timeout=8)
print("return code: %i\n" % ret)
print("stdout:\n%s" % out)
print("stderr:\n%s" % err)
缺少的功能是:
设置
stdout和stderr的大小限制。我在网上查看了几次尝试,但都没有真正奏效。将函数附加到
stdin比仅使用静态字符串更好。该函数应连接到管道stdout和stderr并将字节返回到stdin。
有人知道吗?
PS:我已经看过了:
【问题讨论】:
-
@zwer:是的,关于沙箱与主机系统的不完全分离,您是对的。这将是下一步,我打算为此使用 QEMU。但是,我在这里发布的代码应该在容器内运行(我可能应该提到它)。关于
stdin、stdout和stderr的缓冲区,这正是我想要实现的,但我不知道该怎么做。这就是我问的原因。
标签: python python-3.x subprocess sandbox