]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
lxc: add possibility to define init uid/gid
authorCédric Bosdonnat <cbosdonnat@suse.com>
Tue, 6 Jun 2017 08:54:16 +0000 (10:54 +0200)
committerCédric Bosdonnat <cbosdonnat@suse.com>
Tue, 11 Jul 2017 08:41:24 +0000 (10:41 +0200)
Users may want to run the init command of a container as a special
user / group. This is achieved by adding <inituser> and <initgroup>
elements. Note that the user can either provide a name or an ID to
specify the user / group to be used.

This commit also fixes a side effect of being able to run the command
as a non-root user: the user needs rights on the tty to allow shell
job control.

Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
docs/formatdomain.html.in
docs/schemas/domaincommon.rng
src/conf/domain_conf.c
src/conf/domain_conf.h
src/lxc/lxc_container.c
tests/lxcxml2xmldata/lxc-inituser.xml [new file with mode: 0644]
tests/lxcxml2xmltest.c

index c155e2ae060a1651397aec338d7b4bbdd222ca48..7f4bc1d21274f3fe405357a995187d75f8bbf48b 100644 (file)
       To set a custom work directory for the init, use the <code>initdir</code>
       element.
     </p>
+    <p>
+      To run the init command as a given user or group, use the <code>inituser</code>
+      or <code>initgroup</code> elements respectively. Both elements can be provided
+      either a user (resp. group) id or a name. Prefixing the user or group id with
+      a <code>+</code> will force it to be considered like a numeric value. Without
+      this, it will be first tried as a user or group name.
+    </p>
 
     <pre>
 &lt;os&gt;
   &lt;initarg&gt;emergency.service&lt;/initarg&gt;
   &lt;initenv name='MYENV'&gt;some value&lt;/initenv&gt;
   &lt;initdir&gt;/my/custom/cwd&lt;/initdir&gt;
+  &lt;inituser&gt;tester&lt;/inituser&gt;
+  &lt;initgroup&gt;1000&lt;/initgroup&gt;
 &lt;/os&gt;
     </pre>
 
index 412dba0b35d3457307245e436e7cc75ee5dbcecd..77136108adb36c0ca2e9a9bed5cd1344c887e083 100644 (file)
             <ref name="absFilePath"/>
           </element>
         </optional>
+        <optional>
+          <element name="inituser">
+            <choice>
+              <ref name="unsignedInt"/>
+              <ref name="genericName"/>
+            </choice>
+          </element>
+          <element name="initgroup">
+            <choice>
+              <ref name="unsignedInt"/>
+              <ref name="genericName"/>
+            </choice>
+          </element>
+        </optional>
       </interleave>
     </element>
   </define>
index c264d00737a1718bfae884de875971685b891eea..59771e48ba1e7426dccb267a6e580d4ecb9cf024 100644 (file)
@@ -2877,6 +2877,8 @@ void virDomainDefFree(virDomainDefPtr def)
     for (i = 0; def->os.initenv && def->os.initenv[i]; i++)
         VIR_FREE(def->os.initenv[i]);
     VIR_FREE(def->os.initdir);
+    VIR_FREE(def->os.inituser);
+    VIR_FREE(def->os.initgroup);
     VIR_FREE(def->os.initenv);
     VIR_FREE(def->os.kernel);
     VIR_FREE(def->os.initrd);
@@ -17070,6 +17072,8 @@ virDomainDefParseBootOptions(virDomainDefPtr def,
         def->os.init = virXPathString("string(./os/init[1])", ctxt);
         def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
         def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt);
+        def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt);
+        def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt);
 
         if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0)
             goto error;
