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()를 호출하여 샌드박스에 진입할 시점을 선택할 수 있습니다. 샌드박스를 시작할 시점을 코드에서 정의할 수 있어야 하며 단일 스레드여야 합니다 (FAQ에서 이유 확인).

다음 코드 스니펫에서는 위의 방법 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는 샌드박스이며 샌드박스에 진입해야 할 때 실행자 (crc4sandbox.cc)에 알립니다.

방법 3: 맞춤 포크 서버 - 바이너리를 준비하고 포크 요청을 기다리고 자체적으로 샌드박스 처리

이 모드를 사용하면 바이너리를 시작하고, 샌드박싱을 위해 준비하고, 바이너리 수명 주기의 특정 시점에 실행기에 제공할 수 있습니다.

실행기는 바이너리에 포크 요청을 전송하며, 바이너리는 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이 없다는 단점이 있습니다. 또 다른 일반적인 사용 사례는 Sandboxee에 신뢰할 수 없는 데이터가 처리되기 전에 실행할 수 있는 길고 CPU 집약적인 초기화가 있는 경우입니다.

이 실행기 메서드의 예는 custom_fork를 참고하세요.