Skip to content

Commit

Permalink
Teach lxc_unshare about interfaces, mounts, hostname, daemonize
Browse files Browse the repository at this point in the history
lxc_unshare now takes one or more '-i interfacename' arguments which
will move the named interfaces into the created container.

lxc_unshare now takes -M argument which will cause the standard mounts
(/proc /dev/shm /dev/mqueue) to be auto-mounted inside container.

lxc_unshare now takes '-H hostname' argument to automatically set
the hostname in the container.

lxc_unshare now takes -D argument to automatically daemonize and detach
from the created container, instead of waiting for the container to exit

Signed-off-by: Seth Robertson <srobertson@appcomsci.com>
Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
  • Loading branch information
Seth Robertson authored and hallyn committed Jan 15, 2014
1 parent 13d8bde commit c1bb25a
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 4 deletions.
65 changes: 65 additions & 0 deletions doc/lxc-unshare.sgml.in
Expand Up @@ -52,6 +52,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<command>lxc-unshare</command>
<arg choice="req">-s <replaceable>namespaces</replaceable></arg>
<arg choice="opt">-u <replaceable>user</replaceable></arg>
<arg choice="opt">-H <replaceable>hostname</replaceable></arg>
<arg choice="opt">-i <replaceable>ifname</replaceable></arg>
<arg choice="opt">-d</arg>
<arg choice="opt">-M</arg>
<arg choice="req">command</arg>
</cmdsynopsis>
</refsynopsisdiv>
Expand Down Expand Up @@ -105,6 +109,55 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
</listitem>
</varlistentry>

<varlistentry>
<term>
<option>-H <replaceable>hostname</replaceable></option>
</term>
<listitem>
<para>
Set the hostname in the new container. Only allowed if
the UTSNAME namespace is set.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term>
<option>-i <replaceable>interfacename</replaceable></option>
</term>
<listitem>
<para>
Move the named interface into the container. Only allowed
if the NETWORK namespace is set. You may specify this
argument multiple times to move multiple interfaces into
container.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term>
<option>-d</option>
</term>
<listitem>
<para>
Daemonize (do not wait for the container to exit before exiting)
</para>
</listitem>
</varlistentry>

<varlistentry>
<term>
<option>-M</option>
</term>
<listitem>
<para>
Mount default filesystems (/proc /dev/shm and /dev/mqueue)
in the container. Only allowed if MOUNT namespace is set.
</para>
</listitem>
</varlistentry>

</variablelist>

</refsect1>
Expand All @@ -131,6 +184,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
</programlisting>
ps output will show there are no other processes in the namespace.
</para>
<para>
To spawn a shell in a new network, pid, mount, and hostname
namespace.
<programlisting>
lxc-unshare -s "NETWORK|PID|MOUNT|UTSNAME" -M -H slave -i veth1 /bin/bash
</programlisting>

The resulting shell will have pid 1 and will see two network
interfaces (lo and veth1). The hostname will be "slave" and
/proc will have been remounted. ps output will show there are
no other processes in the namespace.
</para>
</refsect1>

&seealso;
Expand Down
79 changes: 75 additions & 4 deletions src/lxc/lxc_unshare.c
Expand Up @@ -33,22 +33,36 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "caps.h"
#include "log.h"
#include "namespace.h"
#include "network.h"
#include "utils.h"
#include "cgroup.h"
#include "error.h"

lxc_log_define(lxc_unshare_ui, lxc);

struct my_iflist
{
char *mi_ifname;
struct my_iflist *mi_next;
};

