[PATCH 1/2] unshare: allow persisting namespaces

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



For nsenter(1) we already support namespace specification by file
(e.g. bind mount to namespace /proc/[pid]/ns/[type] file). For
example:

  # nsenter --uts=/some/path

This patch extends unshare(1) to setup the bind mount for specified
namespace, for example

  # touch /some/path
  # unshare --uts=/some/path hostname FOO
  # nsenter --uts=/some/path hostname
  FOO

Note that the problem is mount namespace, because create bind mount
to ns/mount file within unshared namespace does not make sense.

Based on patch from Lubomir Rintel <lkundrak@xxxxx>.

Signed-off-by: Karel Zak <kzak@xxxxxxxxxx>
---
 sys-utils/unshare.1 | 70 +++++++++++++++++++++++++++++-----------
 sys-utils/unshare.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 132 insertions(+), 31 deletions(-)

diff --git a/sys-utils/unshare.1 b/sys-utils/unshare.1
index 6fc71f4..14755e7 100644
--- a/sys-utils/unshare.1
+++ b/sys-utils/unshare.1
@@ -8,8 +8,17 @@ unshare \- run program with some namespaces unshared from parent
 .RI [ arguments ]
 .SH DESCRIPTION
 Unshares the indicated namespaces from the parent process and then executes
-the specified \fIprogram\fR.  The namespaces to be unshared are indicated via
-options.  Unshareable namespaces are:
+the specified \fIprogram\fR.
+.PP
+The namespaces can optionally be persisted by bind mounting /proc/[pid]/ns/[type] files
+to a filesystem path and entered with
+.BR nsenter (1)
+even after \fIprogram\fR terminates.
+Once a persistent namespace is no longer needed it can be unpersisted with
+.BR umount (8).
+See EXAMPLES section for more details.
+.PP
+The namespaces to be unshared are indicated via options.  Unshareable namespaces are:
 .TP
 .BR "mount namespace"
 Mounting and unmounting filesystems will not affect the rest of the system
@@ -47,24 +56,29 @@ The process will have a distinct set of UIDs, GIDs and capabilities.
 See \fBclone\fR(2) for the exact semantics of the flags.
 .SH OPTIONS
 .TP
-.BR \-i , " \-\-ipc"
-Unshare the IPC namespace.
+.BR \-i , " \-\-ipc"[=\fIfile\fP]
+Unshare the IPC namespace. If \fIfile\fP is specified then persistent namespace is created
+by bind mount.
 .TP
-.BR \-m , " \-\-mount"
-Unshare the mount namespace.
+.BR \-m , " \-\-mount"[=\fIfile\fP]
+Unshare the mount namespace. If \fIfile\fP is specified then persistent namespace is created
+by bind mount.
 .TP
-.BR \-n , " \-\-net"
-Unshare the network namespace.
+.BR \-n , " \-\-net"[=\fIfile\fP]
+Unshare the network namespace. If \fIfile\fP is specified then persistent namespace is created
+by bind mount.
 .TP
-.BR \-p , " \-\-pid"
-Unshare the pid namespace.
-See also the \fB--fork\fP and \fB--mount-proc\fP options.
+.BR \-p , " \-\-pid"[=\fIfile\fP]
+Unshare the pid namespace. If \fIfile\fP is specified then persistent namespace is created
+by bind mount. See also the \fB--fork\fP and \fB--mount-proc\fP options.
 .TP
-.BR \-u , " \-\-uts"
-Unshare the UTS namespace.
+.BR \-u , " \-\-uts"[=\fIfile\fP]
+Unshare the UTS namespace. If \fIfile\fP is specified then persistent namespace is created
+by bind mount.
 .TP
-.BR \-U , " \-\-user"
-Unshare the user namespace.
+.BR \-U , " \-\-user"[=\fIfile\fP]
+Unshare the user namespace. If \fIfile\fP is specified then persistent namespace is created
+by bind mount.
 .TP
 .BR \-f , " \-\-fork"
 Fork the specified \fIprogram\fR as a child process of \fBunshare\fR rather than
