Skip to content

Commit 1a7fcd8

Browse files
committed
Add option to create init inside the container
In EXECVE execution mode, nsjail already creates an init process that reaps zombie processes inside the container. Add a new --init command line option to allow the creation of the init process also in the ONCE execution mode.
1 parent d6454b4 commit 1a7fcd8

5 files changed

Lines changed: 55 additions & 13 deletions

File tree

cmdline.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ static const struct custom_option custom_opts[] = {
177177
{ { "use_core_scheduling", no_argument, nullptr, 0x709 }, "Enable Linux core scheduling (PR_SCHED_CORE, requires kernel >= 5.14)" },
178178
{ { "user_net", no_argument, nullptr, 0x70A }, "Enable user-mode networking (nstun backend)" },
179179
{ { "oom_score_adj", required_argument, nullptr, 0x800 }, "OOM score adjustment for the sandbox (-1000 to 1000) (default: not set)" },
180+
{ { "init", no_argument, NULL, 0x907 }, "Run an init process inside the container. Incompatible with disable_clone_newpid and with RERUN and LISTEN modes. Implicitly enabled in EXECVE mode unless disable_clone_newpid is also provided." },
181+
180182
};
181183
// clang-format on
182184

@@ -992,6 +994,9 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
992994
case 0x800:
993995
nsj->njc.set_oom_score_adj((int32_t)strtol(optarg, NULL, 0));
994996
break;
997+
case 0x907:
998+
nsj->njc.set_init(true);
999+
break;
9951000
default:
9961001
cmdlineUsage(argv[0]);
9971002
return nullptr;
@@ -1015,6 +1020,21 @@ std::unique_ptr<nsj_t> parseArgs(int argc, char* argv[]) {
10151020
LOG_F("cannot set both cgroup_mem_memsw_max and cgroup_mem_swap_max");
10161021
}
10171022

1023+
if (nsj->njc.init()) {
1024+
if (nsj->njc.mode() == nsjail::Mode::LISTEN ||
1025+
nsj->njc.mode() == nsjail::Mode::RERUN) {
1026+
LOG_F("cannot enable init process with mode LISTEN or RERUN");
1027+
}
1028+
if (!nsj->njc.clone_newpid()) {
1029+
LOG_F("cannot enable init process with disable_clone_newpid");
1030+
}
1031+
}
1032+
1033+
if (nsj->njc.mode() == nsjail::Mode::EXECVE && nsj->njc.clone_newpid()) {
1034+
nsj->njc.set_init(true);
1035+
LOG_D("enabling init process for execve mode");
1036+
}
1037+
10181038
return nsj;
10191039
}
10201040

config.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ message NsJailConfig {
435435
/* Binary path (with arguments) to be executed. If not specified here, it
436436
can be specified with cmd-line as "-- /path/to/command arg1 arg2" */
437437
optional Exe exec_bin = 98;
438+
439+
/* Run an init process inside the container. */
440+
optional bool init = 102 [default = false];
438441
}
439442

440443
// vim: set noexpandtab ts=4 sw=4:

pid.cc

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,9 @@
3333

3434
namespace pid {
3535

36-
bool initNs(nsj_t* nsj) {
37-
if (nsj->njc.mode() != nsjail::Mode::EXECVE) {
38-
return true;
39-
}
40-
if (!nsj->njc.clone_newpid()) {
41-
return true;
42-
}
43-
36+
bool newInitProc() {
4437
LOG_D("Creating a dummy 'init' process");
4538

46-
/*
47-
* If -Me is used then we need to create permanent init inside PID ns, otherwise only the
48-
* first clone/fork will work, and the rest will fail with ENOMEM (see 'man pid_namespaces'
49-
* for details on this behavior)
50-
*/
5139
pid_t pid = subproc::cloneProc(CLONE_FS, 0);
5240
if (pid == -1) {
5341
PLOG_E("Couldn't create a dummy init process");
@@ -84,4 +72,21 @@ bool initNs(nsj_t* nsj) {
8472
}
8573
}
8674

75+
bool initNs(nsj_t* nsj) {
76+
if (nsj->njc.mode() != nsjail::Mode::EXECVE) {
77+
return true;
78+
}
79+
if (!nsj->njc.init()) {
80+
return true;
81+
}
82+
83+
/*
84+
* If -Me is used then we need to create permanent init inside PID ns, otherwise only the
85+
* first clone/fork will work, and the rest will fail with ENOMEM (see 'man pid_namespaces'
86+
* for details on this behavior)
87+
*/
88+
89+
return newInitProc();
90+
}
91+
8792
} // namespace pid

pid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
namespace pid {
3030

31+
bool newInitProc();
3132
bool initNs(nsj_t* nsj);
3233

3334
} // namespace pid

subproc.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "macros.h"
5252
#include "net.h"
5353
#include "nstun/nstun.h"
54+
#include "pid.h"
5455
#include "sandbox.h"
5556
#include "unotify/unotify.h"
5657
#include "user.h"
@@ -535,6 +536,18 @@ pid_t runChild(nsj_t* nsj, int netfd, int fd_in, int fd_out, int fd_err) {
535536
LOG_D("Creating new process with clone flags:%s and exit_signal:SIGCHLD",
536537
cloneFlagsToStr(flags).c_str());
537538

539+
if (nsj->njc.init() && nsj->njc.mode() == nsjail::Mode::ONCE) {
540+
LOG_D("unshare(flags: %s)", cloneFlagsToStr(flags).c_str());
541+
if (unshare(flags) == -1) {
542+
PLOG_F("unshare(%s)", cloneFlagsToStr(flags).c_str());
543+
}
544+
if (!pid::newInitProc()) {
545+
return -1;
546+
}
547+
// Clear clone flags as they have already been applied by unshare()
548+
flags = 0;
549+
}
550+
538551
int sv[2];
539552
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) == -1) {
540553
PLOG_E("socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC) failed");

0 commit comments

Comments
 (0)