Запуск произвольного недружественного кода Python на моем сервере

Я делаю игру, в которой пользователи могут писать программы на Python для управления роботами, которые сражаются друг с другом. Каждый ход (в игре за тысячу ходов) их сценарий будет запускаться на моем сервере, чтобы определить следующий ход робота. Как я могу предотвратить попадание этих пользователей на мой сервер?

Я подумал / исследовал следующее:

  • evalих код в ограниченной среде (т.е. с __builtins__ отключен)
  • используя специфичные для ОС тюрьмы, такие как chroot/ptrace
  • запустить код в виртуальной машине какой-то

У меня есть программа на Python, которая запускает пользовательский скрипт тысячу раз. Как я могу обеспечить максимальную общую продолжительность в одну минуту, ограничить ресурсы их памяти, запретить им доступ к любым файлам или сетевым подключениям и т. Д.? Каким было бы идеальное решение или комбинация решений?

3 ответа

Я работаю над PythonAnywhere, который представляет собой платформу как платформу Python, что означает, что мы выполняем много ненадежного кода от имени наших пользователей - новая интерактивная консоль на первой странице Python.org является нашей, и его ограничения, вероятно, не слишком далеко от вашего.

Я рекомендую использовать виртуализацию на уровне ОС; все на уровне языка, вероятно, будет менее безопасным. Мы используем chroot и cgroups по историческим причинам, но если бы мы начинали с нуля сегодня, я думаю, что мы использовали бы контейнеры Linux (LXC) или Docker. LXC - это, по сути, куча умных оболочек для chroot и cgroups, и так далее, что облегчает их использование, так что вы можете действительно быстро раскрутить эфемерную виртуальную машину. А Docker - еще более простая в использовании оболочка для LXC. Оба супер-быстрые - вы можете запустить новую виртуальную машину менее чем за секунду.

Я посмотрел на ваш профиль и решил, что это постоянный труд любви! так что здесь идет..

Учитывая ваши требования, я предполагаю, что вы планируете что-то, что будет доступно через i/webs.


Относительно вашей второй строки запроса (; которая предотвращает дублирование этого Q;):

У меня есть программа на Python, которая запускает пользовательский скрипт тысячу раз. Как я могу обеспечить максимальную общую продолжительность в одну минуту, ограничить ресурсы их памяти, запретить им доступ к любым файлам или сетевым подключениям и т. Д.? Каким было бы идеальное решение или комбинация решений?

Как вы, наверное, знаете, это хорошо протоптанная земля, восходящая к 1980-м годам. Повторное изобретение иногда, но не всегда лучше, из информированного POV. Для идей о том, как разработать временные инструкции для игроков и так далее, вы можете взглянуть на источники некоторых связанных текущих проектов, которые уже существуют в дебрях. например:

Спойлер:

- Робокод
Этот проект имеет долгую историю; находится в руках сообщества с 2005 года и активно поддерживается. Источники - Java. важно чтение имо.
https://github.com/robo-code/robocode

- код боя:
Это аромат javascript, работающий в Интернете. так что это должно быть очень полезно, помогая вам в этом с вашим питоническим подходом онлайн. сайт теперь является коммерческим / закрытым. тем не менее, вы можете увидеть исходные источники, которые были представлены для игры на github 2012. По крайней мере, это может дать идеи о том, как представить ваш игровой контент миру:)
https://github.com/timehome/game-off-2012
(кстати, код боя стал вторым!)


Ваша первая строка запроса на всем протяжении ТАК. Смотрите здесь. Тем не мение:

Самый дешевый (во всех смыслах) вариант будет chroot острог. Следующим самым дешевым вариантом будет что-то вроде Linux-VServer. Производительность, по-видимому, близка к родной, хотя у меня нет опыта использования этого программного обеспечения. В противном случае, да, полная виртуализация с использованием xen или чего-либо еще.

имо:

Полная виртуализация привнесет головные боли и непосильные накладные расходы.

Если вы осторожны, вы можете сделать это с помощью простого chroot острог.

  • Иметь тюрьму исключительно как одноразовую "арену" для запуска кодов. убедитесь, что он работает с минимальными ресурсами и привилегиями, необходимыми для выполнения работы.

  • Полностью отделите ваше веб-приложение от тюрьмы (например, для тюрьмы не должно быть доступа к сети и т. Д.).

