Implementation of MODE_STANDALONE_EXECVE
authorJagger <robert@swiecki.net>
Sat, 15 Aug 2015 14:02:38 +0000 (16:02 +0200)
committerJagger <robert@swiecki.net>
Sat, 15 Aug 2015 14:02:38 +0000 (16:02 +0200)
cmdline.c
common.h
contain.c
log.c
nsjail.c
subproc.c

index 1f207ff3614efe1fc9cbcabd34d17b03280e9068..a2987c3b1818dc7e3eb4587f641ba09880118148 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -74,6 +74,9 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
        case MODE_STANDALONE_ONCE:
                LOG_I("Mode: STANDALONE_ONCE");
                break;
+       case MODE_STANDALONE_EXECVE:
+               LOG_I("Mode: STANDALONE_EXECVE");
+               break;
        case MODE_STANDALONE_RERUN:
                LOG_I("Mode: STANDALONE_RERUN");
                break;
@@ -183,6 +186,7 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                .initial_gid = getgid(),
                .max_conns_per_ip = 0,
                .tmpfs_size = 4 * (1024 * 1024),
+               .mount_proc = true,
        };
        /*  *INDENT-OFF* */
 
@@ -200,7 +204,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                {{"help", no_argument, NULL, 'h'}, "Help plz.."},
                {{"mode", required_argument, NULL, 'M'}, "Execution mode (default: l [MODE_LISTEN_TCP]):\n"
                        "\tl: Listen to connections on a TCP port (specified with --port) [MODE_LISTEN_TCP]\n"
-                       "\to: Immediately launch a single process on a console [MODE_STANDALONE_ONCE]\n"
+                       "\to: Immediately launch a single process on a console using clone/execve [MODE_STANDALONE_ONCE]\n"
+                       "\te: Immediately launch a single process on a console using execve [MODE_STANDALONE_EXECVE]\n"
                        "\tr: Immediately launch a single process on a console, keep doing it forever [MODE_STANDALONE_RERUN]"},
                {{"chroot", required_argument, NULL, 'c'}, "Directory containing / of the jail (default: '/chroot')"},
                {{"user", required_argument, NULL, 'u'}, "Username/uid of processess inside the jail (default: 'nobody')"},
@@ -213,7 +218,9 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                {{"daemon", no_argument, NULL, 'd'}, "Daemonize after start? (default: false)"},
                {{"verbose", no_argument, NULL, 'v'}, "Verbose output (default: false)"},
                {{"keep_env", no_argument, NULL, 'e'}, "Should all environment variables be passed to the child? (default: false)"},
-               {{"keep_caps", no_argument, NULL, 0x0502}, "Don't drop capabilities (DANGEROUS) (default: false)"},
+               {{"keep_caps", no_argument, NULL, 0x0501}, "Don't drop capabilities (DANGEROUS) (default: false)"},
+               {{"silent", no_argument, NULL, 0x0502}, "Redirect child's fd:0/1/2 to /dev/null (default: false)"},
+               {{"disable_sandbox", no_argument, NULL, 0x0503}, "Don't enable the seccomp-bpf sandboxing (default: false)"},
                {{"rlimit_as", required_argument, NULL, 0x0201}, "RLIMIT_AS in MB, 'max' for RLIM_INFINITY, 'def' for the current value (default: 512)"},
                {{"rlimit_core", required_argument, NULL, 0x0202}, "RLIMIT_CORE in MB, 'max' for RLIM_INFINITY, 'def' for the current value (default: 0)"},
                {{"rlimit_cpu", required_argument, NULL, 0x0203}, "RLIMIT_CPU, 'max' for RLIM_INFINITY, 'def' for the current value (default: 600)"},
@@ -232,14 +239,13 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                {{"disable_clone_newpid", no_argument, NULL, 0x0404}, "Don't use CLONE_NEWPID (default: false)"},
                {{"disable_clone_newipc", no_argument, NULL, 0x0405}, "Don't use CLONE_NEWIPC (default: false)"},
                {{"disable_clone_newuts", no_argument, NULL, 0x0406}, "Don't use CLONE_NEWUTS (default: false)"},
-               {{"disable_sandbox", no_argument, NULL, 0x0501}, "Don't enable the seccomp-bpf sandboxing (default: false)"},
-               {{"rw", no_argument, NULL, 0x0503}, "Mount / as RW (default: RO)"},
-               {{"silent", no_argument, NULL, 0x0504}, "Redirect child's fd:0/1/2 to /dev/null (default: false)"},
+               {{"rw", no_argument, NULL, 0x0601}, "Mount / as RW (default: RO)"},
                {{"bindmount_ro", required_argument, NULL, 'R'}, "List of mountpoints to be mounted --bind (ro) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'. (default: none)"},
                {{"bindmount", required_argument, NULL, 'B'}, "List of mountpoints to be mounted --bind (rw) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'. (default: none)"},
                {{"tmpfsmount", required_argument, NULL, 'T'}, "List of mountpoints to be mounted as RW/tmpfs inside the container. Can be specified multiple times. Supports 'dest' syntax. (default: none)"},
                {{"iface", required_argument, NULL, 'I'}, "Interface which will be cloned (MACVTAP) and put inside the subprocess' namespace"},
