[systemd-devel] [PATCH 2/2] nspawn: spawn shell under specified --user

Michal Vyskocil mvyskocil at suse.cz
Wed Jun 29 05:22:46 PDT 2011


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 |   13 +++++++++
 src/nspawn.c           |   70 +++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 77 insertions(+), 6 deletions(-)

diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index e1b33f7..d3d056c 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -138,6 +138,19 @@
                                 directory will be
                                 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>
 
diff --git a/src/nspawn.c b/src/nspawn.c
index 1ade6e2..5c44d15 100644
--- a/src/nspawn.c
+++ b/src/nspawn.c
@@ -36,6 +36,7 @@
 #include <sys/epoll.h>
 #include <termios.h>
 #include <sys/signalfd.h>
+#include <grp.h>
 
 #include "log.h"
 #include "util.h"
@@ -45,13 +46,15 @@
 #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);
                 }
 
-- 
1.7.4.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/systemd-devel/attachments/20110629/a67ce1fc/attachment.pgp>


More information about the systemd-devel mailing list