@@ -125,14 +139,32 @@ procfs instance.
 root
 .br
 Establish a user namespace as an unprivileged user with a root user within it.
+.TP
+.TQ
+.B # touch /root/uts-ns
+.TQ
+.B # unshare --uts=/root/uts-ns hostanme FOO
+.TQ
+.B # nsenter --uts=/root/uts-ns hostname
+.TQ
+FOO
+.TQ
+.B # umount /root/uts-ns
+.br
+Establish a persistent UTS namespace, modify hostname. The namespace maybe later entered
+by nsenter. The namespace is destroyed by umount the bind reference.
 .SH SEE ALSO
 .BR unshare (2),
 .BR clone (2),
 .BR mount (8)
-.SH BUGS
-None known so far.
-.SH AUTHOR
-Mikhail Gusarov <dottedmag@xxxxxxxxxxxxx>
+.SH AUTHORS
+.UR dottedmag@xxxxxxxxxxxxx
+Mikhail Gusarov
+.UE
+.br
+.UR kzak@xxxxxxxxxx
+Karel Zak
+.UE
 .SH AVAILABILITY
 The unshare command is part of the util-linux package and is available from
 ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 18a7c7b..65d3e61 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -42,6 +42,24 @@
 /* 'private' is kernel default */
 #define UNSHARE_PROPAGATION_DEFAULT	(MS_REC | MS_PRIVATE)
 
+/* /proc namespace files and mountpoints for binds */
+static struct namespace_file {
+	int		type;		/* CLONE_NEW* */
+	const char	*name;		/* ns/<type> */
+	const char	*target;	/* user specified target for bind mount */
+} namespace_files[] = {
+	{ .type = CLONE_NEWUSER, .name = "ns/user" },
+	{ .type = CLONE_NEWIPC,  .name = "ns/ipc"  },
+	{ .type = CLONE_NEWUTS,  .name = "ns/uts"  },
+	{ .type = CLONE_NEWNET,  .name = "ns/net"  },
+	{ .type = CLONE_NEWPID,  .name = "ns/pid"  },
+	{ .type = CLONE_NEWNS,   .name = "ns/mnt"  },
+	{ .name = NULL }
+};
+
+static int npersists;	/* number of persistent namespaces */
+
+
 enum {
 	SETGROUPS_NONE = -1,
 	SETGROUPS_DENY = 0,
@@ -133,6 +151,40 @@ static void set_propagation(unsigned long flags)
 		err(EXIT_FAILURE, _("cannot change root filesystem propagation"));
 }
 
