1. 샌드박스 실행기 메서드 선택
샌드박싱은 Sandboxee 실행을 담당하는 실행기 (샌드박스 실행기 참고)로 시작합니다. executor.h 헤더 파일에는 이 목적에 필요한 API가 포함되어 있습니다. 이 API는 매우 유연하며 사용 사례에 가장 적합한 것을 선택할 수 있습니다. 다음 섹션에서는 선택할 수 있는 3가지 방법을 설명합니다.
방법 1: 독립형 - 샌드박스가 이미 사용 설정된 바이너리 실행
샌드박스를 사용하는 가장 간단한 방법이며 소스 코드가 없는 전체 바이너리를 샌드박스 처리하려는 경우 권장되는 방법입니다. 또한 샌드박싱을 사용하는 가장 안전한 방법이기도 합니다. 악영향을 미칠 수 있는 샌드박싱되지 않은 초기화가 없기 때문입니다.
다음 코드 스니펫에서는 샌드박스 처리할 바이너리의 경로와 execve syscall에 전달해야 하는 인수를 정의합니다. executor.h 헤더 파일에서 볼 수 있듯이 envp 값을 지정하지 않으므로 상위 프로세스에서 환경을 복사합니다. 첫 번째 인수는 항상 실행할 프로그램의 이름이며 스니펫은 다른 인수를 정의하지 않습니다.
이 실행기 메서드의 예는 static 및 tool입니다.
#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::EnterForkLook()을 통해)합니다. 새로 생성된 프로세스는 ::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를 참고하세요.