-               {{"tmpfs_size", required_argument, NULL, 0x0506}, "Number of bytes to allocate for tmpfsmounts in bytes (default: 4194304)"},
+               {{"tmpfs_size", required_argument, NULL, 0x0602}, "Number of bytes to allocate for tmpfsmounts (default: 4194304)"},
+               {{"disable_proc", no_argument, NULL, 0x0603}, "Disable mounting /proc (default: false)"},
                {{0, 0, 0, 0}, NULL},
        };
         /*  *INDENT-ON* */
@@ -268,9 +274,6 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                case 'i':
                        nsjconf->max_conns_per_ip = strtoul(optarg, NULL, 0);
                        break;
-               case 0x0506:
-                       nsjconf->tmpfs_size = strtoull(optarg, NULL, 0);
-                       break;
                case 'u':
                        user = optarg;
                        break;
@@ -351,16 +354,22 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                        nsjconf->clone_newuts = false;
                        break;
                case 0x0501:
-                       nsjconf->apply_sandbox = false;
+                       nsjconf->keep_caps = true;
                        break;
                case 0x0502:
-                       nsjconf->keep_caps = true;
+                       nsjconf->is_silent = true;
                        break;
                case 0x0503:
+                       nsjconf->apply_sandbox = false;
+                       break;
+               case 0x0601:
                        nsjconf->is_root_rw = true;
                        break;