+
+static int set_ns_target(int type, const char *path)
+{
+	struct namespace_file *ns;
+
+	for (ns = namespace_files; ns->name; ns++) {
+		if (ns->type != type)
+			continue;
+		ns->target = path;
+		npersists++;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int bind_ns_files(pid_t pid)
+{
+	struct namespace_file *ns;
+	char src[PATH_MAX];
+
+	for (ns = namespace_files; ns->name; ns++) {
+		if (!ns->target)
+			continue;
+
+		snprintf(src, sizeof(src), "/proc/%u/%s", (unsigned) pid, ns->name);
+
+		if (mount(src, ns->target, NULL, MS_BIND, NULL) != 0)
+			err(EXIT_FAILURE, _("mount %s on %s failed"), src, ns->target);
+	}
+
+	return 0;
+}
+
 static void usage(int status)
 {
 	FILE *out = status == EXIT_SUCCESS ? stdout : stderr;
@@ -145,12 +197,12 @@ static void usage(int status)
 	fputs(_("Run a program with some namespaces unshared from the parent.\n"), out);
 
 	fputs(USAGE_OPTIONS, out);
-	fputs(_(" -m, --mount               unshare mounts namespace\n"), out);
-	fputs(_(" -u, --uts                 unshare UTS namespace (hostname etc)\n"), out);
-	fputs(_(" -i, --ipc                 unshare System V IPC namespace\n"), out);
-	fputs(_(" -n, --net                 unshare network namespace\n"), out);
-	fputs(_(" -p, --pid                 unshare pid namespace\n"), out);
-	fputs(_(" -U, --user                unshare user namespace\n"), out);
+	fputs(_(" -m, --mount[=<file>]      unshare mounts namespace\n"), out);
+	fputs(_(" -u, --uts[=<file>]        unshare UTS namespace (hostname etc)\n"), out);
+	fputs(_(" -i, --ipc[=<file>]        unshare System V IPC namespace\n"), out);
+	fputs(_(" -n, --net[=<file>]        unshare network namespace\n"), out);
+	fputs(_(" -p, --pid[=<file>]        unshare pid namespace\n"), out);
+	fputs(_(" -U, --user[=<file>]       unshare user namespace\n"), out);
 	fputs(_(" -f, --fork                fork before launching <program>\n"), out);
 	fputs(_("     --mount-proc[=<dir>]  mount proc filesystem first (implies --mount)\n"), out);
 	fputs(_(" -r, --map-root-user       map current user to root (implies --user)\n"), out);
@@ -176,12 +228,14 @@ int main(int argc, char *argv[])
 	static const struct option longopts[] = {
 		{ "help", no_argument, 0, 'h' },
 		{ "version", no_argument, 0, 'V'},
-		{ "mount", no_argument, 0, 'm' },
-		{ "uts", no_argument, 0, 'u' },
-		{ "ipc", no_argument, 0, 'i' },
-		{ "net", no_argument, 0, 'n' },
-		{ "pid", no_argument, 0, 'p' },
-		{ "user", no_argument, 0, 'U' },
+
+		{ "mount", optional_argument, 0, 'm' },
+		{ "uts",   optional_argument, 0, 'u' },
+		{ "ipc",   optional_argument, 0, 'i' },
+		{ "net",   optional_argument, 0, 'n' },
+		{ "pid",   optional_argument, 0, 'p' },
+		{ "user",  optional_argument, 0, 'U' },
+
 		{ "fork", no_argument, 0, 'f' },
 		{ "mount-proc", optional_argument, 0, OPT_MOUNTPROC },
 		{ "map-root-user", no_argument, 0, 'r' },
@@ -215,21 +269,33 @@ int main(int argc, char *argv[])
 			return EXIT_SUCCESS;
 		case 'm':
 			unshare_flags |= CLONE_NEWNS;
+			if (optarg)
+				set_ns_target(CLONE_NEWNS, optarg);
 			break;
 		case 'u':
 			unshare_flags |= CLONE_NEWUTS;
+			if (optarg)
+				set_ns_target(CLONE_NEWUTS, optarg);
 			break;
 		case 'i':
 			unshare_flags |= CLONE_NEWIPC;
+			if (optarg)
+				set_ns_target(CLONE_NEWIPC, optarg);
 			break;
 		case 'n':
 			unshare_flags |= CLONE_NEWNET;
+			if (optarg)
+				set_ns_target(CLONE_NEWNET, optarg);
 			break;
 		case 'p':
 			unshare_flags |= CLONE_NEWPID;
+			if (optarg)
+				set_ns_target(CLONE_NEWPID, optarg);
 			break;
 		case 'U':
 			unshare_flags |= CLONE_NEWUSER;
+			if (optarg)
+				set_ns_target(CLONE_NEWUSER, optarg);
 			break;
 		case OPT_MOUNTPROC:
 			unshare_flags |= CLONE_NEWNS;
@@ -273,6 +339,9 @@ int main(int argc, char *argv[])
 		}
 	}
 
+	if (npersists)
+		bind_ns_files(getpid());
+
 	if (maproot) {
 		if (setgrpcmd == SETGROUPS_ALLOW)
 			errx(EXIT_FAILURE, _("options --setgroups=allow and "
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux