mirror of
https://github.com/google/nsjail.git
synced 2026-08-30 18:41:30 -07:00
nsjail.h/all - reduce size of nsjail_t and stop duplicating nsjail::NsJailConfig fields. Now nsjail::NsJailConfig is the source of truth for jails' configuraiton
This commit is contained in:
13
caps.cc
13
caps.cc
@@ -235,7 +235,12 @@ bool initNs(nsj_t* nsj) {
|
||||
/* Set all requested caps in the inheritable set if these are present in the permitted set
|
||||
*/
|
||||
std::string dbgmsg;
|
||||
for (const auto& cap : nsj->caps) {
|
||||
for (ssize_t i = 0; i < nsj->njc.cap_size(); i++) {
|
||||
int cap = nameToVal(nsj->njc.cap(i).c_str());
|
||||
if (cap == -1) {
|
||||
LOG_W("Unknown capability: %s", nsj->njc.cap(i).c_str());
|
||||
continue;
|
||||
}
|
||||
if (!getPermitted(cap_data, cap)) {
|
||||
LOG_W("Capability %s is not permitted in the namespace",
|
||||
capToStr(cap).c_str());
|
||||
@@ -277,7 +282,11 @@ bool initNs(nsj_t* nsj) {
|
||||
|
||||
/* Make sure inheritable set is preserved across execve via the modified ambient set */
|
||||
dbgmsg.clear();
|
||||
for (const auto& cap : nsj->caps) {
|
||||
for (ssize_t i = 0; i < nsj->njc.cap_size(); i++) {
|
||||
int cap = nameToVal(nsj->njc.cap(i).c_str());
|
||||
if (cap == -1) {
|
||||
continue;
|
||||
}
|
||||
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)cap, 0UL, 0UL) ==
|
||||
-1) {
|
||||
PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)",
|
||||
|
||||
170
cmdline.cc
170
cmdline.cc
@@ -252,7 +252,7 @@ static void cmdlineUsage(const char* pname) {
|
||||
|
||||
void addEnv(nsj_t* nsj, const std::string& env) {
|
||||
if (env.find('=') != std::string::npos) {
|
||||
nsj->envs.push_back(env);
|
||||
nsj->njc.add_envar(env);
|
||||
return;
|
||||
}
|
||||
char* e = getenv(env.c_str());
|
||||
@@ -260,7 +260,7 @@ void addEnv(nsj_t* nsj, const std::string& env) {
|
||||
LOG_W("Requested to use the %s envar, but it's not set. It'll be ignored", QC(env));
|
||||
return;
|
||||
}
|
||||
nsj->envs.push_back(std::string(env).append("=").append(e));
|
||||
nsj->njc.add_envar(std::string(env).append("=").append(e));
|
||||
}
|
||||
|
||||
void logParams(nsj_t* nsj) {
|
||||
@@ -284,28 +284,24 @@ void logParams(nsj_t* nsj) {
|
||||
|
||||
LOG_I("Jail parameters: hostname:'%s', chroot:%s, process:'%s', "
|
||||
"bind:[%s]:%d, "
|
||||
"max_conns:%u, max_conns_per_ip:%u, time_limit:%u"
|
||||
", personality:%#lx, daemonize:%s, clone_newnet:%s, "
|
||||
"max_conns:%u, max_conns_per_ip:%u, time_limit:%u, daemonize:%s, clone_newnet:%s, "
|
||||
"clone_newuser:%s, clone_newns:%s, clone_newpid:%s, clone_newipc:%s, "
|
||||
"clone_newuts:%s, "
|
||||
"clone_newcgroup:%s, clone_newtime:%s, keep_caps:%s, "
|
||||
"disable_no_new_privs:%s, "
|
||||
"max_cpus:%u",
|
||||
"clone_newuts:%s, cgroupv2:%s, keep_caps:%s, "
|
||||
"disable_no_new_privs:%s, max_cpus:%u",
|
||||
nsj->njc.hostname().c_str(), QC(nsj->chroot),
|
||||
nsj->njc.exec_bin().path().empty() ? nsj->argv[0].c_str()
|
||||
: nsj->njc.exec_bin().path().c_str(),
|
||||
nsj->njc.bindhost().c_str(), nsj->njc.port(), nsj->njc.max_conns(),
|
||||
nsj->njc.max_conns_per_ip(), nsj->njc.time_limit(), nsj->personality,
|
||||
logYesNo(nsj->njc.daemon()), logYesNo(nsj->njc.clone_newnet()),
|
||||
logYesNo(nsj->njc.clone_newuser()), logYesNo(nsj->njc.clone_newns()),
|
||||
logYesNo(nsj->njc.clone_newpid()), logYesNo(nsj->njc.clone_newipc()),
|
||||
logYesNo(nsj->njc.clone_newuts()), logYesNo(nsj->njc.clone_newcgroup()),
|
||||
logYesNo(nsj->njc.clone_newtime()), logYesNo(nsj->njc.keep_caps()),
|
||||
nsj->njc.max_conns_per_ip(), nsj->njc.time_limit(), logYesNo(nsj->njc.daemon()),
|
||||
logYesNo(nsj->njc.clone_newnet()), logYesNo(nsj->njc.clone_newuser()),
|
||||
logYesNo(nsj->njc.clone_newns()), logYesNo(nsj->njc.clone_newpid()),
|
||||
logYesNo(nsj->njc.clone_newipc()), logYesNo(nsj->njc.clone_newuts()),
|
||||
logYesNo(nsj->njc.use_cgroupv2()), logYesNo(nsj->njc.keep_caps()),
|
||||
logYesNo(nsj->njc.disable_no_new_privs()), nsj->njc.max_cpus());
|
||||
|
||||
for (const auto& p : nsj->mountpts) {
|
||||
LOG_I(
|
||||
"%s: %s", p.is_symlink ? "Symlink" : "Mount", mnt::describeMountPt(p).c_str());
|
||||
for (const auto& p : nsj->njc.mount()) {
|
||||
LOG_I("%s: %s", p.is_symlink() ? "Symlink" : "Mount",
|
||||
mnt::describeMountPt(p).c_str());
|
||||
}
|
||||
for (const auto& uid : nsj->uids) {
|
||||
LOG_I("Uid map: inside_uid:%lu outside_uid:%lu count:%zu newuidmap:%s",
|
||||
@@ -344,9 +340,8 @@ uint64_t parseRLimit(int res, const char* optarg, unsigned long mul) {
|
||||
return cur.rlim_max;
|
||||
}
|
||||
if (!util::isANumber(optarg)) {
|
||||
LOG_F(
|
||||
"RLIMIT %s (%d) needs a numeric value or 'max'/'hard'/'def'/'soft'/'inf' value "
|
||||
"(%s provided)",
|
||||
LOG_F("RLIMIT %s (%d) needs a numeric value or 'max'/'hard'/'def'/'soft'/'inf' "
|
||||
"value (%s provided)",
|
||||
util::rLimName(res).c_str(), res, QC(optarg));
|
||||
}
|
||||
errno = 0;
|
||||
@@ -403,34 +398,33 @@ static bool setupArgv(nsj_t* nsj, int argc, char** argv, int optind) {
|
||||
|
||||
static bool setupMounts(nsj_t* nsj) {
|
||||
if (!(nsj->chroot.empty())) {
|
||||
if (!mnt::addMountPtHead(nsj, nsj->chroot, "/", /* fstype= */ "",
|
||||
/* options= */ "",
|
||||
nsj->is_root_rw ? (MS_BIND | MS_REC | MS_PRIVATE)
|
||||
: (MS_BIND | MS_REC | MS_PRIVATE | MS_RDONLY),
|
||||
/* is_dir= */ mnt::NS_DIR_YES,
|
||||
/* is_mandatory= */ true, /* src_env= */ "",
|
||||
/* dst_env= */ "", /* src_content= */ "",
|
||||
/* is_symlink= */ false)) {
|
||||
return false;
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_src(nsj->chroot);
|
||||
p->set_dst("/");
|
||||
p->set_is_bind(true);
|
||||
p->set_rw(nsj->is_root_rw);
|
||||
p->set_is_dir(true);
|
||||
/* Insert at the beginning */
|
||||
for (int i = nsj->njc.mount_size() - 1; i > 0; i--) {
|
||||
nsj->njc.mutable_mount()->SwapElements(i, i - 1);
|
||||
}
|
||||
} else {
|
||||
if (!mnt::addMountPtHead(nsj, /* src= */ "", "/", "tmpfs",
|
||||
/* options= */ "", nsj->is_root_rw ? 0 : MS_RDONLY,
|
||||
/* is_dir= */ mnt::NS_DIR_YES,
|
||||
/* is_mandatory= */ true, /* src_env= */ "", /* dst_env= */ "",
|
||||
/* src_content= */ "", /* is_symlink= */ false)) {
|
||||
return false;
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_dst("/");
|
||||
p->set_fstype("tmpfs");
|
||||
p->set_rw(nsj->is_root_rw);
|
||||
p->set_is_dir(true);
|
||||
/* Insert at the beginning */
|
||||
for (int i = nsj->njc.mount_size() - 1; i > 0; i--) {
|
||||
nsj->njc.mutable_mount()->SwapElements(i, i - 1);
|
||||
}
|
||||
}
|
||||
if (!nsj->proc_path.empty()) {
|
||||
if (!mnt::addMountPtTail(nsj, /* src= */ "", nsj->proc_path, "proc",
|
||||
/* options= */ "", nsj->njc.mount_proc() ? 0 : MS_RDONLY,
|
||||
/* is_dir= */ mnt::NS_DIR_YES,
|
||||
/* is_mandatory= */ true, /* src_env= */ "",
|
||||
/* dst_env= */ "", /* src_content= */ "",
|
||||
/* is_symlink= */ false)) {
|
||||
return false;
|
||||
}
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_dst(nsj->proc_path);
|
||||
p->set_fstype("proc");
|
||||
p->set_rw(nsj->njc.mount_proc());
|
||||
p->set_is_dir(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -606,19 +600,19 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
|
||||
nsj->njc.set_disable_rl(true);
|
||||
break;
|
||||
case 0x0301:
|
||||
nsj->personality |= ADDR_COMPAT_LAYOUT;
|
||||
nsj->njc.set_persona_addr_compat_layout(true);
|
||||
break;
|
||||
case 0x0302:
|
||||
nsj->personality |= MMAP_PAGE_ZERO;
|
||||
nsj->njc.set_persona_mmap_page_zero(true);
|
||||
break;
|
||||
case 0x0303:
|
||||
nsj->personality |= READ_IMPLIES_EXEC;
|
||||
nsj->njc.set_persona_read_implies_exec(true);
|
||||
break;
|
||||
case 0x0304:
|
||||
nsj->personality |= ADDR_LIMIT_3GB;
|
||||
nsj->njc.set_persona_addr_limit_3gb(true);
|
||||
break;
|
||||
case 0x0305:
|
||||
nsj->personality |= ADDR_NO_RANDOMIZE;
|
||||
nsj->njc.set_persona_addr_no_randomize(true);
|
||||
break;
|
||||
case 'N':
|
||||
nsj->njc.set_clone_newnet(false);
|
||||
@@ -670,7 +664,8 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
|
||||
if (cap == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
nsj->caps.push_back(cap);
|
||||
|
||||
nsj->njc.add_cap(optarg);
|
||||
} break;
|
||||
case 0x0600:
|
||||
nsj->njc.set_no_pivotroot(true);
|
||||
@@ -685,7 +680,7 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
|
||||
case 0x0605:
|
||||
nsj->njc.set_mount_proc(true);
|
||||
nsj->proc_path = optarg;
|
||||
nsj->proc_path = optarg;
|
||||
|
||||
break;
|
||||
case 0x0606:
|
||||
nsj->is_proc_rw = true;
|
||||
@@ -754,14 +749,12 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
|
||||
if (dst.empty()) {
|
||||
dst = src;
|
||||
}
|
||||
if (!mnt::addMountPtTail(nsj.get(), src, dst, /* fstype= */ "",
|
||||
/* options= */ "", MS_BIND | MS_REC | MS_PRIVATE | MS_RDONLY,
|
||||
/* is_dir= */ mnt::NS_DIR_MAYBE, /* is_mandatory= */ true,
|
||||
/* src_env= */ "", /* dst_env= */ "", /* src_content= */ "",
|
||||
/* is_symlink= */ false)) {
|
||||
return nullptr;
|
||||
}
|
||||
}; break;
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_src(src);
|
||||
p->set_dst(dst);
|
||||
p->set_rw(false);
|
||||
p->set_is_bind(true);
|
||||
} break;
|
||||
case 'B': {
|
||||
std::vector<std::string> subopts = util::strSplit(optarg, ':');
|
||||
std::string src = argFromVec(subopts, 0);
|
||||
@@ -769,23 +762,22 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
|
||||
if (dst.empty()) {
|
||||
dst = src;
|
||||
}
|
||||
if (!mnt::addMountPtTail(nsj.get(), src, dst, /* fstype= */ "",
|
||||
/* options= */ "", MS_BIND | MS_REC | MS_PRIVATE,
|
||||
/* is_dir= */ mnt::NS_DIR_MAYBE, /* is_mandatory= */ true,
|
||||
/* src_env= */ "", /* dst_env= */ "", /* src_content= */ "",
|
||||
/* is_symlink= */ false)) {
|
||||
return nullptr;
|
||||
}
|
||||
}; break;
|
||||
std::string options = argFromVec(subopts, 2);
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_src(src);
|
||||
p->set_dst(dst);
|
||||
p->set_options(options);
|
||||
p->set_rw(true);
|
||||
p->set_is_bind(true);
|
||||
} break;
|
||||
case 'T': {
|
||||
if (!mnt::addMountPtTail(nsj.get(), "", optarg, /* fstype= */ "tmpfs",
|
||||
/* options= */ "size=4194304", 0,
|
||||
/* is_dir= */ mnt::NS_DIR_YES, /* is_mandatory= */ true,
|
||||
/* src_env= */ "", /* dst_env= */ "", /* src_content= */ "",
|
||||
/* is_symlink= */ false)) {
|
||||
return nullptr;
|
||||
}
|
||||
}; break;
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_dst(optarg);
|
||||
p->set_fstype("tmpfs");
|
||||
p->set_options("size=4194304");
|
||||
p->set_rw(true);
|
||||
p->set_is_dir(true);
|
||||
} break;
|
||||
case 'm': {
|
||||
std::vector<std::string> subopts = util::strSplit(optarg, ':');
|
||||
std::string src = argFromVec(subopts, 0);
|
||||
@@ -800,26 +792,26 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
|
||||
optionsStream << ":" << subopts[i];
|
||||
}
|
||||
std::string options = optionsStream.str();
|
||||
if (!mnt::addMountPtTail(nsj.get(), src, dst, /* fstype= */ fs_type,
|
||||
/* options= */ options, /* flags= */ 0,
|
||||
/* is_dir= */ mnt::NS_DIR_MAYBE, /* is_mandatory= */ true,
|
||||
/* src_env= */ "", /* dst_env= */ "", /* src_content= */ "",
|
||||
/* is_symlink= */ false)) {
|
||||
return nullptr;
|
||||
}
|
||||
}; break;
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_src(src);
|
||||
p->set_dst(dst);
|
||||
p->set_fstype(fs_type);
|
||||
p->set_options(options);
|
||||
p->set_rw(true);
|
||||
} break;
|
||||
case 's': {
|
||||
std::vector<std::string> subopts = util::strSplit(optarg, ':');
|
||||
std::string src = argFromVec(subopts, 0);
|
||||
std::string dst = argFromVec(subopts, 1);
|
||||
if (!mnt::addMountPtTail(nsj.get(), src, dst, /* fstype= */ "",
|
||||
/* options= */ "", /* flags= */ 0,
|
||||
/* is_dir= */ mnt::NS_DIR_NO, /* is_mandatory= */ true,
|
||||
/* src_env= */ "", /* dst_env= */ "", /* src_content= */ "",
|
||||
/* is_symlink= */ true)) {
|
||||
return nullptr;
|
||||
if (dst.empty()) {
|
||||
dst = src;
|
||||
}
|
||||
}; break;
|
||||
nsjail::MountPt* p = nsj->njc.add_mount();
|
||||
p->set_src(src);
|
||||
p->set_dst(dst);
|
||||
p->set_is_symlink(true);
|
||||
p->set_rw(true);
|
||||
} break;
|
||||
case 'M':
|
||||
switch (optarg[0]) {
|
||||
case 'l':
|
||||
|
||||
61
config.cc
61
config.cc
@@ -27,7 +27,6 @@
|
||||
#include <google/protobuf/util/json_util.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/personality.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
@@ -102,35 +101,9 @@ static bool parseInternal(nsj_t* nsj, const nsjail::NsJailConfig& njc) {
|
||||
for (ssize_t i = 0; i < njc.envar_size(); i++) {
|
||||
cmdline::addEnv(nsj, njc.envar(i));
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < njc.cap_size(); i++) {
|
||||
int cap = caps::nameToVal(njc.cap(i).c_str());
|
||||
if (cap == -1) {
|
||||
return false;
|
||||
}
|
||||
nsj->caps.push_back(cap);
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < njc.pass_fd_size(); i++) {
|
||||
nsj->openfds.push_back(njc.pass_fd(i));
|
||||
}
|
||||
|
||||
if (njc.persona_addr_compat_layout()) {
|
||||
nsj->personality |= ADDR_COMPAT_LAYOUT;
|
||||
}
|
||||
if (njc.persona_mmap_page_zero()) {
|
||||
nsj->personality |= MMAP_PAGE_ZERO;
|
||||
}
|
||||
if (njc.persona_read_implies_exec()) {
|
||||
nsj->personality |= READ_IMPLIES_EXEC;
|
||||
}
|
||||
if (njc.persona_addr_limit_3gb()) {
|
||||
nsj->personality |= ADDR_LIMIT_3GB;
|
||||
}
|
||||
if (njc.persona_addr_no_randomize()) {
|
||||
nsj->personality |= ADDR_NO_RANDOMIZE;
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < njc.uidmap_size(); i++) {
|
||||
if (!user::parseId(nsj, njc.uidmap(i).inside_id(), njc.uidmap(i).outside_id(),
|
||||
njc.uidmap(i).count(), false /* is_gid */, njc.uidmap(i).use_newidmap())) {
|
||||
@@ -147,43 +120,9 @@ static bool parseInternal(nsj_t* nsj, const nsjail::NsJailConfig& njc) {
|
||||
if (!njc.mount_proc()) {
|
||||
nsj->proc_path.clear();
|
||||
}
|
||||
for (ssize_t i = 0; i < njc.mount_size(); i++) {
|
||||
std::string src = njc.mount(i).src();
|
||||
std::string src_env = njc.mount(i).prefix_src_env();
|
||||
std::string dst = njc.mount(i).dst();
|
||||
std::string dst_env = njc.mount(i).prefix_dst_env();
|
||||
std::string fstype = njc.mount(i).fstype();
|
||||
std::string options = njc.mount(i).options();
|
||||
|
||||
uintptr_t flags = (!njc.mount(i).rw()) ? MS_RDONLY : 0;
|
||||
flags |= njc.mount(i).is_bind() ? (MS_BIND | MS_REC | MS_PRIVATE) : 0;
|
||||
flags |= njc.mount(i).nosuid() ? MS_NOSUID : 0;
|
||||
flags |= njc.mount(i).nodev() ? MS_NODEV : 0;
|
||||
flags |= njc.mount(i).noexec() ? MS_NOEXEC : 0;
|
||||
bool is_mandatory = njc.mount(i).mandatory();
|
||||
bool is_symlink = njc.mount(i).is_symlink();
|
||||
std::string src_content = njc.mount(i).src_content();
|
||||
|
||||
mnt::isDir_t is_dir = mnt::NS_DIR_MAYBE;
|
||||
if (njc.mount(i).has_is_dir()) {
|
||||
is_dir = njc.mount(i).is_dir() ? mnt::NS_DIR_YES : mnt::NS_DIR_NO;
|
||||
}
|
||||
|
||||
if (!mnt::addMountPtTail(nsj, src, dst, fstype, options, flags, is_dir,
|
||||
is_mandatory, src_env, dst_env, src_content, is_symlink)) {
|
||||
LOG_E("Couldn't add mountpoint for src:%s dst:%s", QC(src), QC(dst));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (njc.has_seccomp_policy_file()) {
|
||||
nsj->njc.set_seccomp_policy_file(njc.seccomp_policy_file());
|
||||
}
|
||||
/* seccomp_string is handled via nsj->njc.CopyFrom(njc) above */
|
||||
|
||||
for (ssize_t i = 0; i < njc.iface_own().size(); i++) {
|
||||
nsj->ifaces.push_back(njc.iface_own(i));
|
||||
}
|
||||
|
||||
if (njc.has_exec_bin()) {
|
||||
if (njc.exec_bin().has_path()) {
|
||||
|
||||
@@ -62,7 +62,7 @@ clone_newuts: true
|
||||
clone_newcgroup: true
|
||||
|
||||
user_net {
|
||||
enable: true
|
||||
enable: true
|
||||
tcp_ports: "auto"
|
||||
udp_ports: "auto"
|
||||
}
|
||||
|
||||
20
contain.cc
20
contain.cc
@@ -98,8 +98,24 @@ static bool containPrepareEnv(nsj_t* nsj) {
|
||||
PLOG_E("prctl(PR_SET_PDEATHSIG, SIGKILL)");
|
||||
return false;
|
||||
}
|
||||
if (nsj->personality && personality(nsj->personality) == -1) {
|
||||
PLOG_E("personality(%lx)", nsj->personality);
|
||||
unsigned long personality = 0;
|
||||
if (nsj->njc.persona_addr_compat_layout()) {
|
||||
personality |= ADDR_COMPAT_LAYOUT;
|
||||
}
|
||||
if (nsj->njc.persona_mmap_page_zero()) {
|
||||
personality |= MMAP_PAGE_ZERO;
|
||||
}
|
||||
if (nsj->njc.persona_read_implies_exec()) {
|
||||
personality |= READ_IMPLIES_EXEC;
|
||||
}
|
||||
if (nsj->njc.persona_addr_limit_3gb()) {
|
||||
personality |= ADDR_LIMIT_3GB;
|
||||
}
|
||||
if (nsj->njc.persona_addr_no_randomize()) {
|
||||
personality |= ADDR_NO_RANDOMIZE;
|
||||
}
|
||||
if (personality && ::personality(personality) == -1) {
|
||||
PLOG_E("personality(%lx)", personality);
|
||||
return false;
|
||||
}
|
||||
LOG_D("setpriority(%d)", nsj->njc.nice_level());
|
||||
|
||||
327
mnt.cc
327
mnt.cc
@@ -49,9 +49,35 @@
|
||||
|
||||
namespace mnt {
|
||||
|
||||
struct mount_t {
|
||||
std::string src;
|
||||
std::string src_content;
|
||||
std::string dst;
|
||||
std::string fs_type;
|
||||
std::string options;
|
||||
uintptr_t flags;
|
||||
bool is_dir;
|
||||
bool is_symlink;
|
||||
bool is_mandatory;
|
||||
bool mounted;
|
||||
};
|
||||
|
||||
#if !defined(MS_NOSYMFOLLOW)
|
||||
#define MS_NOSYMFOLLOW 256
|
||||
#endif /* if !defined(MS_NOSYMFOLLOW) */
|
||||
#if !defined(MS_LAZYTIME)
|
||||
#define MS_LAZYTIME (1 << 25)
|
||||
#endif /* if !defined(MS_LAZYTIME) */
|
||||
#if !defined(MS_ACTIVE)
|
||||
#define MS_ACTIVE (1 << 30)
|
||||
#endif /* if !defined(MS_ACTIVE) */
|
||||
#if !defined(MS_NOUSER)
|
||||
#define MS_NOUSER (1 << 31)
|
||||
#endif /* if !defined(MS_NOUSER) */
|
||||
|
||||
#if !defined(ST_NOSYMFOLLOW)
|
||||
#define ST_NOSYMFOLLOW 8192
|
||||
#endif /* if !defined(ST_NOSYMFOLLOW) */
|
||||
|
||||
static const std::string flagsToStr(unsigned long flags) {
|
||||
std::string res;
|
||||
@@ -68,9 +94,7 @@ static const std::string flagsToStr(unsigned long flags) {
|
||||
NS_VALSTR_STRUCT(MS_REMOUNT),
|
||||
NS_VALSTR_STRUCT(MS_MANDLOCK),
|
||||
NS_VALSTR_STRUCT(MS_DIRSYNC),
|
||||
#if defined(MS_NOSYMFOLLOW)
|
||||
NS_VALSTR_STRUCT(MS_NOSYMFOLLOW),
|
||||
#endif /* defined(MS_NOSYMFOLLOW) */
|
||||
NS_VALSTR_STRUCT(MS_NOATIME),
|
||||
NS_VALSTR_STRUCT(MS_NODIRATIME),
|
||||
NS_VALSTR_STRUCT(MS_BIND),
|
||||
@@ -87,12 +111,8 @@ static const std::string flagsToStr(unsigned long flags) {
|
||||
NS_VALSTR_STRUCT(MS_I_VERSION),
|
||||
NS_VALSTR_STRUCT(MS_STRICTATIME),
|
||||
NS_VALSTR_STRUCT(MS_LAZYTIME),
|
||||
#if defined(MS_ACTIVE)
|
||||
NS_VALSTR_STRUCT(MS_ACTIVE),
|
||||
#endif /* defined(MS_ACTIVE) */
|
||||
#if defined(MS_NOUSER)
|
||||
NS_VALSTR_STRUCT((uint32_t)MS_NOUSER), // defined as (1<<31)
|
||||
#endif /* defined(MS_NOUSER) */
|
||||
};
|
||||
|
||||
unsigned knownFlagMask = 0U;
|
||||
@@ -113,6 +133,66 @@ static const std::string flagsToStr(unsigned long flags) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const std::string describeMountPt(const nsjail::MountPt& mpt) {
|
||||
std::string descr;
|
||||
|
||||
descr.append(mpt.src().empty() ? "" : QC(mpt.src()))
|
||||
.append(mpt.src().empty() ? "" : " -> ")
|
||||
.append(QC(mpt.dst()))
|
||||
.append(" type:")
|
||||
.append(QC(mpt.fstype()))
|
||||
.append(" options:")
|
||||
.append(QC(mpt.options()));
|
||||
|
||||
if (mpt.has_is_dir()) {
|
||||
descr.append(mpt.is_dir() ? " dir:true" : " dir:false");
|
||||
}
|
||||
if (!mpt.mandatory()) {
|
||||
descr.append(" mandatory:false");
|
||||
}
|
||||
if (!mpt.src_content().empty()) {
|
||||
descr.append(" src_content_len:")
|
||||
.append(std::to_string(mpt.src_content().length()));
|
||||
}
|
||||
if (mpt.is_symlink()) {
|
||||
descr.append(" symlink:true");
|
||||
}
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
/* Helper for internal use with mount_t */
|
||||
static const std::string describeMountPt(const mount_t& mpt) {
|
||||
std::string descr;
|
||||
|
||||
descr.append(mpt.src.empty() ? "" : QC(mpt.src))
|
||||
.append(mpt.src.empty() ? "" : " -> ")
|
||||
.append(QC(mpt.dst))
|
||||
.append(" flags:")
|
||||
.append(flagsToStr(mpt.flags))
|
||||
.append(" type:")
|
||||
.append(QC(mpt.fs_type))
|
||||
.append(" options:")
|
||||
.append(QC(mpt.options));
|
||||
|
||||
if (mpt.is_dir) {
|
||||
descr.append(" dir:true");
|
||||
} else {
|
||||
descr.append(" dir:false");
|
||||
}
|
||||
if (!mpt.is_mandatory) {
|
||||
descr.append(" mandatory:false");
|
||||
}
|
||||
if (!mpt.src_content.empty()) {
|
||||
descr.append(" src_content_len:").append(std::to_string(mpt.src_content.length()));
|
||||
}
|
||||
if (mpt.is_symlink) {
|
||||
descr.append(" symlink:true");
|
||||
}
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
static bool isDir(const char* path) {
|
||||
/*
|
||||
* If the source dir is NULL, we assume it's a dir (for /proc and tmpfs)
|
||||
@@ -266,14 +346,12 @@ static bool remountPt(const mount_t& mpt) {
|
||||
{MS_NOATIME, ST_NOATIME},
|
||||
{MS_NODIRATIME, ST_NODIRATIME},
|
||||
{MS_RELATIME, ST_RELATIME},
|
||||
#if defined(MS_NOSYMFOLLOW) && defined(ST_NOSYMFOLLOW)
|
||||
{MS_NOSYMFOLLOW, ST_NOSYMFOLLOW},
|
||||
#endif /* defined(MS_NOSYMFOLLOW) && defined(ST_NOSYMFOLLOW) */
|
||||
};
|
||||
|
||||
const unsigned long per_mountpoint_flags =
|
||||
MS_LAZYTIME | MS_MANDLOCK | MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_NOEXEC |
|
||||
MS_NOSUID | MS_RELATIME | MS_RDONLY | MS_SYNCHRONOUS;
|
||||
MS_NOSUID | MS_RELATIME | MS_RDONLY | MS_SYNCHRONOUS | MS_NOSYMFOLLOW;
|
||||
unsigned long new_flags = MS_REMOUNT | MS_BIND | (mpt.flags & per_mountpoint_flags);
|
||||
for (const auto& i : mountPairs) {
|
||||
if (vfs.f_flag & i.vfs_flag) {
|
||||
@@ -358,6 +436,64 @@ static std::unique_ptr<std::string> getDir(nsj_t* nsj, const char* name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool addMountPt(mount_t* mnt, const std::string& src, const std::string& dst,
|
||||
const std::string& fstype, const std::string& options, uintptr_t flags, isDir_t is_dir,
|
||||
bool is_mandatory, const std::string& src_env, const std::string& dst_env,
|
||||
const std::string& src_content, bool is_symlink) {
|
||||
if (!src_env.empty()) {
|
||||
const char* e = getenv(src_env.c_str());
|
||||
if (e == nullptr) {
|
||||
LOG_W("No such envar:%s", QC(src_env));
|
||||
return false;
|
||||
}
|
||||
mnt->src = e;
|
||||
}
|
||||
mnt->src.append(src);
|
||||
|
||||
if (!dst_env.empty()) {
|
||||
const char* e = getenv(dst_env.c_str());
|
||||
if (e == nullptr) {
|
||||
LOG_W("No such envar:%s", QC(dst_env));
|
||||
return false;
|
||||
}
|
||||
mnt->dst = e;
|
||||
}
|
||||
mnt->dst.append(dst);
|
||||
|
||||
mnt->fs_type = fstype;
|
||||
mnt->options = options;
|
||||
mnt->flags = flags;
|
||||
mnt->is_symlink = is_symlink;
|
||||
mnt->is_mandatory = is_mandatory;
|
||||
mnt->mounted = false;
|
||||
mnt->src_content = src_content;
|
||||
|
||||
switch (is_dir) {
|
||||
case NS_DIR_YES:
|
||||
mnt->is_dir = true;
|
||||
break;
|
||||
case NS_DIR_NO:
|
||||
mnt->is_dir = false;
|
||||
break;
|
||||
case NS_DIR_MAYBE: {
|
||||
if (!src_content.empty()) {
|
||||
mnt->is_dir = false;
|
||||
} else if (mnt->src.empty()) {
|
||||
mnt->is_dir = true;
|
||||
} else if (mnt->flags & MS_BIND) {
|
||||
mnt->is_dir = mnt::isDir(mnt->src.c_str());
|
||||
} else {
|
||||
mnt->is_dir = true;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
LOG_E("Unknown is_dir value: %d", is_dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool initNoCloneNs(nsj_t* nsj) {
|
||||
/*
|
||||
* If CLONE_NEWNS is not used, we would be changing the global mount namespace, so simply
|
||||
@@ -409,9 +545,31 @@ static bool initCloneNs(nsj_t* nsj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& p : nsj->mountpts) {
|
||||
if (!mountPt(&p, destdir->c_str(), tmpdir->c_str()) && p.is_mandatory) {
|
||||
LOG_E("Couldn't mount %s", QC(p.dst));
|
||||
for (const auto& p : nsj->njc.mount()) {
|
||||
uintptr_t flags = (p.rw() ? 0 : MS_RDONLY);
|
||||
if (p.is_bind()) {
|
||||
flags |= (MS_BIND | MS_REC | MS_PRIVATE);
|
||||
}
|
||||
if (p.nosuid()) {
|
||||
flags |= MS_NOSUID;
|
||||
}
|
||||
if (p.nodev()) {
|
||||
flags |= MS_NODEV;
|
||||
}
|
||||
if (p.noexec()) {
|
||||
flags |= MS_NOEXEC;
|
||||
}
|
||||
|
||||
mount_t mpt;
|
||||
if (!addMountPt(&mpt, p.src(), p.dst(), p.fstype(), p.options(), flags,
|
||||
p.has_is_dir() ? (p.is_dir() ? NS_DIR_YES : NS_DIR_NO) : NS_DIR_MAYBE,
|
||||
p.mandatory(), p.prefix_src_env(), p.prefix_dst_env(), p.src_content(),
|
||||
p.is_symlink())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mountPt(&mpt, destdir->c_str(), tmpdir->c_str()) && mpt.is_mandatory) {
|
||||
LOG_E("Couldn't mount %s", QC(mpt.dst));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -478,8 +636,34 @@ static bool initCloneNs(nsj_t* nsj) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& p : nsj->mountpts) {
|
||||
if (!remountPt(p) && p.is_mandatory) {
|
||||
/* Remounting R/O, if needed */
|
||||
for (const auto& p : nsj->njc.mount()) {
|
||||
uintptr_t flags = (p.rw() ? 0 : MS_RDONLY);
|
||||
if (p.is_bind()) {
|
||||
flags |= (MS_BIND | MS_REC | MS_PRIVATE);
|
||||
}
|
||||
if (p.nosuid()) {
|
||||
flags |= MS_NOSUID;
|
||||
}
|
||||
if (p.nodev()) {
|
||||
flags |= MS_NODEV;
|
||||
}
|
||||
if (p.noexec()) {
|
||||
flags |= MS_NOEXEC;
|
||||
}
|
||||
|
||||
mount_t mpt;
|
||||
if (!addMountPt(&mpt, p.src(), p.dst(), p.fstype(), p.options(), flags,
|
||||
p.has_is_dir() ? (p.is_dir() ? NS_DIR_YES : NS_DIR_NO) : NS_DIR_MAYBE,
|
||||
p.mandatory(), p.prefix_src_env(), p.prefix_dst_env(), p.src_content(),
|
||||
p.is_symlink())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We need to set mounted=true to allow remountPt to work, assuming it was mounted
|
||||
*/
|
||||
mpt.mounted = true;
|
||||
if (!remountPt(mpt) && mpt.is_mandatory) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -531,119 +715,4 @@ bool initNs(nsj_t* nsj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool addMountPt(mount_t* mnt, const std::string& src, const std::string& dst,
|
||||
const std::string& fstype, const std::string& options, uintptr_t flags, isDir_t is_dir,
|
||||
bool is_mandatory, const std::string& src_env, const std::string& dst_env,
|
||||
const std::string& src_content, bool is_symlink) {
|
||||
if (!src_env.empty()) {
|
||||
const char* e = getenv(src_env.c_str());
|
||||
if (e == nullptr) {
|
||||
LOG_W("No such envar:%s", QC(src_env));
|
||||
return false;
|
||||
}
|
||||
mnt->src = e;
|
||||
}
|
||||
mnt->src.append(src);
|
||||
|
||||
if (!dst_env.empty()) {
|
||||
const char* e = getenv(dst_env.c_str());
|
||||
if (e == nullptr) {
|
||||
LOG_W("No such envar:%s", QC(dst_env));
|
||||
return false;
|
||||
}
|
||||
mnt->dst = e;
|
||||
}
|
||||
mnt->dst.append(dst);
|
||||
|
||||
mnt->fs_type = fstype;
|
||||
mnt->options = options;
|
||||
mnt->flags = flags;
|
||||
mnt->is_symlink = is_symlink;
|
||||
mnt->is_mandatory = is_mandatory;
|
||||
mnt->mounted = false;
|
||||
mnt->src_content = src_content;
|
||||
|
||||
switch (is_dir) {
|
||||
case NS_DIR_YES:
|
||||
mnt->is_dir = true;
|
||||
break;
|
||||
case NS_DIR_NO:
|
||||
mnt->is_dir = false;
|
||||
break;
|
||||
case NS_DIR_MAYBE: {
|
||||
if (!src_content.empty()) {
|
||||
mnt->is_dir = false;
|
||||
} else if (mnt->src.empty()) {
|
||||
mnt->is_dir = true;
|
||||
} else if (mnt->flags & MS_BIND) {
|
||||
mnt->is_dir = mnt::isDir(mnt->src.c_str());
|
||||
} else {
|
||||
mnt->is_dir = true;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
LOG_E("Unknown is_dir value: %d", is_dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addMountPtHead(nsj_t* nsj, const std::string& src, const std::string& dst,
|
||||
const std::string& fstype, const std::string& options, uintptr_t flags, isDir_t is_dir,
|
||||
bool is_mandatory, const std::string& src_env, const std::string& dst_env,
|
||||
const std::string& src_content, bool is_symlink) {
|
||||
mount_t mnt;
|
||||
if (!addMountPt(&mnt, src, dst, fstype, options, flags, is_dir, is_mandatory, src_env,
|
||||
dst_env, src_content, is_symlink)) {
|
||||
return false;
|
||||
}
|
||||
nsj->mountpts.insert(nsj->mountpts.begin(), mnt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addMountPtTail(nsj_t* nsj, const std::string& src, const std::string& dst,
|
||||
const std::string& fstype, const std::string& options, uintptr_t flags, isDir_t is_dir,
|
||||
bool is_mandatory, const std::string& src_env, const std::string& dst_env,
|
||||
const std::string& src_content, bool is_symlink) {
|
||||
mount_t mnt;
|
||||
if (!addMountPt(&mnt, src, dst, fstype, options, flags, is_dir, is_mandatory, src_env,
|
||||
dst_env, src_content, is_symlink)) {
|
||||
return false;
|
||||
}
|
||||
nsj->mountpts.push_back(mnt);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string describeMountPt(const mount_t& mpt) {
|
||||
std::string descr;
|
||||
|
||||
descr.append(mpt.src.empty() ? "" : QC(mpt.src))
|
||||
.append(mpt.src.empty() ? "" : " -> ")
|
||||
.append(QC(mpt.dst))
|
||||
.append(" flags:")
|
||||
.append(flagsToStr(mpt.flags))
|
||||
.append(" type:")
|
||||
.append(QC(mpt.fs_type))
|
||||
.append(" options:")
|
||||
.append(QC(mpt.options));
|
||||
|
||||
if (mpt.is_dir) {
|
||||
descr.append(" dir:true");
|
||||
} else {
|
||||
descr.append(" dir:false");
|
||||
}
|
||||
if (!mpt.is_mandatory) {
|
||||
descr.append(" mandatory:false");
|
||||
}
|
||||
if (!mpt.src_content.empty()) {
|
||||
descr.append(" src_content_len:").append(std::to_string(mpt.src_content.length()));
|
||||
}
|
||||
if (mpt.is_symlink) {
|
||||
descr.append(" symlink:true");
|
||||
}
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
} // namespace mnt
|
||||
|
||||
10
mnt.h
10
mnt.h
@@ -38,15 +38,7 @@ typedef enum {
|
||||
} isDir_t;
|
||||
|
||||
bool initNs(nsj_t* nsj);
|
||||
bool addMountPtHead(nsj_t* nsj, const std::string& src, const std::string& dst,
|
||||
const std::string& fstype, const std::string& options, uintptr_t flags, isDir_t is_dir,
|
||||
bool is_mandatory, const std::string& src_env, const std::string& dst_env,
|
||||
const std::string& src_content, bool is_symlink);
|
||||
bool addMountPtTail(nsj_t* nsj, const std::string& src, const std::string& dst,
|
||||
const std::string& fstype, const std::string& options, uintptr_t flags, isDir_t is_dir,
|
||||
bool is_mandatory, const std::string& src_env, const std::string& dst_env,
|
||||
const std::string& src_content, bool is_symlink);
|
||||
const std::string describeMountPt(const mount_t& mpt);
|
||||
const std::string describeMountPt(const nsjail::MountPt& mpt);
|
||||
|
||||
} // namespace mnt
|
||||
|
||||
|
||||
19
nsjail.h
19
nsjail.h
@@ -58,19 +58,6 @@ struct pids_t {
|
||||
pid_t pasta_pid;
|
||||
};
|
||||
|
||||
struct mount_t {
|
||||
std::string src;
|
||||
std::string src_content;
|
||||
std::string dst;
|
||||
std::string fs_type;
|
||||
std::string options;
|
||||
uintptr_t flags;
|
||||
bool is_dir;
|
||||
bool is_symlink;
|
||||
bool is_mandatory;
|
||||
bool mounted;
|
||||
};
|
||||
|
||||
struct idmap_t {
|
||||
uid_t inside_id;
|
||||
uid_t outside_id;
|
||||
@@ -102,20 +89,16 @@ struct nsj_t {
|
||||
std::vector<std::string> argv;
|
||||
uid_t orig_uid;
|
||||
uid_t orig_euid;
|
||||
std::vector<mount_t> mountpts;
|
||||
std::map<pid_t, pids_t> pids;
|
||||
std::vector<idmap_t> uids;
|
||||
std::vector<idmap_t> gids;
|
||||
std::vector<std::string> envs;
|
||||
std::vector<int> openfds;
|
||||
std::vector<int> caps;
|
||||
std::vector<std::string> ifaces;
|
||||
|
||||
std::vector<pipemap_t> pipes;
|
||||
std::string chroot;
|
||||
std::string proc_path;
|
||||
bool is_root_rw;
|
||||
bool is_proc_rw;
|
||||
unsigned long personality;
|
||||
struct sock_fprog seccomp_fprog;
|
||||
};
|
||||
|
||||
|
||||
@@ -197,8 +197,8 @@ static void newProc(nsj_t* nsj, int netfd, int fd_in, int fd_out, int fd_err, in
|
||||
if (!nsj->njc.keep_env()) {
|
||||
clearenv();
|
||||
}
|
||||
for (const auto& env : nsj->envs) {
|
||||
putenv(const_cast<char*>(env.c_str()));
|
||||
for (ssize_t i = 0; i < nsj->njc.envar_size(); i++) {
|
||||
putenv(const_cast<char*>(nsj->njc.envar(i).c_str()));
|
||||
}
|
||||
|
||||
auto connstr = net::connToText(netfd, /* remote= */ true, NULL);
|
||||
|
||||
Reference in New Issue
Block a user