{ { "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" },
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;
}
nsjconf->caps.push_back(cap);
} break;
+ case 0x0600:
+ nsjconf->use_switchroot = true;
+ break;
case 0x0601:
nsjconf->is_root_rw = true;
break;
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;
}