chiark / gitweb /
nspawn: spawn shell under specified --user
authorMichal Vyskocil <mvyskocil@suse.cz>
Wed, 29 Jun 2011 12:22:46 +0000 (14:22 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 1 Jul 2011 21:51:14 +0000 (23:51 +0200)
Add -u/--user option, which changes the effective and real user and
group id to the new value. The user must exists in the chroot, otherwise
it will fail. Both username and user id are accepted. The user home is
created as well.

It also setup HOME, USER, LOGNAME and SHELL variables .

man/systemd-nspawn.xml
src/nspawn.c

index e1b33f72c50ae58dba6e540ef4f50ed5e774ddc0..03c39fc3def88631ff30b51abe06ac1d4293e554 100644 (file)
                                 used.</para></listitem>
                         </varlistentry>
 
+                        <varlistentry>
+                                <term><option>--user=</option></term>
+                                <term><option>--u</option></term>
+
+                                <listitem><para>Run the command
+                                under specified user, create home
+                                directory and cd into it. As rest
+                                of systemd-nspawn, this is not
+                                the security feature and limits
+                                against accidental changes only.
+                                </para></listitem>
+                        </varlistentry>
+
                 </variablelist>
 
         </refsect1>
index 1ade6e25eff1c01a0b42ba1eaa397de28b6d45c4..bead9eea2b67a8042e90ee76ad879e1cae915bde 100644 (file)
@@ -36,6 +36,7 @@
 #include <sys/epoll.h>
 #include <termios.h>
 #include <sys/signalfd.h>
+#include <grp.h>
 
 #include "log.h"
 #include "util.h"
 #include "strv.h"
 
 static char *arg_directory = NULL;
+static char *arg_user = NULL;
 
 static int help(void) {
 
         printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
                "Spawn a minimal namespace container for debugging, testing and building.\n\n"
                "  -h --help            Show this help\n"
-               "  -D --directory=NAME  Root directory for the container\n",
+               "  -D --directory=NAME  Root directory for the container\n"
+               "  -u --user=USER       Run the command under specified user or uid\n",
                program_invocation_short_name);
 
         return 0;
@@ -62,6 +65,7 @@ static int parse_argv(int argc, char *argv[]) {
         static const struct option options[] = {
                 { "help",      no_argument,       NULL, 'h' },
                 { "directory", required_argument, NULL, 'D' },
+                { "user",      optional_argument, NULL, 'u' },
                 { NULL,        0,                 NULL, 0   }
         };
 
@@ -70,7 +74,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "+hD:", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "+hD:u:", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -87,6 +91,15 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case 'u':
+                        free(arg_user);
+                        if (!(arg_user = strdup(optarg))) {
+                                log_error("Failed to duplicate user name.");
+                                return -ENOMEM;
+                        }
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -693,14 +706,19 @@ int main(int argc, char *argv[]) {
                 /* child */
 
                 const char *hn;
+                const char *home = NULL;
+                uid_t uid = (uid_t) -1;
+                gid_t gid = (gid_t) -1;
                 const char *envp[] = {
-                        "HOME=/root",
                         "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
-                        NULL,
+                        NULL, /* TERM */
+                        NULL, /* HOME */
+                        NULL, /* USER */
+                        NULL, /* LOGNAME */
                         NULL
                 };
 
-                envp[2] = strv_find_prefix(environ, "TERM=");
+                envp[1] = strv_find_prefix(environ, "TERM=");
 
                 close_nointr_nofail(master);
 
@@ -757,13 +775,53 @@ int main(int argc, char *argv[]) {
                 if (drop_capabilities() < 0)
                         goto child_fail;
 
+                if (arg_user) {
+
+                        if (get_user_creds((const char**)&arg_user, &uid, &gid, &home) < 0) {
+                                log_error("get_user_creds() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (mkdir_parents(home, 0775) < 0) {
+                                log_error("mkdir_parents() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (safe_mkdir(home, 0775, uid, gid) < 0) {
+                                log_error("safe_mkdir() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (initgroups((const char*)arg_user, gid) < 0) {
+                                log_error("initgroups() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (setregid(gid, gid) < 0) {
+                                log_error("setregid() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (setreuid(uid, uid) < 0) {
+                                log_error("setreuid() failed: %m");
+                                goto child_fail;
+                        }
+                }
+
+                if ((asprintf((char**)(envp + 2), "HOME=%s", home? home: "/root") < 0) ||
+                    (asprintf((char**)(envp + 3), "USER=%s", arg_user? arg_user : "root") < 0) ||
+                    (asprintf((char**)(envp + 4), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) {
+                    log_error("environment setup failed: %m");
+                    goto child_fail;
+                }
+
                 if ((hn = file_name_from_path(arg_directory)))
                         sethostname(hn, strlen(hn));
 
                 if (argc > optind)
                         execvpe(argv[optind], argv + optind, (char**) envp);
                 else {
-                        chdir("/root");
+                        chdir(home? home : "/root");
                         execle("/bin/bash", "-bash", NULL, (char**) envp);
                 }