Skip to content

Commit

Permalink
rootless: allow setgroups if --force-mapping-tool is set
Browse files Browse the repository at this point in the history
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed Jan 15, 2018
1 parent 553438a commit 9e4fd2f
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 30 deletions.
4 changes: 4 additions & 0 deletions create.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ command(s) that get executed on start, edit the args parameter of the spec. See
Name: "preserve-fds",
Usage: "Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total)",
},
cli.BoolFlag{
Name: "force-mapping-tool",
Usage: "forcibly use newuidmap/newgidmap tool. This is useful for rootless mode to keep /proc/PID/setgroups \"allow\"",
},
},
Action: func(context *cli.Context) error {
if err := checkArgs(context, 1, exactArgs); err != nil {
Expand Down
12 changes: 10 additions & 2 deletions libcontainer/container_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type linuxContainer struct {
criuPath string
newuidmapPath string
newgidmapPath string
forceMappingTool bool
m sync.Mutex
criuVersion int
state containerState
Expand Down Expand Up @@ -1749,8 +1750,9 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
Value: []byte(c.newgidmapPath),
})
}
// The following only applies if we are root.
if !c.config.Rootless {
// The following only applies if we are root, unless we are
// advised to use the mapping tool.
if !(c.config.Rootless && !c.forceMappingTool) {
// check if we have CAP_SETGID to setgroup properly
pid, err := capability.NewPid(os.Getpid())
if err != nil {
Expand All @@ -1764,6 +1766,12 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
}
}
}
if c.forceMappingTool {
r.AddData(&Boolmsg{
Type: ForceMappingToolAttr,
Value: true,
})
}
}

// write oom_score_adj
Expand Down
31 changes: 22 additions & 9 deletions libcontainer/factory_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ type LinuxFactory struct {
NewuidmapPath string
NewgidmapPath string

// ForceMappingTool forcibly enables newuidmap/newgidmap tool.
ForceMappingTool bool

// Validator provides validation to container configurations.
Validator validate.Validator

Expand Down Expand Up @@ -191,15 +194,16 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
return nil, newGenericError(err, SystemError)
}
c := &linuxContainer{
id: id,
root: containerRoot,
config: config,
initPath: l.InitPath,
initArgs: l.InitArgs,
criuPath: l.CriuPath,
newuidmapPath: l.NewuidmapPath,
newgidmapPath: l.NewgidmapPath,
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
id: id,
root: containerRoot,
config: config,
initPath: l.InitPath,
initArgs: l.InitArgs,
criuPath: l.CriuPath,
newuidmapPath: l.NewuidmapPath,
newgidmapPath: l.NewgidmapPath,
forceMappingTool: l.ForceMappingTool,
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
}
if intelrdt.IsEnabled() {
c.intelRdtManager = l.NewIntelRdtManager(config, id, "")
Expand Down Expand Up @@ -362,3 +366,12 @@ func NewgidmapPath(newgidmapPath string) func(*LinuxFactory) error {
return nil
}
}

