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:
Robert Swiecki
2025-11-24 17:01:46 +01:00
parent 06350410ac
commit 57a6e17e50
9 changed files with 313 additions and 313 deletions

13
caps.cc
View File

@@ -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)",

View File

@@ -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':

View File

@@ -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()) {

View File

@@ -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
View File

@@ -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
View File

@@ -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

View File

@@ -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;
};

View File

@@ -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);