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