You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
757 lines
27 KiB
757 lines
27 KiB
diff -up util-linux-2.23.2/include/pathnames.h.kzak util-linux-2.23.2/include/pathnames.h |
|
--- util-linux-2.23.2/include/pathnames.h.kzak 2015-06-26 10:00:19.111877564 +0200 |
|
+++ util-linux-2.23.2/include/pathnames.h 2015-06-26 10:00:51.623630869 +0200 |
|
@@ -85,6 +85,10 @@ |
|
#define _PATH_PROC_LOCKS "/proc/locks" |
|
#define _PATH_PROC_CDROMINFO "/proc/sys/dev/cdrom/info" |
|
|
|
+#define _PATH_PROC_UIDMAP "/proc/self/uid_map" |
|
+#define _PATH_PROC_GIDMAP "/proc/self/gid_map" |
|
+#define _PATH_PROC_SETGROUPS "/proc/self/setgroups" |
|
+ |
|
#define _PATH_PROC_ATTR_CURRENT "/proc/self/attr/current" |
|
#define _PATH_PROC_ATTR_EXEC "/proc/self/attr/exec" |
|
#define _PATH_PROC_CAPLASTCAP "/proc/sys/kernel/cap_last_cap" |
|
diff -up util-linux-2.23.2/sys-utils/Makemodule.am.kzak util-linux-2.23.2/sys-utils/Makemodule.am |
|
diff -up util-linux-2.23.2/sys-utils/nsenter.1.kzak util-linux-2.23.2/sys-utils/nsenter.1 |
|
--- util-linux-2.23.2/sys-utils/nsenter.1.kzak 2015-06-26 09:58:39.468633643 +0200 |
|
+++ util-linux-2.23.2/sys-utils/nsenter.1 2015-06-26 09:58:51.672541041 +0200 |
|
@@ -1,44 +1,45 @@ |
|
-.TH NSENTER 1 "January 2013" "util-linux" "User Commands" |
|
+.TH NSENTER 1 "June 2013" "util-linux" "User Commands" |
|
.SH NAME |
|
nsenter \- run program with namespaces of other processes |
|
.SH SYNOPSIS |
|
.B nsenter |
|
-.RI [ options ] |
|
-.RI [ program ] |
|
-.RI [ arguments ] |
|
+[options] |
|
+.RI [ program |
|
+.RI [ arguments ]] |
|
.SH DESCRIPTION |
|
Enters the namespaces of one or more other processes and then executes the specified |
|
program. Enterable namespaces are: |
|
.TP |
|
.B mount namespace |
|
-mounting and unmounting filesystems will not affect rest of the system |
|
+Mounting and unmounting filesystems will not affect the rest of the system |
|
.RB ( CLONE_\:NEWNS |
|
-flag), except for filesystems which are explicitly marked as shared (by mount |
|
---make-\:shared). See /proc\:/self\:/mountinfo for the shared flag. |
|
+flag), except for filesystems which are explicitly marked as shared (with |
|
+\fBmount --make-\:shared\fP; see \fI/proc\:/self\:/mountinfo\fP for the |
|
+\fBshared\fP flag). |
|
.TP |
|
.B UTS namespace |
|
-setting hostname, domainname will not affect rest of the system |
|
+Setting hostname or domainname will not affect the rest of the system. |
|
.RB ( CLONE_\:NEWUTS |
|
-flag). |
|
+flag) |
|
.TP |
|
.B IPC namespace |
|
-process will have independent namespace for System V message queues, semaphore |
|
-sets and shared memory segments |
|
+The process will have an independent namespace for System V message queues, |
|
+semaphore sets and shared memory segments. |
|
.RB ( CLONE_\:NEWIPC |
|
-flag). |
|
+flag) |
|
.TP |
|
.B network namespace |
|
-process will have independent IPv4 and IPv6 stacks, IP routing tables, firewall |
|
-rules, the |
|
+The process will have independent IPv4 and IPv6 stacks, IP routing tables, |
|
+firewall rules, the |
|
.I /proc\:/net |
|
and |
|
.I /sys\:/class\:/net |
|
-directory trees, sockets etc. |
|
+directory trees, sockets, etc. |
|
.RB ( CLONE_\:NEWNET |
|
-flag). |
|
+flag) |
|
.TP |
|
.B PID namespace |
|
-children will have a set of PID to process mappings separate from the |
|
+Children will have a set of PID to process mappings separate from the |
|
.B nsenter |
|
process |
|
.RB ( CLONE_\:NEWPID |
|
@@ -46,18 +47,18 @@ flag). |
|
.B nsenter |
|
will fork by default if changing the PID namespace, so that the new program |
|
and its children share the same PID namespace and are visible to each other. |
|
-If \-\-no\-fork is used, the new program will be exec'ed without forking. |
|
-.PP |
|
-See the |
|
-.BR clone (2) |
|
-for exact semantics of the flags. |
|
+If \fB\-\-no\-fork\fP is used, the new program will be exec'ed without forking. |
|
.TP |
|
-If program is not given, run ``${SHELL}'' (default: /bin\:/sh). |
|
+.B user namespace |
|
+The process will have a distinct set of UIDs, GIDs and capabilities. |
|
+.RB ( CLONE_\:NEWUSER |
|
+flag) |
|
+.TP |
|
+See \fBclone\fP(2) for the exact semantics of the flags. |
|
+.TP |
|
+If \fIprogram\fP is not given, then ``${SHELL}'' is run (default: /bin\:/sh). |
|
|
|
.SH OPTIONS |
|
-Argument with square brakets, such as [\fIfile\fR], means optional argument. |
|
-Command line syntax to specify optional argument \-\-mount=/path\:/to\:/file. |
|
-Please notice the equals sign. |
|
.TP |
|
\fB\-t\fR, \fB\-\-target\fR \fIpid\fP |
|
Specify a target process to get contexts from. The paths to the contexts |
|
@@ -83,6 +84,9 @@ the network namespace |
|
/proc/\fIpid\fR/ns/pid |
|
the PID namespace |
|
.TP |
|
+/proc/\fIpid\fR/ns/user |
|
+the user namespace |
|
+.TP |
|
/proc/\fIpid\fR/root |
|
the root directory |
|
.TP |
|
@@ -91,51 +95,71 @@ the working directory respectively |
|
.PD |
|
.RE |
|
.TP |
|
-\fB\-m\fR, \fB\-\-mount\fR [\fIfile\fR] |
|
-Enter the mount namespace. If no file is specified enter the mount namespace |
|
-of the target process. If file is specified enter the mount namespace |
|
+\fB\-m\fR, \fB\-\-mount\fR[=\fIfile\fR] |
|
+Enter the mount namespace. If no file is specified, enter the mount namespace |
|
+of the target process. If file is specified, enter the mount namespace |
|
specified by file. |
|
.TP |
|
-\fB\-u\fR, \fB\-\-uts\fR [\fIfile\fR] |
|
-Enter the UTS namespace. If no file is specified enter the UTS namespace of |
|
-the target process. If file is specified enter the UTS namespace specified by |
|
+\fB\-u\fR, \fB\-\-uts\fR[=\fIfile\fR] |
|
+Enter the UTS namespace. If no file is specified, enter the UTS namespace of |
|
+the target process. If file is specified, enter the UTS namespace specified by |
|
file. |
|
.TP |
|
-\fB\-i\fR, \fB\-\-ipc\fR [\fIfile\fR] |
|
-Enter the IPC namespace. If no file is specified enter the IPC namespace of |
|
-the target process. If file is specified enter the IPC namespace specified by |
|
+\fB\-i\fR, \fB\-\-ipc\fR[=\fIfile\fR] |
|
+Enter the IPC namespace. If no file is specified, enter the IPC namespace of |
|
+the target process. If file is specified, enter the IPC namespace specified by |
|
file. |
|
.TP |
|
-\fB\-n\fR, \fB\-\-net\fR [\fIfile\fR] |
|
-Enter the network namespace. If no file is specified enter the network |
|
-namespace of the target process. If file is specified enter the network |
|
+\fB\-n\fR, \fB\-\-net\fR[=\fIfile\fR] |
|
+Enter the network namespace. If no file is specified, enter the network |
|
+namespace of the target process. If file is specified, enter the network |
|
namespace specified by file. |
|
.TP |
|
-\fB\-p\fR, \fB\-\-pid\fR [\fIfile\fR] |
|
-Enter the PID namespace. If no file is specified enter the PID namespace of |
|
-the target process. If file is specified enter the PID namespace specified by |
|
+\fB\-p\fR, \fB\-\-pid\fR[=\fIfile\fR] |
|
+Enter the PID namespace. If no file is specified, enter the PID namespace of |
|
+the target process. If file is specified, enter the PID namespace specified by |
|
file. |
|
.TP |
|
-\fB\-r\fR, \fB\-\-root\fR [\fIdirectory\fR] |
|
-Set the root directory. If no directory is specified set the root directory to |
|
-the root directory of the target process. If directory is specified set the |
|
+\fB\-U\fR, \fB\-\-user\fR[=\fIfile\fR] |
|
+Enter the user namespace. If no file is specified, enter the user namespace of |
|
+the target process. If file is specified, enter the user namespace specified by |
|
+file. See also the \fB\-\-setuid\fR and \fB\-\-setgid\fR options. |
|
+.TP |
|
+\fB\-G\fR, \fB\-\-setgid\fR \fIgid\fR |
|
+Set the group ID which will be used in the entered namespace and drop |
|
+supplementary groups. |
|
+.BR nsenter (1) |
|
+always sets GID for user namespaces, the default is 0. |
|
+.TP |
|
+\fB\-S\fR, \fB\-\-setuid\fR \fIuid\fR |
|
+Set the user ID which will be used in the entered namespace. |
|
+.BR nsenter (1) |
|
+always sets UID for user namespaces, the default is 0. |
|
+.TP |
|
+\fB\-\-preserve\-credentials\fR |
|
+Don't modify UID and GID when enter user namespace. The default is to |
|
+drops supplementary groups and sets GID and UID to 0. |
|
+.TP |
|
+\fB\-r\fR, \fB\-\-root\fR[=\fIdirectory\fR] |
|
+Set the root directory. If no directory is specified, set the root directory to |
|
+the root directory of the target process. If directory is specified, set the |
|
root directory to the specified directory. |
|
.TP |
|
-\fB\-w\fR, \fB\-\-wd\fR [\fIdirectory\fR] |
|
-Set the working directory. If no directory is specified set the working |
|
+\fB\-w\fR, \fB\-\-wd\fR[=\fIdirectory\fR] |
|
+Set the working directory. If no directory is specified, set the working |
|
directory to the working directory of the target process. If directory is |
|
-specified set the working directory to the specified directory. |
|
+specified, set the working directory to the specified directory. |
|
.TP |
|
-\fB\-F\fR, \fB\-\-no-fork\fR |
|
-Do not fork before exec'ing the specified program. By default when entering a |
|
-pid namespace enter calls fork before calling exec so that the children will be |
|
-in the newly entered pid namespace. |
|
+\fB\-F\fR, \fB\-\-no\-fork\fR |
|
+Do not fork before exec'ing the specified program. By default, when entering a |
|
+PID namespace, \fBnsenter\fP calls \fBfork\fP before calling \fBexec\fP so that |
|
+any children will also be in the newly entered PID namespace. |
|
.TP |
|
\fB\-V\fR, \fB\-\-version\fR |
|
Display version information and exit. |
|
.TP |
|
\fB\-h\fR, \fB\-\-help\fR |
|
-Print a help message. |
|
+Display help text and exit. |
|
.SH SEE ALSO |
|
.BR setns (2), |
|
.BR clone (2) |
|
diff -up util-linux-2.23.2/sys-utils/nsenter.c.kzak util-linux-2.23.2/sys-utils/nsenter.c |
|
--- util-linux-2.23.2/sys-utils/nsenter.c.kzak 2015-06-26 09:58:39.468633643 +0200 |
|
+++ util-linux-2.23.2/sys-utils/nsenter.c 2015-06-26 09:58:51.673541033 +0200 |
|
@@ -28,6 +28,7 @@ |
|
#include <assert.h> |
|
#include <sys/types.h> |
|
#include <sys/wait.h> |
|
+#include <grp.h> |
|
|
|
#include "strutils.h" |
|
#include "nls.h" |
|
@@ -42,7 +43,12 @@ static struct namespace_file { |
|
int fd; |
|
} namespace_files[] = { |
|
/* Careful the order is significant in this array. |
|
+ * |
|
+ * The user namespace comes first, so that it is entered |
|
+ * first. This gives an unprivileged user the potential to |
|
+ * enter the other namespaces. |
|
*/ |
|
+ { .nstype = CLONE_NEWUSER, .name = "ns/user", .fd = -1 }, |
|
{ .nstype = CLONE_NEWIPC, .name = "ns/ipc", .fd = -1 }, |
|
{ .nstype = CLONE_NEWUTS, .name = "ns/uts", .fd = -1 }, |
|
{ .nstype = CLONE_NEWNET, .name = "ns/net", .fd = -1 }, |
|
@@ -56,18 +62,25 @@ static void usage(int status) |
|
FILE *out = status == EXIT_SUCCESS ? stdout : stderr; |
|
|
|
fputs(USAGE_HEADER, out); |
|
- fprintf(out, _(" %s [options] <program> [args...]\n"), |
|
+ fprintf(out, _(" %s [options] <program> [<argument>...]\n"), |
|
program_invocation_short_name); |
|
|
|
+ fputs(USAGE_SEPARATOR, out); |
|
+ fputs(_("Run a program with namespaces of other processes.\n"), out); |
|
+ |
|
fputs(USAGE_OPTIONS, out); |
|
fputs(_(" -t, --target <pid> target process to get namespaces from\n"), out); |
|
- fputs(_(" -m, --mount [=<file>] enter mount namespace\n"), out); |
|
- fputs(_(" -u, --uts [=<file>] enter UTS namespace (hostname etc)\n"), out); |
|
- fputs(_(" -i, --ipc [=<file>] enter System V IPC namespace\n"), out); |
|
- fputs(_(" -n, --net [=<file>] enter network namespace\n"), out); |
|
- fputs(_(" -p, --pid [=<file>] enter pid namespace\n"), out); |
|
- fputs(_(" -r, --root [=<dir>] set the root directory\n"), out); |
|
- fputs(_(" -w, --wd [=<dir>] set the working directory\n"), out); |
|
+ fputs(_(" -m, --mount[=<file>] enter mount namespace\n"), out); |
|
+ fputs(_(" -u, --uts[=<file>] enter UTS namespace (hostname etc)\n"), out); |
|
+ fputs(_(" -i, --ipc[=<file>] enter System V IPC namespace\n"), out); |
|
+ fputs(_(" -n, --net[=<file>] enter network namespace\n"), out); |
|
+ fputs(_(" -p, --pid[=<file>] enter pid namespace\n"), out); |
|
+ fputs(_(" -U, --user[=<file>] enter user namespace\n"), out); |
|
+ fputs(_(" -S, --setuid <uid> set uid in entered namespace\n"), out); |
|
+ fputs(_(" -G, --setgid <gid> set gid in entered namespace\n"), out); |
|
+ fputs(_(" --preserve-credentials do not touch uids or gids\n"), out); |
|
+ fputs(_(" -r, --root[=<dir>] set the root directory\n"), out); |
|
+ fputs(_(" -w, --wd[=<dir>] set the working directory\n"), out); |
|
fputs(_(" -F, --no-fork do not fork before exec'ing <program>\n"), out); |
|
|
|
fputs(USAGE_SEPARATOR, out); |
|
@@ -153,6 +166,9 @@ static void continue_as_child(void) |
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
+ enum { |
|
+ OPT_PRESERVE_CRED = CHAR_MAX + 1 |
|
+ }; |
|
static const struct option longopts[] = { |
|
{ "help", no_argument, NULL, 'h' }, |
|
{ "version", no_argument, NULL, 'V'}, |
|
@@ -162,24 +178,30 @@ int main(int argc, char *argv[]) |
|
{ "ipc", optional_argument, NULL, 'i' }, |
|
{ "net", optional_argument, NULL, 'n' }, |
|
{ "pid", optional_argument, NULL, 'p' }, |
|
+ { "user", optional_argument, NULL, 'U' }, |
|
+ { "setuid", required_argument, NULL, 'S' }, |
|
+ { "setgid", required_argument, NULL, 'G' }, |
|
{ "root", optional_argument, NULL, 'r' }, |
|
{ "wd", optional_argument, NULL, 'w' }, |
|
{ "no-fork", no_argument, NULL, 'F' }, |
|
+ { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED }, |
|
{ NULL, 0, NULL, 0 } |
|
}; |
|
|
|
struct namespace_file *nsfile; |
|
- int c, namespaces = 0; |
|
- bool do_rd = false, do_wd = false; |
|
+ int c, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0; |
|
+ bool do_rd = false, do_wd = false, force_uid = false, force_gid = false; |
|
int do_fork = -1; /* unknown yet */ |
|
+ uid_t uid = 0; |
|
+ gid_t gid = 0; |
|
|
|
- setlocale(LC_MESSAGES, ""); |
|
+ setlocale(LC_ALL, ""); |
|
bindtextdomain(PACKAGE, LOCALEDIR); |
|
textdomain(PACKAGE); |
|
atexit(close_stdout); |
|
|
|
while ((c = |
|
- getopt_long(argc, argv, "hVt:m::u::i::n::p::r::w::F", |
|
+ getopt_long(argc, argv, "+hVt:m::u::i::n::p::U::S:G:r::w::F", |
|
longopts, NULL)) != -1) { |
|
switch (c) { |
|
case 'h': |
|
@@ -221,6 +243,20 @@ int main(int argc, char *argv[]) |
|
else |
|
namespaces |= CLONE_NEWPID; |
|
break; |
|
+ case 'U': |
|
+ if (optarg) |
|
+ open_namespace_fd(CLONE_NEWUSER, optarg); |
|
+ else |
|
+ namespaces |= CLONE_NEWUSER; |
|
+ break; |
|
+ case 'S': |
|
+ uid = strtoul_or_err(optarg, _("failed to parse uid")); |
|
+ force_uid = true; |
|
+ break; |
|
+ case 'G': |
|
+ gid = strtoul_or_err(optarg, _("failed to parse gid")); |
|
+ force_gid = true; |
|
+ break; |
|
case 'F': |
|
do_fork = 0; |
|
break; |
|
@@ -236,6 +272,9 @@ int main(int argc, char *argv[]) |
|
else |
|
do_wd = true; |
|
break; |
|
+ case OPT_PRESERVE_CRED: |
|
+ preserve_cred = 1; |
|
+ break; |
|
default: |
|
usage(EXIT_FAILURE); |
|
} |
|
@@ -253,6 +292,26 @@ int main(int argc, char *argv[]) |
|
open_target_fd(&wd_fd, "cwd", NULL); |
|
|
|
/* |
|
+ * Update namespaces variable to contain all requested namespaces |
|
+ */ |
|
+ for (nsfile = namespace_files; nsfile->nstype; nsfile++) { |
|
+ if (nsfile->fd < 0) |
|
+ continue; |
|
+ namespaces |= nsfile->nstype; |
|
+ } |
|
+ |
|
+ /* for user namespaces we always set UID and GID (default is 0) |
|
+ * and clear root's groups if --preserve-credentials is no specified */ |
|
+ if ((namespaces & CLONE_NEWUSER) && !preserve_cred) { |
|
+ force_uid = true, force_gid = true; |
|
+ |
|
+ /* We call setgroups() before and after we enter user namespace, |
|
+ * let's complain only if both fail */ |
|
+ if (setgroups(0, NULL) != 0) |
|
+ setgroups_nerrs++; |
|
+ } |
|
+ |
|
+ /* |
|
* Now that we know which namespaces we want to enter, enter them. |
|
*/ |
|
for (nsfile = namespace_files; nsfile->nstype; nsfile++) { |
|
@@ -302,6 +361,15 @@ int main(int argc, char *argv[]) |
|
if (do_fork == 1) |
|
continue_as_child(); |
|
|
|
+ if (force_uid || force_gid) { |
|
+ if (force_gid && setgroups(0, NULL) != 0 && setgroups_nerrs) /* drop supplementary groups */ |
|
+ err(EXIT_FAILURE, _("setgroups failed")); |
|
+ if (force_gid && setgid(gid) < 0) /* change GID */ |
|
+ err(EXIT_FAILURE, _("setgid failed")); |
|
+ if (force_uid && setuid(uid) < 0) /* change UID */ |
|
+ err(EXIT_FAILURE, _("setuid failed")); |
|
+ } |
|
+ |
|
if (optind < argc) { |
|
execvp(argv[optind], argv + optind); |
|
err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]); |
|
diff -up util-linux-2.23.2/sys-utils/unshare.1.kzak util-linux-2.23.2/sys-utils/unshare.1 |
|
--- util-linux-2.23.2/sys-utils/unshare.1.kzak 2015-06-26 09:58:39.484633521 +0200 |
|
+++ util-linux-2.23.2/sys-utils/unshare.1 2015-06-26 09:58:51.673541033 +0200 |
|
@@ -1,28 +1,27 @@ |
|
-.\" Process this file with |
|
-.\" groff -man -Tascii lscpu.1 |
|
-.\" |
|
-.TH UNSHARE 1 "July 2013" "util-linux" "User Commands" |
|
+.TH UNSHARE 1 "July 2014" "util-linux" "User Commands" |
|
.SH NAME |
|
unshare \- run program with some namespaces unshared from parent |
|
.SH SYNOPSIS |
|
.B unshare |
|
-.RI [ options ] |
|
+[options] |
|
.I program |
|
.RI [ arguments ] |
|
.SH DESCRIPTION |
|
Unshares the indicated namespaces from the parent process and then executes |
|
-the specified program. The namespaces to be unshared are indicated via |
|
+the specified \fIprogram\fR. 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 |
|
(\fBCLONE_NEWNS\fP flag), except for filesystems which are explicitly marked as |
|
-shared (with \fBmount --make-shared\fP; see \fI/proc/self/mountinfo\fP for the |
|
-\fBshared\fP flags). |
|
- |
|
-It's recommended to use \fBmount --make-rprivate\fP or \fBmount --make-rslave\fP |
|
-after \fBunshare --mount\fP to make sure that mountpoints in the new namespace |
|
-are really unshared from parental namespace. |
|
+shared (with \fBmount --make-shared\fP; see \fI/proc/self/mountinfo\fP or |
|
+\fBfindmnt -o+PROPAGATION\fP for the \fBshared\fP flags). |
|
+.sp |
|
+.B unshare |
|
+automatically sets propagation to \fBprivate\fP |
|
+in the new mount namespace to make sure that the new namespace is really |
|
+unshared. This feature is possible to disable by option \fB\-\-propagation unchanged\fP. |
|
+Note that \fBprivate\fP is the kernel default. |
|
.TP |
|
.BR "UTS namespace" |
|
Setting hostname or domainname will not affect the rest of the system. |
|
@@ -40,13 +39,14 @@ sockets, etc. (\fBCLONE_NEWNET\fP flag) |
|
.BR "pid namespace" |
|
Children will have a distinct set of PID to process mappings from their parent. |
|
(\fBCLONE_NEWPID\fP flag) |
|
+.TP |
|
+.BR "user namespace" |
|
+The process will have a distinct set of UIDs, GIDs and capabilities. |
|
+(\fBCLONE_NEWUSER\fP flag) |
|
.PP |
|
See \fBclone\fR(2) for the exact semantics of the flags. |
|
.SH OPTIONS |
|
.TP |
|
-.BR \-h , " \-\-help" |
|
-Display help text and exit. |
|
-.TP |
|
.BR \-i , " \-\-ipc" |
|
Unshare the IPC namespace. |
|
.TP |
|
@@ -63,16 +63,68 @@ See also the \fB--fork\fP and \fB--mount |
|
.BR \-u , " \-\-uts" |
|
Unshare the UTS namespace. |
|
.TP |
|
+.BR \-U , " \-\-user" |
|
+Unshare the user namespace. |
|
+.TP |
|
.BR \-f , " \-\-fork" |
|
Fork the specified \fIprogram\fR as a child process of \fBunshare\fR rather than |
|
running it directly. This is useful when creating a new pid namespace. |
|
.TP |
|
-.BR \-\-mount-proc "[=\fImountpoint\fP]" |
|
-Just before running the program, mount the proc filesystem at the \fImountpoint\fP |
|
+.BR \-\-mount\-proc "[=\fImountpoint\fP]" |
|
+Just before running the program, mount the proc filesystem at \fImountpoint\fP |
|
(default is /proc). This is useful when creating a new pid namespace. It also |
|
implies creating a new mount namespace since the /proc mount would otherwise |
|
-mess up existing programs on the system. The new proc filesystem is explicitly |
|
+mess up existing programs on the system. The new proc filesystem is explicitly |
|
mounted as private (by MS_PRIVATE|MS_REC). |
|
+.TP |
|
+.BR \-r , " \-\-map\-root\-user" |
|
+Run the program only after the current effective user and group IDs have been mapped to |
|
+the superuser UID and GID in the newly created user namespace. This makes it possible to |
|
+conveniently gain capabilities needed to manage various aspects of the newly created |
|
+namespaces (such as configuring interfaces in the network namespace or mounting filesystems in |
|
+the mount namespace) even when run unprivileged. As a mere convenience feature, it does not support |
|
+more sophisticated use cases, such as mapping multiple ranges of UIDs and GIDs. |
|
+This option implies --setgroups=deny. |
|
+.TP |
|
+.BR "\-\-propagation \fIprivate|shared|slave|unchanged\fP" |
|
+Recursively sets mount propagation flag in the new mount namespace. The default |
|
+is to set the propagation to \fIprivate\fP, this feature is possible to disable |
|
+by \fIunchanged\fP argument. The options is silently ignored when mount namespace (\fB\-\-mount\fP) |
|
+is not requested. |
|
+.TP |
|
+.BR "\-\-setgroups \fIallow|deny\fP" |
|
+Allow or deny |
|
+.BR setgroups (2) |
|
+syscall in user namespaces. |
|
+ |
|
+.BR setgroups(2) |
|
+is only callable with CAP_SETGID and CAP_SETGID in a user |
|
+namespace (since Linux 3.19) does not give you permission to call setgroups(2) |
|
+until after GID map has been set. The GID map is writable by root when |
|
+.BR setgroups(2) |
|
+is enabled and GID map becomes writable by unprivileged processes when |
|
+.BR setgroups(2) |
|
+is permanently disabled. |
|
+.TP |
|
+.BR \-V , " \-\-version" |
|
+Display version information and exit. |
|
+.TP |
|
+.BR \-h , " \-\-help" |
|
+Display help text and exit. |
|
+.SH EXAMPLES |
|
+.TP |
|
+.B # unshare --fork --pid --mount-proc readlink /proc/self |
|
+.TQ |
|
+1 |
|
+.br |
|
+Establish a PID namespace, ensure we're PID 1 in it against newly mounted |
|
+procfs instance. |
|
+.TP |
|
+.B $ unshare --map-root-user --user sh -c whoami |
|
+.TQ |
|
+root |
|
+.br |
|
+Establish a user namespace as an unprivileged user with a root user within it. |
|
.SH SEE ALSO |
|
.BR unshare (2), |
|
.BR clone (2), |
|
diff -up util-linux-2.23.2/sys-utils/unshare.c.kzak util-linux-2.23.2/sys-utils/unshare.c |
|
--- util-linux-2.23.2/sys-utils/unshare.c.kzak 2015-06-26 09:58:39.484633521 +0200 |
|
+++ util-linux-2.23.2/sys-utils/unshare.c 2015-06-26 09:58:51.673541033 +0200 |
|
@@ -32,19 +32,117 @@ |
|
|
|
#include "nls.h" |
|
#include "c.h" |
|
+#include "closestream.h" |
|
#include "namespace.h" |
|
#include "exec_shell.h" |
|
#include "xalloc.h" |
|
#include "pathnames.h" |
|
+#include "all-io.h" |
|
|
|
+/* 'private' is kernel default */ |
|
+#define UNSHARE_PROPAGATION_DEFAULT (MS_REC | MS_PRIVATE) |
|
+ |
|
+enum { |
|
+ SETGROUPS_NONE = -1, |
|
+ SETGROUPS_DENY = 0, |
|
+ SETGROUPS_ALLOW = 1, |
|
+}; |
|
+ |
|
+static const char *setgroups_strings[] = |
|
+{ |
|
+ [SETGROUPS_DENY] = "deny", |
|
+ [SETGROUPS_ALLOW] = "allow" |
|
+}; |
|
+ |
|
+static int setgroups_str2id(const char *str) |
|
+{ |
|
+ size_t i; |
|
+ |
|
+ for (i = 0; i < ARRAY_SIZE(setgroups_strings); i++) |
|
+ if (strcmp(str, setgroups_strings[i]) == 0) |
|
+ return i; |
|
+ |
|
+ errx(EXIT_FAILURE, _("unsupported --setgroups argument '%s'"), str); |
|
+} |
|
+ |
|
+static void setgroups_control(int action) |
|
+{ |
|
+ const char *file = _PATH_PROC_SETGROUPS; |
|
+ const char *cmd; |
|
+ int fd; |
|
+ |
|
+ if (action < 0 || (size_t) action >= ARRAY_SIZE(setgroups_strings)) |
|
+ return; |
|
+ cmd = setgroups_strings[action]; |
|
+ |
|
+ fd = open(file, O_WRONLY); |
|
+ if (fd < 0) { |
|
+ if (errno == ENOENT) |
|
+ return; |
|
+ err(EXIT_FAILURE, _("cannot open %s"), file); |
|
+ } |
|
+ |
|
+ if (write_all(fd, cmd, strlen(cmd))) |
|
+ err(EXIT_FAILURE, _("write failed %s"), file); |
|
+ close(fd); |
|
+} |
|
+ |
|
+static void map_id(const char *file, uint32_t from, uint32_t to) |
|
+{ |
|
+ char *buf; |
|
+ int fd; |
|
+ |
|
+ fd = open(file, O_WRONLY); |
|
+ if (fd < 0) |
|
+ err(EXIT_FAILURE, _("cannot open %s"), file); |
|
+ |
|
+ xasprintf(&buf, "%u %u 1", from, to); |
|
+ if (write_all(fd, buf, strlen(buf))) |
|
+ err(EXIT_FAILURE, _("write failed %s"), file); |
|
+ free(buf); |
|
+ close(fd); |
|
+} |
|
+ |
|
+static unsigned long parse_propagation(const char *str) |
|
+{ |
|
+ size_t i; |
|
+ static const struct prop_opts { |
|
+ const char *name; |
|
+ unsigned long flag; |
|
+ } opts[] = { |
|
+ { "slave", MS_REC | MS_SLAVE }, |
|
+ { "private", MS_REC | MS_PRIVATE }, |
|
+ { "shared", MS_REC | MS_SHARED }, |
|
+ { "unchanged", 0 } |
|
+ }; |
|
+ |
|
+ for (i = 0; i < ARRAY_SIZE(opts); i++) { |
|
+ if (strcmp(opts[i].name, str) == 0) |
|
+ return opts[i].flag; |
|
+ } |
|
+ |
|
+ errx(EXIT_FAILURE, _("unsupported propagation mode: %s"), str); |
|
+} |
|
+ |
|
+static void set_propagation(unsigned long flags) |
|
+{ |
|
+ if (flags == 0) |
|
+ return; |
|
+ |
|
+ if (mount("none", "/", NULL, flags, NULL) != 0) |
|
+ err(EXIT_FAILURE, _("cannot change root filesystem propagation")); |
|
+} |
|
|
|
static void usage(int status) |
|
{ |
|
FILE *out = status == EXIT_SUCCESS ? stdout : stderr; |
|
|
|
fputs(USAGE_HEADER, out); |
|
- fprintf(out, |
|
- _(" %s [options] <program> [args...]\n"), program_invocation_short_name); |
|
+ fprintf(out, _(" %s [options] <program> [<argument>...]\n"), |
|
+ program_invocation_short_name); |
|
+ |
|
+ fputs(USAGE_SEPARATOR, out); |
|
+ 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); |
|
@@ -52,8 +150,13 @@ static void usage(int status) |
|
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(_(" -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); |
|
+ fputs(_(" --propagation <slave|shared|private|unchanged>\n" |
|
+ " modify mount propagation in mount namespace\n"), out); |
|
+ fputs(_(" -s, --setgroups allow|deny control the setgroups syscall in user namespaces\n"), out); |
|
|
|
fputs(USAGE_SEPARATOR, out); |
|
fputs(USAGE_HELP, out); |
|
@@ -66,7 +169,9 @@ static void usage(int status) |
|
int main(int argc, char *argv[]) |
|
{ |
|
enum { |
|
- OPT_MOUNTPROC = CHAR_MAX + 1 |
|
+ OPT_MOUNTPROC = CHAR_MAX + 1, |
|
+ OPT_PROPAGATION, |
|
+ OPT_SETGROUPS |
|
}; |
|
static const struct option longopts[] = { |
|
{ "help", no_argument, 0, 'h' }, |
|
@@ -76,20 +181,29 @@ int main(int argc, char *argv[]) |
|
{ "ipc", no_argument, 0, 'i' }, |
|
{ "net", no_argument, 0, 'n' }, |
|
{ "pid", no_argument, 0, 'p' }, |
|
+ { "user", no_argument, 0, 'U' }, |
|
{ "fork", no_argument, 0, 'f' }, |
|
{ "mount-proc", optional_argument, 0, OPT_MOUNTPROC }, |
|
+ { "map-root-user", no_argument, 0, 'r' }, |
|
+ { "propagation", required_argument, 0, OPT_PROPAGATION }, |
|
+ { "setgroups", required_argument, 0, OPT_SETGROUPS }, |
|
{ NULL, 0, 0, 0 } |
|
}; |
|
|
|
+ int setgrpcmd = SETGROUPS_NONE; |
|
int unshare_flags = 0; |
|
- int c, forkit = 0; |
|
+ int c, forkit = 0, maproot = 0; |
|
const char *procmnt = NULL; |
|
+ unsigned long propagation = UNSHARE_PROPAGATION_DEFAULT; |
|
+ uid_t real_euid = geteuid(); |
|
+ gid_t real_egid = getegid();; |
|
|
|
setlocale(LC_ALL, ""); |
|
bindtextdomain(PACKAGE, LOCALEDIR); |
|
textdomain(PACKAGE); |
|
+ atexit(close_stdout); |
|
|
|
- while ((c = getopt_long(argc, argv, "+fhVmuinp", longopts, NULL)) != -1) { |
|
+ while ((c = getopt_long(argc, argv, "+fhVmuinpUr", longopts, NULL)) != -1) { |
|
switch (c) { |
|
case 'f': |
|
forkit = 1; |
|
@@ -114,10 +228,23 @@ int main(int argc, char *argv[]) |
|
case 'p': |
|
unshare_flags |= CLONE_NEWPID; |
|
break; |
|
+ case 'U': |
|
+ unshare_flags |= CLONE_NEWUSER; |
|
+ break; |
|
case OPT_MOUNTPROC: |
|
unshare_flags |= CLONE_NEWNS; |
|
procmnt = optarg ? optarg : "/proc"; |
|
break; |
|
+ case 'r': |
|
+ unshare_flags |= CLONE_NEWUSER; |
|
+ maproot = 1; |
|
+ break; |
|
+ case OPT_SETGROUPS: |
|
+ setgrpcmd = setgroups_str2id(optarg); |
|
+ break; |
|
+ case OPT_PROPAGATION: |
|
+ propagation = parse_propagation(optarg); |
|
+ break; |
|
default: |
|
usage(EXIT_FAILURE); |
|
} |
|
@@ -146,6 +273,25 @@ int main(int argc, char *argv[]) |
|
} |
|
} |
|
|
|
+ if (maproot) { |
|
+ if (setgrpcmd == SETGROUPS_ALLOW) |
|
+ errx(EXIT_FAILURE, _("options --setgroups=allow and " |
|
+ "--map-root-user are mutually exclusive")); |
|
+ |
|
+ /* since Linux 3.19 unprivileged writing of /proc/self/gid_map |
|
+ * has s been disabled unless /proc/self/setgroups is written |
|
+ * first to permanently disable the ability to call setgroups |
|
+ * in that user namespace. */ |
|
+ setgroups_control(SETGROUPS_DENY); |
|
+ map_id(_PATH_PROC_UIDMAP, 0, real_euid); |
|
+ map_id(_PATH_PROC_GIDMAP, 0, real_egid); |
|
+ |
|
+ } else if (setgrpcmd != SETGROUPS_NONE) |
|
+ setgroups_control(setgrpcmd); |
|
+ |
|
+ if ((unshare_flags & CLONE_NEWNS) && propagation) |
|
+ set_propagation(propagation); |
|
+ |
|
if (procmnt && |
|
(mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 || |
|
mount("proc", procmnt, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0))
|
|
|