static void usage(char *cmd)
{
fprintf(stderr, "%s <options> command [command_arguments]\n", basename(cmd));
fprintf(stderr, "Options are:\n");
fprintf(stderr, "\t -s flags: ORed list of flags to unshare:\n" \
fprintf(stderr, "\t -s flags : ORed list of flags to unshare:\n" \
"\t MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n");
fprintf(stderr, "\t -u <id> : new id to be set if -s USER is specified\n");
fprintf(stderr, "\t -u <id> : new id to be set if -s USER is specified\n");
fprintf(stderr, "\t -i <iface> : Interface name to be moved into container (presumably with NETWORK unsharing set)\n");
fprintf(stderr, "\t -H <hostname>: Set the hostname in the container\n");
fprintf(stderr, "\t -d : Daemonize (do not wait for container to exit)\n");
fprintf(stderr, "\t -M : reMount default fs inside container (/proc /dev/shm /dev/mqueue)\n");
_exit(1);
}

Expand Down Expand Up @@ -88,6 +102,8 @@ struct start_arg {
char ***args;
int *flags;
uid_t *uid;
int want_default_mounts;
const char *want_hostname;
};

static int do_start(void *arg)
Expand All @@ -96,6 +112,17 @@ static int do_start(void *arg)
char **args = *start_arg->args;
int flags = *start_arg->flags;
uid_t uid = *start_arg->uid;
int want_default_mounts = start_arg->want_default_mounts;
const char *want_hostname = start_arg->want_hostname;

if ((flags & CLONE_NEWNS) && want_default_mounts)
lxc_setup_fs();

if ((flags & CLONE_NEWUTS) && want_hostname)
if (sethostname(want_hostname, strlen(want_hostname)) < 0) {
ERROR("failed to set hostname %s: %s", want_hostname, strerror(errno));
exit(1);
}

// Setuid is useful even without a new user id space
if ( uid >= 0 && setuid(uid)) {
Expand All @@ -116,22 +143,44 @@ int main(int argc, char *argv[])
char *namespaces = NULL;
char **args;
int flags = 0;
int daemonize = 0;
uid_t uid = -1; /* valid only if (flags & CLONE_NEWUSER) */
pid_t pid;

struct my_iflist *tmpif, *my_iflist = NULL;
struct start_arg start_arg = {
.args = &args,
.uid = &uid,
.flags = &flags,
.want_hostname = NULL,
.want_default_mounts = 0,
};

while ((opt = getopt(argc, argv, "s:u:h")) != -1) {
while ((opt = getopt(argc, argv, "s:u:hH:i:dM")) != -1) {
switch (opt) {
case 's':
namespaces = optarg;
break;
case 'i':
if (!(tmpif = malloc(sizeof(*tmpif)))) {
perror("malloc");
exit(1);
}
tmpif->mi_ifname = optarg;
tmpif->mi_next = my_iflist;
my_iflist = tmpif;
break;
case 'd':
daemonize = 1;
break;
case 'M':
start_arg.want_default_mounts = 1;
break;
case 'H':
start_arg.want_hostname = optarg;
break;
case 'h':
usage(argv[0]);
break;
case 'u':
uid = lookup_user(optarg);
if (uid == -1)
Expand All @@ -154,6 +203,18 @@ int main(int argc, char *argv[])
if (ret)
usage(argv[0]);

if (!(flags & CLONE_NEWNET) && my_iflist) {
ERROR("-i <interfacename> needs -s NETWORK option");
return 1;
}

if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) {
ERROR("-H <hostname> needs -s UTSNAME option");
return 1;
}

if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) {
ERROR("-M needs -s MOUNT option");
return 1;
}

Expand All @@ -163,6 +224,16 @@ int main(int argc, char *argv[])
return -1;
}

if (my_iflist) {
for (tmpif = my_iflist; tmpif; tmpif = tmpif->mi_next) {
if (lxc_netdev_move_by_name(tmpif->mi_ifname, pid) < 0)
fprintf(stderr,"Could not move interface %s into container %d: %s\n", tmpif->mi_ifname, pid, strerror(errno));
}
}

if (daemonize)
exit(0);

if (waitpid(pid, &status, 0) < 0) {
ERROR("failed to wait for '%d'", pid);
return -1;
Expand Down

0 comments on commit c1bb25a

Please sign in to comment.