1. Escolher um método de executor de sandbox
O sandbox começa com um executor (consulte Sandbox Executor), que é responsável por executar o Sandboxee. O arquivo de cabeçalho executor.h contém a API necessária para essa finalidade. A API é muito flexível e permite que você escolha o que funciona melhor para seu caso de uso. As seções a seguir descrevem os três métodos diferentes que você pode escolher.
Método 1: independente – execute um binário com o sandboxing já ativado
Essa é a maneira mais simples de usar o sandbox e é o método recomendado quando você quer colocar em sandbox um binário inteiro sem código-fonte. Também é a maneira mais segura de usar o sandbox, já que não há inicialização sem sandbox que possa ter efeitos adversos.
No snippet de código a seguir, definimos o caminho do binário a ser isolado em sandbox
e os argumentos que precisamos transmitir para uma syscall execve. Como você pode ver no arquivo de cabeçalho executor.h, não especificamos um valor para envp
e, portanto, copiamos o ambiente do processo principal. O primeiro argumento é sempre o nome do programa a ser executado, e nosso snippet não define nenhum outro argumento.
Exemplos desse método de executor são: static e 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);
Método 2: Forkserver do Sandbox2 – informar ao executor quando usar o sandbox
Esse método oferece a flexibilidade de não estar em sandbox durante a inicialização
e, em seguida, escolher quando entrar no sandbox chamando
::sandbox2::Client::SandboxMeHere()
. É necessário definir no código quando você quer iniciar o isolamento em sandbox, e ele precisa ser de uma única linha de execução. Leia o motivo no FAQ.
No snippet de código a seguir, usamos o mesmo código descrito no Método 1 acima. No entanto, para permitir que o programa seja executado de maneira
não isolada durante a inicialização, chamamos 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);
Como o executor agora tem um sandbox desativado até ser notificado pelo
Sandboxee, precisamos criar uma instância ::sandbox2::Client
, configurar
a comunicação entre o executor e o Sandboxee e notificar o executor
que a inicialização foi concluída e queremos iniciar o sandbox agora
chamando 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();
…
Um exemplo desse método de executor é
crc4, em que crc4bin.cc
é o
Sandboxee e notifica o executor (crc4sandbox.cc
) quando ele precisa entrar na
sandbox.
Método 3: Forkserver personalizado – prepare um binário, aguarde solicitações de fork e crie um sandbox por conta própria
Esse modo permite iniciar um binário, prepará-lo para o sandbox e, em um momento específico do ciclo de vida do binário, disponibilizá-lo para o executor.
O executor vai enviar uma solicitação de fork para seu binário, que vai fork()
(via
::sandbox2::ForkingClient::WaitAndFork()
). O processo recém-criado estará
pronto para ser isolado em sandbox com ::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());
Esse modo é bastante complicado e aplicável apenas em alguns casos específicos, por exemplo, quando você tem requisitos de memória restritos. Você vai se beneficiar do COW, mas terá a desvantagem de não ter ASLR real. Outro exemplo de uso típico seria quando o Sandboxee tem uma inicialização longa e com uso intenso da CPU que pode ser executada antes do processamento dos dados não confiáveis.
Para ver um exemplo desse método de executor, consulte custom_fork.