在我的服务器上运行任意不友好的Python代码

在我的服务器上运行任意不友好的Python代码,python,virtual-machine,chroot,ptrace,jail,Python,Virtual Machine,Chroot,Ptrace,Jail,我正在制作一个游戏,用户可以编写Python程序来控制相互争斗的机器人。每回合(千回合游戏)他们的脚本都会在我的服务器上运行,以确定机器人的下一步行动。如何防止这些用户对我的服务器恶意 我想到/研究了以下几点: eval在有限的环境中评估他们的代码(即禁用\uuuu内置\uuuu) 使用特定于操作系统的监狱,如chroot/ptrace 在某种VM中运行代码 我有一个Python程序,可以运行用户脚本一千次。我如何强制执行一分钟的最大总持续时间,限制其内存资源,阻止其访问任何文件或网络连接,

我正在制作一个游戏,用户可以编写Python程序来控制相互争斗的机器人。每回合(千回合游戏)他们的脚本都会在我的服务器上运行,以确定机器人的下一步行动。如何防止这些用户对我的服务器恶意

我想到/研究了以下几点:

  • eval
    在有限的环境中评估他们的代码(即禁用
    \uuuu内置\uuuu
  • 使用特定于操作系统的监狱,如
    chroot
    /
    ptrace
  • 在某种VM中运行代码

我有一个Python程序,可以运行用户脚本一千次。我如何强制执行一分钟的最大总持续时间,限制其内存资源,阻止其访问任何文件或网络连接,等等?理想的解决方案或解决方案组合是什么?

在cpython中没有内置的方法来运行沙盒代码,但在pypy中有

pythonwiki上还描述了一些其他方法(比如使用jailkit),但它们似乎有各种缺点


我会选择平淡的路线。

我看了你的个人资料,决定这是一项持续的爱的工作!这就是

鉴于您的需求,我假设您正在计划通过internet提供的内容

关于你的第二条查询:

我有一个Python程序,可以运行用户脚本一千次。 我如何强制执行一分钟的最大总持续时间(限制) 他们的内存资源,阻止他们访问任何文件或 网络连接等等?理想的解决方案是什么 解决方案的组合

正如你可能知道的那样,这是一个可以追溯到20世纪80年代的好地方。从知情的角度来看,再发明有时是最好的,但并不总是最好的。对于如何计算玩家时间切片的想法,你可以看看一些现有的相关项目的来源。例如:

扰流板:

-
此项目具有 ; 自2005年以来一直掌握在社区手中,并积极 保持。源代码是java。基本阅读imo.


-
这是一种javascript风格 并且在网上工作。因此,这对你的生活会有很大的帮助 你的在线蟒蛇方法。该网站现在似乎已关闭 商业/封闭来源。然而,你可以看到 为github 2012游戏提交的原始来源。在 最起码,这可能会提供如何展示您的 向全世界公布最终游戏内容:)

(顺便说一句,fightcode获得亚军!)


你的第一条询盘就到此为止了。然而:

最便宜的(从任何意义上讲)选择是
chroot
监狱。下一个最便宜的选择是这样的。虽然我没有使用此软件的经验,但性能显然接近本机。否则,是的,完全虚拟化使用或任何东西

意见:

完全虚拟化会带来头痛和不必要的开销

如果(并且只有)你很小心,你可以用一个简单的
chroot
jail

  • 把监狱仅仅作为一个一次性的“竞技场”来运行代码。确保它以完成工作所需的最低资源和权限运行

  • 将您的web应用程序与监狱完全分离(例如,监狱不需要网络访问等)

