
CodeJail は、信頼されないコードをセキュアなサンドボックス内で実行するための管理を行います。主に Python の実行用に設計されていますが、他の言語でも使用できます。
セキュリティは AppArmor によって強化されます。お使いのオペレーティングシステムが AppArmor をサポートしていない場合、または AppArmor プロファイルが正しく定義・設定されていない場合、CodeJail は実行を保護しません。
CodeJail は設定可能なように設計されており、適切にインストールすれば Python 実行用に自動設定されます。
CodeJail のサンドボックスは、いくつかの要素で構成されています。
#) サンドボックス環境。Python のセットアップでは、これは Python と関連するコアパッケージを仮想環境 (virtualenv) としてまとめたものになります。このドキュメント全体では と表記します。これは読み取り専用で、サンドボックスのインスタンス化間で共有されます。
サンドボックス化されたコードは、AppArmor プロファイルが許可する範囲で OS ライブラリにもアクセスできます。
#) サンドボックス実行ディレクトリ。これは /tmp/codejail-XXXXXXXX のような名前の一時的な読み取り専用ディレクトリで、提出されたコード (./jailed_code)、オプションの追加ファイル、および提出されたコードがスクラッチ領域として使用できる書き込み可能な一時ディレクトリ (./tmp) が含まれます。
提出されたコードは通常、サーバー上でテストされるために学生が提出したコードであり、追加ファイルは通常、採点用またはユーティリティライブラリを含む python_lib.zip です。
実行には、CodeJail は 2 つのユーザーアカウントを必要とします。1 つはコードが実行されるメインアカウントで、サンドボックスを作成する権限を持ちます。これを <SANDBOX_CALLER> と呼びます。もう 1 つはサンドボックスが実行されるアカウントです。これは通常、sandbox というアカウントです。
このライブラリは現在、以下のバージョンで動作することがテストされています。
Python:
Ubuntu:
(サンドボックス内で使用される Python のバージョンは、ライブラリ自体に使用されるバージョンと異なる場合があることに注意してください。)
以下の手順では、CodeJail が Python コードを安全に実行できるようにオペレーティングシステムを設定する方法を詳しく説明します。ただし、codejail.safe_exec.ALWAYS_BE_UNSAFE = True を設定して、提出された Python をセキュリティ対策なしでマシン上で直接実行することも可能です。これはセキュリティを気にしない開発者のマシンでは問題ない場合があり、CodeJail の API との統合をテストできます。ただし、信頼されないソースからの入力がある場合は絶対に使用しないでください。本番システムではこのオプションを使用しないでください。
Python の実行をセキュアにするには、新しい virtualenv を作成します。つまり、プロジェクト用のメインの virtualenv と、サンドボックス化された Python コード用の新しい virtualenv の 2 つを持つことになります。
新しい virtualenv の場所を選び、 と呼びます。既存の virtualenv のすぐ隣に置き、末尾に -sandbox を追加すると、自動的に検出されて使用されます。したがって、既存の virtualenv が /home/chris/ve/myproj にある場合、 は /home/chris/ve/myproj-sandbox にします。
LMS を実行するユーザーは <SANDBOX_CALLER> です。たとえば、開発マシン上の自分自身や、サーバー上の www-data などです。
設定に依存するその他の詳細は次のとおりです。
独立した Python 実行ファイルができるように --copies を使用して新しい virtualenv を作成します::
$ sudo python3.12 -m venv --copies
デフォルトでは、virtualenv はシステムの Python へのシンボリックリンクを作成するだけであり、一部のオペレーティングシステムの apparmor のデフォルト設定では、制限 (confinement) が適用されない場合があります。
(任意) サンドボックス化されたコードで利用できるようにしたい特定のパッケージがある場合は、サンドボックスの仮想環境を有効にして、pip でインストールします::
$ /bin/pip install -r requirements/sandbox.txt
サンドボックスユーザーを追加します::
$ sudo addgroup sandbox $ sudo adduser --disabled-login sandbox --ingroup sandbox
Web サーバーがサンドボックス化された Python を sandbox ユーザーとして実行できるようにします。ファイル /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>
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 は、さまざまなプロジェクトで信頼されないコードを実行するために使用できるほど汎用性が高い設計です。2 つのレイヤーを提供します。
jail_code.py はサブプロセスのセキュアな実行を提供します。これは、AppArmor によって管理されるサブプロセスでプログラムを実行することによって実現されます。
safe_exec.py は Python の実行の特別な処理を提供し、jail_code を使用して Python の exec ステートメントのセマンティクスを提供します。
CodeJail は AppArmor の下でプログラムを実行します。AppArmor は、プログラムがアクセスできるリソースを制限する OS 提供の機能です。リソースへのアクセスを制限して Python コードを実行するために、新しい virtualenv を作成し、その Python 実行ファイルを AppArmor プロファイルで指定し、そのプロファイルでリソースを制限します。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 は、プロセスが作成できる 1 つのファイルのサイズを制限でき、同時に開いているファイル数を制限できますが、書き込まれるファイルの総数は制限できないため、すべての ファイルにわたって書き込まれる合計バイト数を制限することはできません。部分的な緩和策として、最大実行時間を制限することが挙げられます。(サンドボックス内に書き込まれたすべてのファイルは、実行終了時に削除されます。)NPROC 制限は、現在の プロセスが新しいスレッドとプロセスを作成する能力を制限しますが、使用量カウント (既に存在するプロセス数) は、同じ UID を持つすべての プロセスの合計です。同じホスト上の他のコンテナで、その UID が異なるユーザー名にマッピングされている場合も含みます。この制約は、rlimit の適用方法により、アプリユーザーにも適用されます。ホスト上の他のソフトウェアが使用しないように UID を選択したとしても、同じホスト上の複数の codejail サンドボックスプロセスがこの使用プールを共有し、互いのプロセス作成能力を低下させる可能性があります。この状況では、NPROC は、一度に 1 つのリクエストを処理する単一の codejail インスタンスの場合よりも高く設定する必要があります。サンドボックスは互いに強力な分離を持ちません。適切な設定の下では、信頼されないコードが他の実行中のコード実行を発見することはできないはずですが、この前提が破られた場合、1 つのサンドボックスが理論的に別のサンドボックスに干渉する可能性があります。
セキュリティ問題を公開の場で報告しないでください。[email protected] にメールで連絡してください。
プロジェクトのメインの virtualenv を再度有効にします。
rlimit を設定するための PAM の使用を無効にします::
sed -i '/pam_limits.so/d' /etc/pam.d/sudo