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
|
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
|
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.
|
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
|
.TP
|
||||||
.BR \-V , " \-\-version"
|
.BR \-V , " \-\-version"
|
||||||
Display version information and exit.
|
Display version information and exit.
|
||||||
|
|
|
@ -39,12 +39,39 @@
|
||||||
#include "pathnames.h"
|
#include "pathnames.h"
|
||||||
#include "all-io.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 *file = _PATH_PROC_SETGROUPS;
|
||||||
const char *deny = "deny";
|
const char *cmd;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
if (action < 0 || (size_t) action >= ARRAY_SIZE(setgroups_strings))
|
||||||
|
return;
|
||||||
|
cmd = setgroups_strings[action];
|
||||||
|
|
||||||
fd = open(file, O_WRONLY);
|
fd = open(file, O_WRONLY);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
|
@ -52,7 +79,7 @@ static void disable_setgroups(void)
|
||||||
err(EXIT_FAILURE, _("cannot open %s"), file);
|
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);
|
err(EXIT_FAILURE, _("write failed %s"), file);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
@ -94,6 +121,7 @@ static void usage(int status)
|
||||||
fputs(_(" -f, --fork fork before launching <program>\n"), out);
|
fputs(_(" -f, --fork fork before launching <program>\n"), out);
|
||||||
fputs(_(" --mount-proc[=<dir>] mount proc filesystem first (implies --mount)\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(_(" -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_SEPARATOR, out);
|
||||||
fputs(USAGE_HELP, out);
|
fputs(USAGE_HELP, out);
|
||||||
|
@ -106,7 +134,8 @@ static void usage(int status)
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
enum {
|
enum {
|
||||||
OPT_MOUNTPROC = CHAR_MAX + 1
|
OPT_MOUNTPROC = CHAR_MAX + 1,
|
||||||
|
OPT_SETGROUPS
|
||||||
};
|
};
|
||||||
static const struct option longopts[] = {
|
static const struct option longopts[] = {
|
||||||
{ "help", no_argument, 0, 'h' },
|
{ "help", no_argument, 0, 'h' },
|
||||||
|
@ -120,9 +149,11 @@ int main(int argc, char *argv[])
|
||||||
{ "fork", no_argument, 0, 'f' },
|
{ "fork", no_argument, 0, 'f' },
|
||||||
{ "mount-proc", optional_argument, 0, OPT_MOUNTPROC },
|
{ "mount-proc", optional_argument, 0, OPT_MOUNTPROC },
|
||||||
{ "map-root-user", no_argument, 0, 'r' },
|
{ "map-root-user", no_argument, 0, 'r' },
|
||||||
|
{ "setgroups", required_argument, 0, OPT_SETGROUPS },
|
||||||
{ NULL, 0, 0, 0 }
|
{ NULL, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int setgrpcmd = SETGROUPS_NONE;
|
||||||
int unshare_flags = 0;
|
int unshare_flags = 0;
|
||||||
int c, forkit = 0, maproot = 0;
|
int c, forkit = 0, maproot = 0;
|
||||||
const char *procmnt = NULL;
|
const char *procmnt = NULL;
|
||||||
|
@ -170,6 +201,9 @@ int main(int argc, char *argv[])
|
||||||
unshare_flags |= CLONE_NEWUSER;
|
unshare_flags |= CLONE_NEWUSER;
|
||||||
maproot = 1;
|
maproot = 1;
|
||||||
break;
|
break;
|
||||||
|
case OPT_SETGROUPS:
|
||||||
|
setgrpcmd = setgroups_str2id(optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(EXIT_FAILURE);
|
usage(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -199,10 +233,20 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maproot) {
|
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_UIDMAP, 0, real_euid);
|
||||||
map_id(_PATH_PROC_GIDMAP, 0, real_egid);
|
map_id(_PATH_PROC_GIDMAP, 0, real_egid);
|
||||||
}
|
|
||||||
|
} else if (setgrpcmd != SETGROUPS_NONE)
|
||||||
|
setgroups_control(setgrpcmd);
|
||||||
|
|
||||||
if (procmnt &&
|
if (procmnt &&
|
||||||
(mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 ||
|
(mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL) != 0 ||
|
||||||
|
|
Loading…
Reference in New Issue