因此,工作流程可能如下所示:

  • 通过web应用上传游戏请求

  • 祝福游戏请求。你说的是“任意的不友好代码”,但实际上游戏会有一些正式参数;您只需要Python功能的一个子集。至少检查一下

  • 排队等候显然理智的游戏请求,并将它们提交到竞技场(又名监狱)

  • 如果你真的很偏执,为每一个新游戏建造一个新的干净的竞技场/监狱

  • 在竞技场上进行比赛。将每个离散的游戏状态(以内部低成本格式)保存到监狱本地的数据库中

  • 在监狱外&游戏完成后,向web应用程序提供游戏状态数据库,然后将其发布到互联网上

  • 关于设置Python监狱的说明。 我就是这样进入这个职位的。我正在寻找关于如何使用(非常优秀的)python的具体说明,但找不到太多。所以,我自己做了饭,然后看看是否有地方可以放

    请注意,某些nix发行版,如Ubuntu和Centos,在其操作中使用
    python
    。在这种情况下,为了避免破坏系统,您可能希望从源代码构建所需的任何Python版本

    下面是我在Centos上安装Python2.7的方法(假设您已经安装了jailkit)

    首先,包括创建专用用户在内的更正式的jailkit方法可能是:

    # Once-only set-up:
    
    # as root user:
    
    ## get, build and alt-install required python onto host OS
    mkdir -p /usr/local/src/python
    cd /usr/local/src/python
    wget http://www.python.org/ftp/python/2.7/Python-2.7.tgz
    tar -vxzf Python-2.7.tgz
    cd Python-2.7
    ./configure               #default ${prefix}="/usr/local"
    make
    make altinstall           #don't clobber systems' python; everythin installed under /urs/local and comes with 2.7 postfix.
    
    
    ## set-up a jailkit config for python 2.7 (& optionally to support some minimal devs)
    cat <<OOOK >> /etc/jailkit/jk_init.ini
    
    [python2.7]
    comment = python 2.7 interpreter and libraries
    paths = python2.7, /usr/local/lib/python2.7, /usr/local/include/python2.7, /etc/ld.so.conf
    devices = /dev/null, /dev/zero, /dev/random, /dev/urandom 
    
    OOOK
    
    这里有一个不推荐的,更特别的方法

    # Once-only set-up:
    
    # as root user:
    
    ## get and build required python
    mkdir -p /usr/local/src/python
    cd /usr/local/src/python
    wget http://www.python.org/ftp/python/2.7/Python-2.7.tgz
    tar -vxzf Python-2.7.tgz
    cd Python-2.7
    ./configure --prefix="/usr"         # optionally override default ${prefix} ~ we don't really need /local/ indirection for our jail
    make
    
    ## find out minimal python lib dependencies so we can add them to the jail - see jk_cp -j below..
    ldd /home/jail_py/usr/bin/python                 
    

    注意:根据您的要求,您可能还需要安装
    /proc
    。例如,如果未*装入
    /proc
    ,则运行类似的操作将失败

    cat <<OOOK | jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/local/bin/python2.7 -
    
    #thx: https://raw.github.com/pixelb/ps_mem/master/ps_mem.py
    
    import os
    import sys
    import errno
    
    
    class Proc:
        def __init__(self):
            uname = os.uname()
            if uname[0] == "FreeBSD":
                self.proc = '/compat/linux/proc'
            else:
                self.proc = '/proc'
        def path(self, *args):
            return os.path.join(self.proc, *(str(a) for a in args))
        def open(self, *args):
            try:
                return open(self.path(*args))
            except (IOError, OSError):
                val = sys.exc_info()[1]
                if (val.errno == errno.ENOENT or # kernel thread or process gone
                    val.errno == errno.EPERM):
                    raise LookupError
                raise
        def meminfo(self):
            fd=self.open("meminfo")
            for next in iter(fd.readline, ""):
                print next.replace('\n', '')
    
    
    Proc().meminfo()
    
    OOOK
    
    cat这是一个Python平台即服务(platform-as-a-service),这意味着我们代表用户运行大量不可信的代码——Python.org首页上的新交互式控制台是我们的,它的约束条件可能与您的不太远

    我建议使用操作系统级虚拟化;语言层面的任何东西都可能不那么安全。出于历史原因,我们使用chroot和cgroup,但是如果我们开始
    # Ad-infinitum:
    
    # as root user:
    
    ## create a user
    useradd -M -s /sbin/nologin prisoner
    passwd prisoner
    
    ## manually create a jail
    mkdir -p /home/jail_py
    
    ## deploy python into jail
    # IMPORTANT: be sure to export DESTDIR to avoid stomping on your system python ;)
    cd /usr/local/src/python/Python-2.7
    export DESTDIR="/home/jail_py"      # point install to /home/jail_py/${prefix}    
    make install                        # no need for altinstall since we're deploying directly to the jail
    cd /home
    
    
    ## provision jail with a copy of /etc/ld.so.conf to prevent jk_cp form chucking out warnings
    jk_cp -j /home/jail_py  /etc/ld.so.conf
    
    ## copy minimal python lib dependencies to the jail - see ldd above
    jk_cp -j /home/jail_py  /lib/libpthread.so.0 /lib/libdl.so.2 /lib/libutil.so.1 /lib/libm.so.6 /lib/libc.so.6 /lib/ld-linux.so.2
    
    ## quick sanity-check for setuids
    find /home/jail_py -perm -4000 -exec ls -ldb {} \;
    
    ## test running jailed python as user 'prisoner'
    echo "import sys; print sys.version" | jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/bin/python
    echo "import os; print 'cwd:{} uid:{}'.format(os.getcwd(),os.getuid())" | jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/bin/python
    jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/bin/python
    
    
    ## optional, if we need /proc
    mkdir -p /home/jail_py/proc
    mount -t proc /proc /home/jail_py/proc
    
    #optional, if we need some /dev/x
    jk_cp -j /home/jail_py /dev/null    
    
    
    #nuke everything
    umount /home/jail_py/proc
    rm -fr /home/jail_py
    userdel prisoner
    
    cat <<OOOK | jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/local/bin/python2.7 -
    
    #thx: https://raw.github.com/pixelb/ps_mem/master/ps_mem.py
    
    import os
    import sys
    import errno
    
    
    class Proc:
        def __init__(self):
            uname = os.uname()
            if uname[0] == "FreeBSD":
                self.proc = '/compat/linux/proc'
            else:
                self.proc = '/proc'
        def path(self, *args):
            return os.path.join(self.proc, *(str(a) for a in args))
        def open(self, *args):
            try:
                return open(self.path(*args))
            except (IOError, OSError):
                val = sys.exc_info()[1]
                if (val.errno == errno.ENOENT or # kernel thread or process gone
                    val.errno == errno.EPERM):
                    raise LookupError
                raise
        def meminfo(self):
            fd=self.open("meminfo")
            for next in iter(fd.readline, ""):
                print next.replace('\n', '')
    
    
    Proc().meminfo()
    
    OOOK