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)
Это было полезно, и я почти что просто украл этот код, так что для этого парня было достойным достойным примером.