// ForceMappingTool returns an option func to configure a LinuxFactory with the
// provided ..
func ForceMappingTool(flag bool) func(*LinuxFactory) error {
return func(l *LinuxFactory) error {
l.ForceMappingTool = flag
return nil
}
}
21 changes: 11 additions & 10 deletions libcontainer/message_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import (
// list of known message types we want to send to bootstrap program
// The number is randomly chosen to not conflict with known netlink types
const (
InitMsg uint16 = 62000
CloneFlagsAttr uint16 = 27281
NsPathsAttr uint16 = 27282
UidmapAttr uint16 = 27283
GidmapAttr uint16 = 27284
SetgroupAttr uint16 = 27285
OomScoreAdjAttr uint16 = 27286
RootlessAttr uint16 = 27287
UidmapPathAttr uint16 = 27288
GidmapPathAttr uint16 = 27289
InitMsg uint16 = 62000
CloneFlagsAttr uint16 = 27281
NsPathsAttr uint16 = 27282
UidmapAttr uint16 = 27283
GidmapAttr uint16 = 27284
SetgroupAttr uint16 = 27285
OomScoreAdjAttr uint16 = 27286
RootlessAttr uint16 = 27287
UidmapPathAttr uint16 = 27288
GidmapPathAttr uint16 = 27289
ForceMappingToolAttr uint16 = 27290
)

type Int32msg struct {
Expand Down
36 changes: 28 additions & 8 deletions libcontainer/nsenter/nsexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ struct nlconfig_t {
size_t uidmappath_len;
char *gidmappath;
size_t gidmappath_len;
uint8_t force_mapping_tool;
};

/*
Expand All @@ -104,6 +105,7 @@ struct nlconfig_t {
#define ROOTLESS_ATTR 27287
#define UIDMAPPATH_ATTR 27288
#define GIDMAPPATH_ATTR 27289
#define FORCE_MAPPING_TOOL_ATTR 27290

/*
* Use the raw syscall for versions of glibc which don't include a function for
Expand Down Expand Up @@ -178,7 +180,8 @@ enum policy_t {
SETGROUPS_DENY,
};

/* This *must* be called before we touch gid_map. */
/* This *must* be called before we touch gid_map,
unless force_mapping_tool is set */
static void update_setgroups(int pid, enum policy_t setgroup)
{
char *policy;
Expand Down Expand Up @@ -272,11 +275,17 @@ static int try_mapping_tool(const char *app, int pid, char *map, size_t map_len)
return -1;
}

static void update_uidmap(const char *path, int pid, char *map, size_t map_len)
static void update_uidmap(const char *path, int pid, char *map, size_t map_len, bool force_mapping_tool)
{
if (map == NULL || map_len <= 0)
return;

if (force_mapping_tool) {
if (try_mapping_tool(path, pid, map, map_len))
bail("failed to use newuid map on %d", pid);
return;
}

if (write_file(map, map_len, "/proc/%d/uid_map", pid) < 0) {
if (errno != EPERM)
bail("failed to update /proc/%d/uid_map", pid);
Expand All @@ -285,11 +294,17 @@ static void update_uidmap(const char *path, int pid, char *map, size_t map_len)
}
}

static void update_gidmap(const char *path, int pid, char *map, size_t map_len)
static void update_gidmap(const char *path, int pid, char *map, size_t map_len, bool force_mapping_tool)
{
if (map == NULL || map_len <= 0)
return;

if (force_mapping_tool) {
if (try_mapping_tool(path, pid, map, map_len))
bail("failed to use newgid map on %d", pid);
return;
}

if (write_file(map, map_len, "/proc/%d/gid_map", pid) < 0) {
if (errno != EPERM)
bail("failed to update /proc/%d/gid_map", pid);
Expand Down Expand Up @@ -450,6 +465,9 @@ static void nl_parse(int fd, struct nlconfig_t *config)
case SETGROUP_ATTR:
config->is_setgroup = readint8(current);
break;
case FORCE_MAPPING_TOOL_ATTR:
config->force_mapping_tool = readint8(current);
break;
default:
bail("unknown netlink message type %d", nlattr->nla_type);
}
Expand Down Expand Up @@ -681,20 +699,22 @@ void nsexec(void)
* have to explicitly disable setgroups(2) if we're
* creating a rootless container (this is required since
* Linux 3.19).
*
* However, we can use mapping tool (with suid bit) to allow setgroups.
*/
if (config.is_rootless && config.is_setgroup) {
if (config.is_rootless && config.is_setgroup && !config.force_mapping_tool) {
kill(child, SIGKILL);
bail("cannot allow setgroup in an unprivileged user namespace setup");
bail("cannot allow setgroup in an unprivileged user namespace setup without the mapping tool");
}

if (config.is_setgroup)
update_setgroups(child, SETGROUPS_ALLOW);
if (config.is_rootless)
if (config.is_rootless && !config.force_mapping_tool)
update_setgroups(child, SETGROUPS_DENY);

/* Set up mappings. */
update_uidmap(config.uidmappath, child, config.uidmap, config.uidmap_len);
update_gidmap(config.gidmappath, child, config.gidmap, config.gidmap_len);
update_uidmap(config.uidmappath, child, config.uidmap, config.uidmap_len, config.force_mapping_tool);
update_gidmap(config.gidmappath, child, config.gidmap, config.gidmap_len, config.force_mapping_tool);

s = SYNC_USERMAP_ACK;
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
Expand Down
4 changes: 4 additions & 0 deletions run.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ command(s) that get executed on start, edit the args parameter of the spec. See
Name: "preserve-fds",
Usage: "Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total)",
},
cli.BoolFlag{
Name: "force-mapping-tool",
Usage: "forcibly use newuidmap/newgidmap tool. This is useful for rootless mode to keep /proc/PID/setgroups \"allow\"",
},
},
Action: func(context *cli.Context) error {
if err := checkArgs(context, 1, exactArgs); err != nil {
Expand Down
7 changes: 6 additions & 1 deletion utils_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
if err != nil {
newgidmap = ""
}
forceMappingTool := context.Bool("force-mapping-tool")
if forceMappingTool && (newuidmap == "" || newgidmap == "") {
return nil, errors.New("force mapping tool flag passed, but newuidmap/newgidmap tool is not available")
}

return libcontainer.New(abs, cgroupManager, intelRdtManager,
libcontainer.CriuPath(context.GlobalString("criu")),
libcontainer.NewuidmapPath(newuidmap),
libcontainer.NewgidmapPath(newgidmap))
libcontainer.NewgidmapPath(newgidmap),
libcontainer.ForceMappingTool(forceMappingTool))
}

// getContainer returns the specified container instance by loading it from state
Expand Down

0 comments on commit 9e4fd2f

Please sign in to comment.