unshare: add --setgroups=deny|allow
Since Linux 3.19 the file /proc/self/setgroups controls setgroups(2) syscall usage in user namespaces. This patch provides command line knob for this feature. The new --setgroups does not automatically implies --user to avoid complexity, it's user's responsibility to use it in right context. The exception is --map-root-user which is mutually exclusive to --setgroups=allow. CC: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
0bf159413b
commit
fbceefded6
|
@ -85,6 +85,21 @@ conveniently gain capabilities needed to manage various aspects of the newly cre
|
|||
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 \-s , " \-\-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 permamently disabled.
|
||||
.TP
|
||||
.BR \-V , " \-\-version"
|
||||
Display version information and exit.
|
||||
|
|
|
@ -39,12 +39,39 @@
|
|||
#include "pathnames.h"
|
||||
#include "all-io.h"
|
||||
|
||||
static void disable_setgroups(void)
|
||||
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 *deny = "deny";
|
||||
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)
|
||||
|
@ -52,7 +79,7 @@ static void disable_setgroups(void)
|
|||
err(EXIT_FAILURE, _("cannot open %s"), file);
|
||||
}
|
||||
|
||||
if (write_all(fd, deny, strlen(deny)))
|
||||
if (write_all(fd, cmd, strlen(cmd)))
|
||||
err(EXIT_FAILURE, _("write failed %s"), file);
|
||||
close(fd);
|
||||
}
|
||||
|
@ -94,6 +121,7 @@ static void usage(int status)
|
|||
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(_(" -s, --setgroups <allow|deny> control setgroups syscall in user namespaces\n"), out);
|
||||
|
||||
fputs(USAGE_SEPARATOR, out);
|
||||
fputs(USAGE_HELP, out);
|
||||
|
@ -106,7 +134,8 @@ static void usage(int status)
|
|||
int main(int argc, char *argv[])
|
||||
{
|
||||
enum {
|
||||
OPT_MOUNTPROC = CHAR_MAX + 1
|
||||
OPT_MOUNTPROC = CHAR_MAX + 1,
|
||||
OPT_SETGROUPS
|
||||
};
|
||||
static const struct option longopts[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
|
@ -120,9 +149,11 @@ int main(int argc, char *argv[])
|
|||
{ "fork", no_argument, 0, 'f' },
|
||||
{ "mount-proc", optional_argument, 0, OPT_MOUNTPROC },
|
||||
{ "map-root-user", no_argument, 0, 'r' },
|
||||
{ "setgroups", required_argument, 0, OPT_SETGROUPS },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
int setgrpcmd = SETGROUPS_NONE;
|
||||
int unshare_flags = 0;
|
||||
int c, forkit = 0, maproot = 0;
|
||||
const char *procmnt = NULL;
|
||||
|
@ -170,6 +201,9 @@ int main(int argc, char *argv[])
|
|||
unshare_flags |= CLONE_NEWUSER;
|
||||
maproot = 1;
|
||||
break;
|
||||
case OPT_SETGROUPS:
|
||||
setgrpcmd = setgroups_str2id(optarg);
|
||||
break;
|
||||
default:
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
|
@ -199,10 +233,20 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
if (maproot) {
|
||||
disable_setgroups();
|
||||
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 (procmnt &&
|
||||
(mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 ||
|
||||
|
|
Loading…
Reference in New Issue