-               case 0x0504:
-                       nsjconf->is_silent = true;
+               case 0x0602:
+                       nsjconf->tmpfs_size = strtoull(optarg, NULL, 0);
+                       break;
+               case 0x0603:
+                       nsjconf->mount_proc = false;
                        break;
                case 'R':
                        {
@@ -400,6 +409,9 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                        case 'o':
                                nsjconf->mode = MODE_STANDALONE_ONCE;
                                break;
+                       case 'e':
+                               nsjconf->mode = MODE_STANDALONE_EXECVE;
+                               break;
                        case 'r':
                                nsjconf->mode = MODE_STANDALONE_RERUN;
                                break;
index 95e9349a076e9e7b991bc123a2d605ee7072356d..ae303883523f4c46aeedf86885de590d5fe99e3d 100644 (file)
--- a/common.h
+++ b/common.h
@@ -46,6 +46,7 @@ struct constchar_t {
 enum mode_t {
        MODE_LISTEN_TCP = 0,
        MODE_STANDALONE_ONCE,
+       MODE_STANDALONE_EXECVE,
        MODE_STANDALONE_RERUN
 };
 
@@ -84,6 +85,7 @@ struct nsjconf_t {
        gid_t initial_gid;
        unsigned int max_conns_per_ip;
        size_t tmpfs_size;
+       bool mount_proc;
         LIST_HEAD(pidslist, pids_t) pids;
         LIST_HEAD(rwbindmountptslist, constchar_t) rwbindmountpts;
         LIST_HEAD(robindmountptslist, constchar_t) robindmountpts;
index e9d5a7216c7712c9694ff911cd2ba6de5b4b4d9c..d4430a970330446b0d4aba3b48246582d302fa96 100644 (file)
--- a/contain.c
+++ b/contain.c
@@ -270,6 +270,30 @@ static bool remountBindMount(const char *spec, unsigned long flags)
        return success;
 }
 
+static bool containMountProc(struct nsjconf_t *nsjconf, const char *newrootdir)
+{
+       char procrootdir[PATH_MAX];
+       snprintf(procrootdir, sizeof(procrootdir), "%s/proc", newrootdir);
+
+       if (nsjconf->mount_proc == false) {
+               return true;
+       }
+
+       if (nsjconf->mode == MODE_STANDALONE_EXECVE) {
+               if (mount("/proc", procrootdir, NULL, MS_REC | MS_BIND, NULL) == -1) {
+                       PLOG_E("mount('/proc', '%s', MS_REC|MS_BIND)", procrootdir);
+                       return false;
+               }
+               return true;
+       }
+       if (mount(NULL, procrootdir, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) == -1) {
+               PLOG_E("mount('%s', 'proc')", procrootdir);
+               return false;
+       }
+
+       return true;
+}
+
 bool containMountFS(struct nsjconf_t * nsjconf)
 {
        const char *destdir = "/tmp";
@@ -287,11 +311,7 @@ bool containMountFS(struct nsjconf_t * nsjconf)
                PLOG_E("mount('%s', '%s', MS_BIND | MS_REC)", nsjconf->chroot, newrootdir);
                return false;
        }
-
-       char procrootdir[PATH_MAX];
-       snprintf(procrootdir, sizeof(procrootdir), "%s/proc", newrootdir);
-       if (mount(NULL, procrootdir, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) == -1) {
-               PLOG_E("mount('%s', 'proc')", procrootdir);
+       if (containMountProc(nsjconf, newrootdir) == false) {
                return false;
        }
 
@@ -455,7 +475,9 @@ bool containMakeFdsCOE(void)
 bool containSetupFD(struct nsjconf_t * nsjconf, int fd_in, int fd_out, int fd_err, int fd_log)
 {
        /* Make sure all logs go to the parent process from now on */
-       logRedirectLogFD(fd_log);
+       if (fd_log != -1) {
+               logRedirectLogFD(fd_log);
+       }
 
        if (nsjconf->mode != MODE_LISTEN_TCP) {
                if (nsjconf->is_silent == false) {
diff --git a/log.c b/log.c
index 19f6d5ee6fd2c448b08f07fac8f9501fe8358429..ca4559a078fc55412372c5c9b581f066aaf12302 100644 (file)
--- a/log.c
+++ b/log.c
@@ -52,7 +52,7 @@ bool logInitLogFile(struct nsjconf_t *nsjconf, const char *logfile, bool is_verb
                logfile = _LOG_DEFAULT_FILE;
        }
        if (logfile == NULL) {
-               logfile = "/dev/tty";
+               logfile = "/proc/self/fd/2";
        }
        log_fd = open(logfile, O_CREAT | O_RDWR | O_APPEND, 0640);
        if (log_fd == -1) {
index b1acb177d3ef406010f4bcf6b8796355b27499d2..1bf0371ec8a2672d3940bd3184ee4b55f5d653bb 100644 (file)
--- a/nsjail.c
+++ b/nsjail.c
@@ -92,8 +92,12 @@ static bool nsjailSetSigHandlers(void)
        return true;
 }
 
-static bool nsjailSetTimer(void)
+static bool nsjailSetTimer(struct nsjconf_t *nsjconf)
 {
+       if (nsjconf->mode == MODE_STANDALONE_EXECVE) {
+               return true;
+       }
+
        struct itimerval it = {
                .it_value = {.tv_sec = 1,.tv_usec = 0},
                .it_interval = {.tv_sec = 1,.tv_usec = 0},
@@ -173,7 +177,7 @@ int main(int argc, char *argv[])
        if (nsjailSetSigHandlers() == false) {
                exit(1);
        }
-       if (nsjailSetTimer() == false) {
+       if (nsjailSetTimer(&nsjconf) == false) {
                exit(1);
        }
 
index ff16f962bb402831ce282b38605f57a6eaf419c2..3e75bdf0db5586603a4a87fa6a341b9e79b34d27 100644 (file)
--- a/subproc.c
+++ b/subproc.c
@@ -204,7 +204,7 @@ void subprocRunChild(struct nsjconf_t *nsjconf, int fd_in, int fd_out, int fd_er
                return;
        }
 
-       unsigned int flags = SIGCHLD;
+       unsigned int flags = 0UL;
        flags |= (nsjconf->clone_newnet ? CLONE_NEWNET : 0);
        flags |= (nsjconf->clone_newuser ? CLONE_NEWUSER : 0);
        flags |= (nsjconf->clone_newns ? CLONE_NEWNS : 0);
@@ -212,6 +212,21 @@ void subprocRunChild(struct nsjconf_t *nsjconf, int fd_in, int fd_out, int fd_er
        flags |= (nsjconf->clone_newipc ? CLONE_NEWIPC : 0);
        flags |= (nsjconf->clone_newuts ? CLONE_NEWUTS : 0);
 
+       if (nsjconf->mode == MODE_STANDALONE_EXECVE) {
+               if (nsjconf->clone_newpid) {
+                       LOG_W("CLONE_NEWPID requested. It causes troubles with unshare() "
+                             "[ENOMEM with clone/fork/vfork]. Disabling it");
+                       flags &= ~(CLONE_NEWPID);
+               }
+               LOG_D("Entering namespace with flags: %#x", flags);
+               if (unshare(flags) == -1) {
+                       PLOG_E("unshare(%u)", flags);
+                       _exit(EXIT_FAILURE);
+               }
+               subprocNewProc(nsjconf, fd_in, fd_out, fd_err, -1);
+       }
+
+       flags |= SIGCHLD;
        LOG_D("Creating new process with clone flags: %#x", flags);
 
        int pipefd[2];