@@ -24958,6 +24962,11 @@ virDomainDefFormatInternal(virDomainDefPtr def,
     if (def->os.initdir)
         virBufferEscapeString(buf, "<initdir>%s</initdir>\n",
                               def->os.initdir);
+    if (def->os.inituser)
+        virBufferAsprintf(buf, "<inituser>%s</inituser>\n", def->os.inituser);
+    if (def->os.initgroup)
+        virBufferAsprintf(buf, "<initgroup>%s</initgroup>\n", def->os.initgroup);
+
     if (def->os.loader)
         virDomainLoaderDefFormat(buf, def->os.loader);
     virBufferEscapeString(buf, "<kernel>%s</kernel>\n",
index 0be5506694ebbbb0355b9d1f7bae3fd64738137a..a9b079cf290caf9e0ca4068be4f173bc91a72d39 100644 (file)
@@ -1870,6 +1870,8 @@ struct _virDomainOSDef {
     char **initargv;
     virDomainOSEnvPtr *initenv;
     char *initdir;
+    char *inituser;
+    char *initgroup;
     char *kernel;
     char *initrd;
     char *cmdline;
index 8d8e1a735cdb21a371841f8784a46f59cb266fd9..6309abe4b5bbe5b0bece7c294cdcf50be6eb556f 100644 (file)
@@ -2110,6 +2110,55 @@ static int lxcAttachNS(int *ns_fd)
     return 0;
 }
 
+/**
+ * lxcContainerSetUserGroup:
+ * @cmd: command to update
+ * @vmDef: domain definition for the container
+ * @ttyPath: guest path to the tty
+ *
+ * Set the command UID and GID. As this function attempts at
+ * converting the user/group name into uid/gid, it needs to
+ * be called after the pivot root is done.
+ *
+ * The owner of the tty is also changed to the given user.
+ */
+static int lxcContainerSetUserGroup(virCommandPtr cmd,
+                                    virDomainDefPtr vmDef,
+                                    const char *ttyPath)
+{
+    uid_t uid;
+    gid_t gid;
+
+    if (vmDef->os.inituser) {
+        if (virGetUserID(vmDef->os.inituser, &uid) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, _("User %s doesn't exist"),
+                           vmDef->os.inituser);
+            return -1;
+        }
+        virCommandSetUID(cmd, uid);
+
+        /* Change the newly created tty owner to the inituid for
+         * shells to have job control. */
+        if (chown(ttyPath, uid, -1) < 0) {
+            virReportSystemError(errno,
+                                 _("Failed to change ownership of tty %s"),
+                                 ttyPath);
+            return -1;
+        }
+    }
+
+    if (vmDef->os.initgroup) {
+        if (virGetGroupID(vmDef->os.initgroup, &gid) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, _("Group %s doesn't exist"),
+                           vmDef->os.initgroup);
+            return -1;
+        }
+        virCommandSetGID(cmd, gid);
+    }
+
+    return 0;
+}
+
 
 /**
  * lxcContainerChild:
@@ -2208,6 +2257,9 @@ static int lxcContainerChild(void *data)
         goto cleanup;
     }
 
+    if (lxcContainerSetUserGroup(cmd, vmDef, argv->ttyPaths[0]) < 0)
+        goto cleanup;
+
     /* rename and enable interfaces */
     if (lxcContainerRenameAndEnableInterfaces(vmDef,
                                               argv->nveths,
diff --git a/tests/lxcxml2xmldata/lxc-inituser.xml b/tests/lxcxml2xmldata/lxc-inituser.xml
new file mode 100644 (file)
index 0000000..08338a2
--- /dev/null
@@ -0,0 +1,31 @@
+<domain type='lxc'>
+  <name>jessie</name>
+  <uuid>e21987a5-e98e-9c99-0e35-803e4d9ad1fe</uuid>
+  <memory unit='KiB'>1048576</memory>
+  <currentMemory unit='KiB'>1048576</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch='x86_64'>exe</type>
+    <init>/sbin/sh</init>
+    <inituser>tester</inituser>
+    <initgroup>1234</initgroup>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>restart</on_crash>
+  <devices>
+    <emulator>/usr/libexec/libvirt_lxc</emulator>
+    <filesystem type='mount' accessmode='passthrough'>
+      <source dir='/mach/jessie'/>
+      <target dir='/'/>
+    </filesystem>
+    <console type='pty'>
+      <target type='lxc' port='0'/>
+    </console>
+  </devices>
+  <seclabel type='none'/>
+</domain>
index c81b0eace7e66d1bc000accbc37186646638814f..9b9314cf846e0c4a324d4049a98066662c2d92c5 100644 (file)
@@ -100,6 +100,7 @@ mymain(void)
                  VIR_DOMAIN_DEF_PARSE_SKIP_OSTYPE_CHECKS);
     DO_TEST("initenv");
     DO_TEST("initdir");
+    DO_TEST("inituser");
 
     virObjectUnref(caps);
     virObjectUnref(xmlopt);