From: Eli Zrihen Date: Thu, 17 Jun 2021 11:57:01 +0000 (+0300) Subject: Added use_switchroot option X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dab1713ac9fb431f1cc6fd68b8f31b2a1caa2cb0;p=platform%2Fupstream%2Fnsjail.git Added use_switchroot option --- diff --git a/cmdline.cc b/cmdline.cc index d706b76..793f611 100644 --- a/cmdline.cc +++ b/cmdline.cc @@ -76,6 +76,7 @@ struct custom_option custom_opts[] = { { { "exec_file", required_argument, NULL, 'x' }, "File to exec (default: argv[0])" }, { { "execute_fd", no_argument, NULL, 0x0607 }, "Use execveat() to execute a file-descriptor instead of executing the binary path. In such case argv[0]/exec_file denotes a file path before mount namespacing" }, { { "chroot", required_argument, NULL, 'c' }, "Directory containing / of the jail (default: none)" }, + { { "use_switchroot", no_argument, NULL, 0x600 }, "When creating a mount namespace, use switch_root rather then the default pivot_root (usefull when using rootfs, on which the kernel disallows pivot_root)" }, { { "rw", no_argument, NULL, 0x601 }, "Mount chroot dir (/) R/W (default: R/O)" }, { { "user", required_argument, NULL, 'u' }, "Username/uid of processes inside the jail (default: your current uid). You can also use inside_ns_uid:outside_ns_uid:count convention here. Can be specified multiple times" }, { { "group", required_argument, NULL, 'g' }, "Groupname/gid of processes inside the jail (default: your current gid). You can also use inside_ns_gid:global_ns_gid:count convention here. Can be specified multiple times" }, @@ -431,6 +432,7 @@ std::unique_ptr parseArgs(int argc, char* argv[]) { nsjconf->clone_newnet = true; nsjconf->clone_newuser = true; nsjconf->clone_newns = true; + nsjconf->use_switchroot = false; nsjconf->clone_newpid = true; nsjconf->clone_newipc = true; nsjconf->clone_newuts = true; @@ -648,6 +650,9 @@ std::unique_ptr parseArgs(int argc, char* argv[]) { } nsjconf->caps.push_back(cap); } break; + case 0x0600: + nsjconf->use_switchroot = true; + break; case 0x0601: nsjconf->is_root_rw = true; break; diff --git a/config.cc b/config.cc index 413f6fc..9c4cf73 100644 --- a/config.cc +++ b/config.cc @@ -184,6 +184,8 @@ static bool configParseInternal(nsjconf_t* nsjconf, const nsjail::NsJailConfig& nsjconf->clone_newuts = njc.clone_newuts(); nsjconf->clone_newcgroup = njc.clone_newcgroup(); nsjconf->clone_newtime = njc.clone_newtime(); + + nsjconf->use_switchroot = njc.use_switchroot(); for (ssize_t i = 0; i < njc.uidmap_size(); i++) { if (!user::parseId(nsjconf, njc.uidmap(i).inside_id(), njc.uidmap(i).outside_id(), diff --git a/config.proto b/config.proto index 8c55991..31f8342 100644 --- a/config.proto +++ b/config.proto @@ -86,6 +86,9 @@ message NsJailConfig { /* Initial current working directory for the binary */ optional string cwd = 9 [default = "/"]; + /* Defines whether to use switch_root or pivot_root */ + optional bool use_switchroot = 88 [default = false]; + /* TCP port to listen to. Valid with mode=LISTEN only */ optional uint32 port = 10 [default = 0]; /* Host to bind to for mode=LISTEN. Must be in IPv6 format */ diff --git a/mnt.cc b/mnt.cc index ef2dbd7..e99f4d8 100644 --- a/mnt.cc +++ b/mnt.cc @@ -398,30 +398,61 @@ static bool initCloneNs(nsjconf_t* nsjconf) { PLOG_E("umount2('%s', MNT_DETACH)", tmpdir->c_str()); return false; } - /* - * This requires some explanation: It's actually possible to pivot_root('/', '/'). - * After this operation has been completed, the old root is mounted over the new - * root, and it's OK to simply umount('/') now, and to have new_root as '/'. This - * allows us not care about providing any special directory for old_root, which is - * sometimes not easy, given that e.g. /tmp might not always be present inside - * new_root - */ - if (util::syscall( - __NR_pivot_root, (uintptr_t)destdir->c_str(), (uintptr_t)destdir->c_str()) == -1) { - PLOG_E("pivot_root('%s', '%s')", destdir->c_str(), destdir->c_str()); - return false; - } - - if (umount2("/", MNT_DETACH) == -1) { - PLOG_E("umount2('/', MNT_DETACH)"); - return false; - } - - for (const auto& p : nsjconf->mountpts) { - if (!remountPt(p) && p.is_mandatory) { - return false; - } - } + + if (false == nsjconf->use_switchroot) { + /* + * This requires some explanation: It's actually possible to pivot_root('/', '/'). + * After this operation has been completed, the old root is mounted over the new + * root, and it's OK to simply umount('/') now, and to have new_root as '/'. This + * allows us not care about providing any special directory for old_root, which is + * sometimes not easy, given that e.g. /tmp might not always be present inside + * new_root + */ + if (util::syscall( + __NR_pivot_root, (uintptr_t)destdir->c_str(), (uintptr_t)destdir->c_str()) == -1) { + PLOG_E("pivot_root('%s', '%s')", destdir->c_str(), destdir->c_str()); + return false; + } + + if (umount2("/", MNT_DETACH) == -1) { + PLOG_E("umount2('/', MNT_DETACH)"); + return false; + } + } else { + /* + * pivot_root would normally un-mount the old root, however in certain cases this + * operation is forbidden. There are systems (mainly embedded) that keep their root + * file system in RAM, when initially loaded by the kernel (e.g. initramfs), + * and there is no other file system that is mounted on top of it.In such systems, + * there is option to pivot_root! + * For more information, see kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt. + * switch_root alternative: + * Innstead of un-mounting the old rootfs, it is over mounted by moving the new root to it. + * This way, we prevent the process from hacking its way back into the old root. + */ + if (chdir(destdir->c_str()) == -1) { + PLOG_E("chdir('%s')", destdir->c_str()); + return false; + } + + /* mount moving the new root on top of '/'. This operation is atomic and doesn't involve + un-mounting '/' at any stage */ + if (mount(".", "/", NULL, MS_MOVE, NULL) == -1) { + PLOG_E("mount('/', %s, NULL, MS_MOVE, NULL)", destdir->c_str()); + return false; + } + + if (chroot(".") == -1) { + PLOG_E("chroot('%s')", destdir->c_str()); + return false; + } + } + + for (const auto& p : nsjconf->mountpts) { + if (!remountPt(p) && p.is_mandatory) { + return false; + } + } return true; } diff --git a/nsjail.h b/nsjail.h index 7dd588e..75acc1f 100644 --- a/nsjail.h +++ b/nsjail.h @@ -120,6 +120,7 @@ struct nsjconf_t { bool clone_newnet; bool clone_newuser; bool clone_newns; + bool use_switchroot; bool clone_newpid; bool clone_newipc; bool clone_newuts;