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: 맞춤 포크 서버 – 바이너리를 준비하고, 포크 요청을 대기하고, 직접 샌드박스 처리
이 모드를 사용하면 바이너리를 시작하고 샌드박스를 준비하고 바이너리 수명 주기의 특정 시점에 실행자가 사용할 수 있도록 할 수 있습니다.
실행자가 바이너리에 포크 요청을 전송하고 ::sandbox2::ForkingClient::WaitAndFork()
를 통해 fork()
를 실행합니다. 새로 만든 프로세스는 ::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를 참고하세요.