There are multiple possibilities with Linux to sandbox applications.
- chroot … Changed file system root
- namespaces … Separated namespaces (e.g. Mount table)
- cgroups … Restricted resources (e.g. Memory)
Example (with changed system root)
#include <filesystem>
#include <iostream>
#include <vector>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/wait.h>
namespace sfs = std::filesystem;
void mountDeps(std::string& chrootPath, std::vector<std::string>& deps);
void unmountDeps(std::string& chrootPath, std::vector<std::string>& deps);
void startApp(std::string& chrootPath, std::vector<std::string>& deps);
void changeRoot(std::string& chrootPath);
int main()
{
std::vector<std::string> deps = { "child.out", "/lib", "/lib64" };
std::string chrootPath = "/tmp/chroot/";
sfs::create_directory(chrootPath);
mountDeps(chrootPath, deps);
startApp(chrootPath, deps);
unmountDeps(chrootPath, deps);
sfs::remove_all(chrootPath);
return 0;
}
void mountDeps(std::string& chrootPath, std::vector<std::string>& deps)
{
for (std::string& dep : deps)
{
std::string dest = chrootPath + dep;
if (sfs::is_directory(dep)) { sfs::create_directory(dest); }
else { close(open(dest.c_str(), O_WRONLY | O_CREAT, 0755)); }
if (mount(dep.c_str(), dest.c_str(), "", MS_BIND, ""))
{
std::cerr << "Bind mounting from " << dep << " to " << dest <<
" failed: " << strerror(errno) << std::endl;
}
}
}
void unmountDeps(std::string& chrootPath, std::vector<std::string>& deps)
{
for (std::string& dep : deps)
{
std::string dest = chrootPath + dep;
if (umount2(dest.c_str(), MNT_DETACH))
{
std::cerr << "Unmounting of " << dest <<
" failed: " << strerror(errno) << std::endl;
}
sfs::remove_all(dest);
}
}
void startApp(std::string& chrootPath, std::vector<std::string>& deps)
{
pid_t pid = fork();
switch (pid)
{
case -1:
/* Couldn't fork */
{
std::cerr << "Couldn't fork" << std::endl;
}
break;
case 0:
/* Child process */
{
changeRoot(chrootPath);
std::string execFile = "./" + deps[0];
if (execlp(execFile.c_str(), execFile.c_str(), NULL) == -1)
{
std::cerr << "Execution of " << deps[0] << " failed: "
<< strerror(errno) << std::endl;
}
// The child is gone now
}
break;
default:
/* Parent process */
{
std::cout << "Parent: I put my child in a sandbox" << std::endl;
waitpid(pid, NULL, 0);
}
break;
}
}
void changeRoot(std::string& chrootPath)
{
if (chroot(chrootPath.c_str()) == -1)
{
std::cerr << "Chroot to " << chrootPath << " failed: "
<< strerror(errno) << std::endl;
}
if (chdir("/") == -1)
{
std::cerr << "Chdir to root failed: "
<< strerror(errno) << std::endl;
}
}
#include <filesystem>
#include <fstream>
#include <iostream>
namespace sfs = std::filesystem;
int main()
{
std::cout << "Child: Let's check the root:";
for (auto& entry : sfs::directory_iterator("/"))
{
std::cout << " " << entry.path();
}
std::cout << std::endl;
std::cout << "Child: Oh shit, I have to play in a sandbox" << std::endl;
return 0;
}
$ g++ child.cpp -o child.out $ ldd child.out linux-vdso.so.1 (0x00007ffcc8375000) libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f50bb800000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f50bb400000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f50bb715000) /lib64/ld-linux-x86-64.so.2 (0x00007f50bbb1e000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f50bbae3000) $ g++ main.cpp -o main.out $ sudo ./main.out Parent: I put my child in a sandbox Child: Let's check the root: "/child.out" "/lib" "/lib64" Child: Oh shit, I have to play in a sandbox