CodeJail 用于在安全沙箱中管理不受信任代码的执行。它主要面向 Python 执行而设计,但也可以用于其他语言。
安全性通过 AppArmor 强制执行。如果你的操作系统不支持 AppArmor,或者 AppArmor 配置文件的定义和配置不正确,那么 CodeJail 将无法保护执行过程。
CodeJail 被设计为可配置的,如果你正确安装它,它将为 Python 执行自动进行配置。
一个 CodeJail 沙箱由以下几个部分组成:
#) 沙箱环境。对于 Python 环境,这将是 Python 以及相关的核心包,构成一个 virtualenv。本文档中通称其为 。该环境是只读的,并在所有沙箱实例之间共享。
沙箱代码还可以在 AppArmor 配置文件允许的范围内访问操作系统库。
#) 沙箱执行目录。这是一个临时只读目录,命名类似 /tmp/codejail-XXXXXXXX,其中包含提交的代码(./jailed_code)、可选的其他文件,以及一个可写的临时目录(./tmp),供提交的代码用作临时空间。
提交的代码通常是学生提交到服务器上进行测试的代码,而其他文件通常是包含评分或工具库的 python_lib.zip。
要运行 CodeJail,需要两个用户帐户。一个帐户是运行代码的主帐户,该帐户有权创建沙箱。此帐户将被称为 <SANDBOX_CALLER>。第二个帐户是沙箱运行所使用的帐户。这通常是 sandbox 帐户。
该库目前经测试可在以下版本下运行
Python:
Ubuntu:
(请注意,沙箱内部使用的 Python 版本可能与该库本身使用的版本不同。)
以下说明详细介绍了如何配置你的操作系统,以使 CodeJail 能够安全地执行 Python 代码。不过,你也可以设置 codejail.safe_exec.ALWAYS_BE_UNSAFE = True,直接在机器上执行提交的 Python 代码,而不提供任何安全性。这对于不太关心安全的开发者机器来说可能没问题,并且可以测试与 CodeJail API 的集成。但是,如果任何输入来自不受信任的来源,则绝不能使用此选项。切勿在生产系统中使用此选项。
为了确保 Python 执行的安全,你将创建一个新的 virtualenv。这意味着你将拥有两个 virtualenv:一个用于你的项目的主 virtualenv,另一个用于沙箱 Python 代码。
为新的 virtualenv 选择一个位置,称其为 。如果你将其放在现有 virtualenv 旁边,并加上 -sandbox 后缀,它将自动被检测并使用。因此,如果你现有的 virtualenv 位于 /home/chris/ve/myproj,请将 设为 /home/chris/ve/myproj-sandbox。
运行 LMS 的用户是 <SANDBOX_CALLER>,例如,在你的开发机器上是你自己,在服务器上是 www-data。
这里的其他细节取决于你的配置:
使用 --copies 创建新的 virtualenv,以确保有一个独立的 Python 可执行文件来加以限制::
$ sudo python3.12 -m venv --copies
默认情况下,virtualenv 只会符号链接到系统 Python,而某些操作系统上 apparmor 的默认配置可能会阻止对其应用隔离。
(可选)如果你有特定的包希望提供给沙箱代码使用,可以通过激活沙箱虚拟环境并使用 pip 安装它们::
$ /bin/pip install -r requirements/sandbox.txt
添加一个沙箱用户::
$ sudo addgroup sandbox $ sudo adduser --disabled-login sandbox --ingroup sandbox
让 Web 服务器以 sandbox 身份运行沙箱 Python。创建文件 /etc/sudoers.d/01-sandbox::
$ sudo visudo -f /etc/sudoers.d/01-sandbox
<SANDBOX_CALLER> ALL=(sandbox) SETENV:NOPASSWD:/bin/python <SANDBOX_CALLER> ALL=(sandbox) SETENV:NOPASSWD:/usr/bin/find <SANDBOX_CALLER> ALL=(ALL) NOPASSWD:/usr/bin/pkill
(请注意,find 二进制文件可以运行任意代码,因此对于非 codejail 用途来说,这并非安全的 sudoers 文件。)
编辑 AppArmor 配置文件。这是一个文本文件,用于指定对沙箱 Python 可执行文件的限制。该文件必须位于 /etc/apparmor.d 中,并且应根据可执行文件命名,将斜杠替换为点。例如,如果你的沙箱 Python 位于 /home/chris/ve/myproj-sandbox/bin/python,那么你的 AppArmor 配置文件必须是 /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python。
请参阅 apparmor-profiles/ 中的示例配置文件。该配置文件必须进行自定义,以匹配你的沙箱位置。
解析配置文件::
$ sudo apparmor_parser --replace --warn=all --warn=no-debug-cache --Werror <APPARMOR_FILE>
重新激活你项目的主 virtualenv。
禁用使用 PAM 设置 rlimit::
sed -i '/pam_limits.so/d' /etc/pam.d/sudo
如果你的 CodeJail 已正确配置为使用 safe_exec,请在 Python 终端中尝试以下命令::
import codejail.jail_code
codejail.jail_code.configure('python', '<SANDENV>/bin/python', user='sandbox')
import codejail.safe_exec
jailed_globals = {}
codejail.safe_exec.safe_exec("output=open('/etc/passwd').read()", jailed_globals)
print(jailed_globals) # should be unreachable if codejail is working properly
这应该会抛出一个异常。
如果你需要更改安装到沙箱 virtualenv 中的包,则需要禁用 AppArmor,因为你的沙箱 Python 没有权限修改其 site-packages 目录中的文件。
为你的沙箱禁用 AppArmor::
$ sudo apt-get install apparmor-utils # if you haven't already $ sudo aa-complain /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python
安装或以其他方式更改已安装的包::
$ pip install -r requirements/sandbox.txt
为你的沙箱重新启用 AppArmor::
$ sudo aa-enforce /etc/apparmor.d/home.chris.ve.myproj-sandbox.bin.python
要运行测试,你必须执行标准的安装步骤。然后你必须设置以下环境变量::
$ export CODEJAIL_TEST_USER=<owner of sandbox (usually 'sandbox')>
$ export CODEJAIL_TEST_VENV=<SANDENV>
使用 Makefile 运行测试::
$ make tests
如果未配置代理模式,部分代理测试将被跳过。
CodeJail 具有足够的通用性,可以在各种项目中用于运行不受信任的代码。它提供了两层:
jail_code.py 提供子进程的安全执行。它通过在由 AppArmor 管理的子进程中运行程序来实现这一点。
safe_exec.py 提供对 Python 执行的专门处理,使用 jail_code 来提供 Python exec 语句的语义。
CodeJail 在 AppArmor 下运行程序。AppArmor 是操作系统提供的一项功能,用于限制程序可以访问的资源。为了以有限的资源访问权限运行 Python 代码,我们创建一个新的 virtualenv,然后在一个 AppArmor 配置文件中指定该 Python 可执行文件,并在该配置文件中限制资源。CodeJail 将使用该可执行文件执行所提供的 Python 程序,AppArmor 将自动限制其可以访问的资源。CodeJail 还使用 setrlimit 来限制进程可用的 CPU 时间和/或内存。
codejail.jail_code 接收要运行的程序、要复制到其环境中的文件、命令行参数以及一个 stdin 流。它创建一个临时目录,创建或复制所需文件,生成一个子进程来运行代码,并返回该进程的输出和退出状态。
codejail.safe_exec 模拟 Python 的 exec 语句。它接收一段 Python 代码,并使用 jail_code 运行它,同时以副作用的方式修改 globals 字典。safe_exec 通过将 globals 序列化为 JSON 传入子进程,并在子进程中将结果序列化传出来实现这一点。
如果 codejail 或 AppArmor 配置不当,codejail 可能会默认以不安全的方式(无沙箱)运行代码。它并非默认安全。集成 codejail 的项目应考虑在启动时、在接受不受信任的输入之前,加入一个运行时测试套件,以检查隔离是否正确。
沙箱隔离是通过 AppArmor 隔离实现的。codejail 有助于实现这一点,但如果没有 AppArmor,则无法隔离执行。
资源限制只能通过 Linux 的 rlimit 所提供的机制来约束。一些值得注意的缺陷:
FSIZE 可以限制进程创建的任何单个文件的大小,并且可以限制其同时打开的文件数量,但它不能限制写入的文件总数,因此也不能限制写入所有文件的总字节数。部分缓解措施是限制最大执行时间。(无论如何,沙箱中写入的所有文件都将在执行结束时被删除。)NPROC 限制约束了当前进程创建新线程和进程的能力,但使用计数(已存在的进程数)是所有具有相同 UID 的进程的总和,即使在同一主机上该 UID 可能映射到不同用户名的其他容器中也是如此。由于 rlimit 的应用方式,此约束也适用于应用用户。即使选择的 UID 未被主机上的其他软件使用,同一主机上的多个 codejail 沙箱进程也会共享此使用池,并可能降低彼此创建进程的能力。在这种情况下,NPROC 需要设置为比单个 codejail 实例一次处理单个请求时更高的值。沙箱之间没有很强的隔离。在正确配置下,不受信任的代码不应能够发现其他正在执行的代码,但如果此假设被违反,那么一个沙箱理论上可能会干扰另一个沙箱。
请不要公开报告安全问题。请发送邮件至 [email protected]。