lxc: add possibility to define init uid/gid

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>
This commit is contained in:
Cédric Bosdonnat 2017-06-06 10:54:16 +02:00
parent 552f7c139a
commit 426929aea9
7 changed files with 118 additions and 0 deletions

View File

@ -334,6 +334,13 @@
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;
@ -343,6 +350,8 @@
&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>

View File

@ -400,6 +400,20 @@
<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>

View 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",

View File

@ -1870,6 +1870,8 @@ struct _virDomainOSDef {
char **initargv;
virDomainOSEnvPtr *initenv;
char *initdir;
char *inituser;
char *initgroup;
char *kernel;
char *initrd;
char *cmdline;

View 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,

View File

@ -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>

View 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);