Таким образом, рабочий процесс может быть таким:

  1. загружать игровые запросы через ваше веб-приложение.

  2. благослови игровые запросы. вы сказали " произвольные недружественные коды ", но на самом деле игра будет иметь некоторые формальные параметры; вы только будете хотеть подмножество python функциональность. по крайней мере, проверить это.

  3. выстраивать в очередь явно вменяемые игровые запросы и отправлять их на арену (ака тюрьма).

  4. если вы действительно параноик, создайте новую чистую арену / тюрьму для каждой новой игры.

  5. запустить игру на арене. сохранение каждого отдельного игрового состояния (во внутреннем недорогом формате) в базе данных, локальной для тюрьмы.

  6. снаружи тюрьмы, и когда игра будет завершена, предоставьте веб-приложению базу данных о состоянии игры, а затем передайте ее на веб-сайт i / we.

  7. все сделано. иди и выпей!


Примечания по настройке тюрьмы Python.

Вот как я попал в этот пост. Я искал конкретные инструкции о том, как заключить в тюрьму python используя (очень отличный) джейлкит. но не мог найти много.. поэтому, приготовив свой собственный, я тогда посмотрел, есть ли место для этого на SO.

Имейте в виду, что некоторые дистрибутивы nix, такие как Unbuntu и Centos, используют python в их работе. в этом случае, чтобы избежать путаницы в вашей системе, вы можете захотеть собрать любую версию python требуется из источников.

Вот мой рецепт для установки python 2.7 (предполагается, что у вас уже установлен джейлкит) в Centos

Во-первых, более формальный подход к джейлкиту, включая создание выделенного пользователя, может быть следующим:

# 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

# Ad-infinitum:

# as root user:

cd /home

## create a python jail by passing jk_init the name of the sction created in /etc/jailkit/jk_init.ini
jk_init -v -j /home/jail_py python2.7

## optionally create a dedicated user
useradd -M -s /sbin/nologin prisoner
passwd prisoner

## quick sanity-check for setuids
find /home/jail_py -perm -4000 -exec ls -ldb {} \;

## test
echo "import sys; print sys.version" | jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/local/bin/python2.7
echo "import os; print 'cwd:{} uid:{}'.format(os.getcwd(),os.getuid())" | jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/local/bin/python2.7
jk_chrootlaunch -u prisoner -j /home/jail_py -x /home/jail_py/usr/local/bin/python2.7

## optional, if we need /proc
mkdir -p /home/jail_py/proc
mount -t proc /proc /home/jail_py/proc


## nuke everything
umount /home/jail_py/proc
rm -fr /home/jail_py
userdel prisoner


И вот не рекомендуемый, более специальный подход

# 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                 

# 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


Примечание: в зависимости от ваших требований вы также можете / должны монтировать /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


Вы, конечно, * не должны нуждаться в интерактивной сессии. Но все же, возможно, стоит запустить свою арену под преданным пользователем. Но ты не обязан. В любом случае, джейлкит берет на себя большую часть усилий по созданию достойного chroot острог. Вы можете взять источники и возиться с ними в соответствии с вашими потребностями (я так и сделал). или вы можете просто использовать jk_chrootlaunch, чтобы безопасно вызвать вашу арену за пределами тюрьмы.

(NB: причина, по которой мне нужно было связываться с исходниками джейлкита, заключается в том, что он использует тот же взлом "/./", что и pure-ftp. Lol)

Наконец, вы можете прочитать: http://www.unixwiz.net/techtips/chroot-practices.html


Желаю удачи, и я надеюсь, что вы все еще активны в этом.. ~i41 хотел бы увидеть питонную реализацию =) мы привыкли играть в эту игру навязчиво в универе Mat-Peck c-zone stylee;)

Нет встроенного способа запуска изолированного кода в cpython, но есть в pypy.

http://pypy.org/features.html

Есть несколько других способов, описанных в вики Python (например, с помощью jailkit), но они, кажется, имеют различные недостатки.

https://wiki.python.org/moin/SandboxedPython

Я бы пошел по пыпу.

Другие вопросы по тегам