1. サンドボックス実行メソッドを選択する

サンドボックス化は、Sandboxee の実行を担当するエグゼキュータ(サンドボックス エグゼキュータを参照)から始まります。executor.h ヘッダー ファイルには、この目的で必要な API が含まれています。この API は非常に柔軟性が高く、ユースケースに最適なものを選択できます。以降のセクションでは、選択できる 3 つの方法について説明します。

方法 1: スタンドアロン - サンドボックスがすでに有効になっているバイナリを実行する

これはサンドボックス化を行う最も簡単な方法であり、ソースコードのないバイナリ全体をサンドボックス化する場合におすすめの方法です。また、悪影響を及ぼす可能性のあるサンドボックス化されていない初期化がないため、サンドボックス化を使用する最も安全な方法でもあります。

次のコード スニペットでは、サンドボックス化するバイナリのパスと、execve syscall に渡す引数を定義しています。executor.h ヘッダー ファイルで確認できるように、envp の値を指定していないため、親プロセスから環境がコピーされます。最初の引数は常に実行されるプログラムの名前です。このスニペットでは、他の引数は定義されていません。

この実行メソッドの例としては、statictool があります。

#include "sandboxed_api/sandbox2/executor.h"

std::string path = "path/to/binary";
std::vector<std::string> args = {path};  // args[0] will become the sandboxed
                                         // process' argv[0], typically the
                                         // path to the binary.
auto executor = absl::make_unique<sandbox2::Executor>(path, args);

方法 2: Sandbox2 Forkserver - サンドボックス化するタイミングをエグゼキュータに伝える

このメソッドでは、初期化中にサンドボックス化を解除し、::sandbox2::Client::SandboxMeHere() を呼び出してサンドボックス化を開始するタイミングを選択できます。サンドボックス化を開始するタイミングをコードで定義できる必要があり、シングル スレッドである必要があります(理由はよくある質問をご覧ください)。

次のコード スニペットでは、上記の方法 1 で説明したコードと同じコードを使用しています。ただし、初期化中にプログラムをサンドボックス化されていない状態で実行できるようにするため、set_enable_sandbox_before_exec(false) を呼び出します。

#include "sandboxed_api/sandbox2/executor.h"

std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto executor = absl::make_unique<sandbox2::Executor>(path, args);
executor->set_enable_sandbox_before_exec(false);

Sandboxee から通知されるまで、実行プログラムは無効なサンドボックスを保持しているため、::sandbox2::Client インスタンスを作成し、実行プログラムと Sandboxee 間の通信を設定してから、sandbox2_client.SandboxMeHere() を呼び出して、初期化が完了し、サンドボックス化を開始することを実行プログラムに通知する必要があります。

// main() of sandboxee
int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, false);

  // Set-up the sandbox2::Client object, using a file descriptor (1023).
  sandbox2::Comms comms(sandbox2::Comms::kSandbox2ClientCommsFD);
  sandbox2::Client sandbox2_client(&comms);
  // Enable sandboxing from here.
  sandbox2_client.SandboxMeHere();
  

このエグゼキュータ メソッドの例は crc4 です。ここで、crc4bin.cc は Sandboxee であり、サンドボックスに入る必要があるときにエグゼキュータ(crc4sandbox.cc)に通知します。

方法 3: カスタム Forkserver - バイナリを準備し、フォーク リクエストを待機して、独自にサンドボックス化する

このモードでは、バイナリを起動してサンドボックス化の準備を行い、バイナリのライフサイクルの特定の時点で実行プログラムで使用できるようにします。

実行プログラムはバイナリにフォーク リクエストを送信し、バイナリは fork()::sandbox2::ForkingClient::WaitAndFork() 経由)します。新しく作成されたプロセスは、::sandbox2::Client::SandboxMeHere() でサンドボックス化する準備が整います。

#include "sandboxed_api/sandbox2/executor.h"

// Start the custom ForkServer
std::string path = "path/to/binary";
std::vector<std::string> args = {path};
auto fork_executor = absl::make_unique<sandbox2::Executor>(path, args);
fork_executor->StartForkServer();

// Initialize Executor with Comms channel to the ForkServer
auto executor = absl::make_unique<sandbox2::Executor>(
    fork_executor->ipc()->GetComms());

このモードは非常に複雑で、メモリ要件が厳しい場合など、特定のケースにのみ適用されます。COW のメリットはありますが、ASLR が実際には機能しないというデメリットがあります。もう 1 つの一般的な使用例は、Sandboxee に、信頼できないデータが処理される前に実行できる、CPU 使用率の高い長い初期化がある場合です。

このエグゼキュータ メソッドの例については、custom_fork をご覧ください。