Python: Защита ненадежных скриптов/подпроцессов с chroot и chjail?

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

Для этого я рассмотрел подход к тому, чтобы иметь несколько папок (по одному для каждого плагина) и несколько сценариев оболочки/питона, названных в честь предопределенных имен для различных событий, которые могут произойти.

Одним из примеров является наличие файла on_pdf_uploaded.py, который выполняется, когда PDF файл загружается на сервер. Для этого я бы использовал инструменты подпроцесса Python.

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

Поскольку код плагина исходит из ненадежного источника, я хочу сделать его максимально безопасным. Моя идея состояла в том, чтобы выполнить код в подпроцессе, но поместить его в chroot тюрьму с другим пользователем, чтобы он не мог получить доступ к каким-либо другим ресурсам на сервере.

К сожалению, я ничего не мог найти об этом, и я не хотел бы полагаться на ненадежный script, чтобы попасть в тюрьму.

Кроме того, я не могу поместить основной/вызывающий процесс в chroot-тюрьму, поскольку код плагина может выполняться одновременно в нескольких процессах, пока сервер отвечает на другие запросы.

Итак, вот вопрос: как я могу выполнять подпроцессы/скрипты в chroot-тюрьме с минимальными привилегиями, чтобы защитить остальную часть сервера от повреждения от неисправного, ненадежного кода?

Спасибо!

Ответ 1

После создания вашей тюрьмы вы вызываете os.chroot из своего источника Python, чтобы войти в нее. Но даже тогда все разделяемые библиотеки или файлы модулей, уже открытые интерпретатором, все равно будут открыты, и я не знаю, какими будут последствия закрытия этих файлов через os.close; Я никогда не пробовал.

Даже если это работает, настройка chroot - это большое дело, поэтому убедитесь, что выгода стоит того. В худшем случае вам нужно будет убедиться, что вся рабочая среда Python со всеми модулями, которые вы собираетесь использовать, а также все зависимые программы и разделяемые библиотеки и другие файлы из /bin, /lib и т.д. Доступны в каждой файловой системе, находящейся в тюрьме, И, конечно же, это не защитит другие типы ресурсов, т.е. Сетевые адресаты, базу данных.

Альтернативой может быть чтение ненадежного кода в виде строки, а затем exec code in mynamespace, где mynamespace - словарь, определяющий только символы, которые вы хотите открыть ненадежному коду. Это будет своего рода "тюрьма" внутри виртуальной машины Python. Возможно, вам придется проанализировать источник, который сначала ищет такие вещи, как import, если только замена встроенной функции __import__ не перехватит это (я не уверен).

Ответ 2

Возможно, что-то вроде этого?

# main.py
subprocess.call(["python", "pluginhandler.py", "plugin", env])

Затем

# pluginhandler.py
os.chroot(chrootpath)
os.setgid(gid) # Important! Set GID first! See comments for details.
os.setuid(uid)
os.execle(programpath, arg1, arg2, ..., env)
# or another subprocess call 
subprocess.call["python", "plugin", env])

РЕДАКТИРОВАТЬ: Хотелось использовать fork(), но я не совсем понял, что он сделал. Посмотрел. новый код!

# main.py
import os,sys
somevar = someimportantdata
pid = os.fork()
if pid:
    # this is the parent process... do whatever needs to be done as the parent
else:
    # we are the child process... lets do that plugin thing!
    os.setgid(gid) # Important! Set GID first! See comments for details.
    os.setuid(uid)
    os.chroot(chrootpath)
    import untrustworthyplugin
    untrustworthyplugin.run(somevar)
    sys.exit(0)

Это было полезно, и я почти что просто украл этот код, так что для этого парня было достойным достойным примером.