nsenter: specify namespace type in setns()

This avoids us from running into cases where libcontainer thinks that a
particular namespace file is a different type, and makes it a fatal
error rather than causing broken functionality.

Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
Aleksa Sarai 2016-07-19 00:40:24 +10:00
parent 02f8fa7863
commit ed053a740c
No known key found for this signature in database
GPG Key ID: 9E18AA267DDB8DB4
4 changed files with 123 additions and 27 deletions

View File

@ -22,8 +22,8 @@ var (
supportedNamespaces = make(map[NamespaceType]bool)
)
// nsToFile converts the namespace type to its filename
func nsToFile(ns NamespaceType) string {
// NsName converts the namespace type to its filename
func NsName(ns NamespaceType) string {
switch ns {
case NEWNET:
return "net"
@ -50,7 +50,7 @@ func IsNamespaceSupported(ns NamespaceType) bool {
if ok {
return supported
}
nsFile := nsToFile(ns)
nsFile := NsName(ns)
// if the namespace type is unknown, just return false
if nsFile == "" {
return false
@ -84,7 +84,7 @@ func (n *Namespace) GetPath(pid int) string {
if n.Path != "" {
return n.Path
}
return fmt.Sprintf("/proc/%d/ns/%s", pid, nsToFile(n.Type))
return fmt.Sprintf("/proc/%d/ns/%s", pid, NsName(n.Type))
}
func (n *Namespaces) Remove(t NamespaceType) bool {

View File

@ -1223,16 +1223,21 @@ func (c *linuxContainer) currentState() (*State, error) {
// can setns in order.
func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceType]string) ([]string, error) {
paths := []string{}
nsTypes := []configs.NamespaceType{
order := []configs.NamespaceType{
configs.NEWIPC,
configs.NEWUTS,
configs.NEWNET,
configs.NEWPID,
configs.NEWNS,
configs.NEWUSER,
}
// join userns if the init process explicitly requires NEWUSER
if c.config.Namespaces.Contains(configs.NEWUSER) {
nsTypes = append(nsTypes, configs.NEWUSER)
// Remove namespaces that we don't need to join.
var nsTypes []configs.NamespaceType
for _, ns := range order {
if c.config.Namespaces.Contains(ns) {
nsTypes = append(nsTypes, ns)
}
}
for _, nsType := range nsTypes {
if p, ok := namespaces[nsType]; ok && p != "" {
@ -1249,7 +1254,7 @@ func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceTyp
if strings.ContainsRune(p, ',') {
return nil, newSystemError(fmt.Errorf("invalid path %s", p))
}
paths = append(paths, p)
paths = append(paths, fmt.Sprintf("%s:%s", configs.NsName(nsType), p))
}
}
return paths, nil

View File

@ -29,7 +29,7 @@ func TestNsenterValidPaths(t *testing.T) {
namespaces := []string{
// join pid ns of the current process
fmt.Sprintf("/proc/%d/ns/pid", os.Getpid()),
fmt.Sprintf("pid:/proc/%d/ns/pid", os.Getpid()),
}
cmd := &exec.Cmd{
Path: os.Args[0],
@ -87,7 +87,47 @@ func TestNsenterInvalidPaths(t *testing.T) {
namespaces := []string{
// join pid ns of the current process
fmt.Sprintf("/proc/%d/ns/pid", -1),
fmt.Sprintf("pid:/proc/%d/ns/pid", -1),
}
cmd := &exec.Cmd{
Path: os.Args[0],
Args: args,
ExtraFiles: []*os.File{child},
Env: []string{"_LIBCONTAINER_INITPIPE=3"},
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
// write cloneFlags
r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0)
r.AddData(&libcontainer.Int32msg{
Type: libcontainer.CloneFlagsAttr,
Value: uint32(syscall.CLONE_NEWNET),
})
r.AddData(&libcontainer.Bytemsg{
Type: libcontainer.NsPathsAttr,
Value: []byte(strings.Join(namespaces, ",")),
})
if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil {
t.Fatal(err)
}
if err := cmd.Wait(); err == nil {
t.Fatalf("nsenter exits with a zero exit status")
}
}
func TestNsenterIncorrectPathType(t *testing.T) {
args := []string{"nsenter-exec"}
parent, child, err := newPipe()
if err != nil {
t.Fatalf("failed to create pipe %v", err)
}
namespaces := []string{
// join pid ns of the current process
fmt.Sprintf("net:/proc/%d/ns/pid", os.Getpid()),
}
cmd := &exec.Cmd{
Path: os.Args[0],

View File

@ -11,6 +11,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
@ -265,6 +266,44 @@ static void start_child(int pipenum, jmp_buf *env, int syncpipe[2], struct nlcon
exit(0);
}
/* Returns the clone(2) flag for a namespace, given the name of a namespace. */
static int nsflag(char *name)
{
if (false)
/* dummy */ ;
#ifdef CLONE_NEWCGROUP
else if (!strcmp(name, "cgroup"))
return CLONE_NEWCGROUP;
#endif
#ifdef CLONE_NEWIPC
else if (!strcmp(name, "ipc"))
return CLONE_NEWIPC;
#endif
#ifdef CLONE_NEWNS
else if (!strcmp(name, "mnt"))
return CLONE_NEWNS;
#endif
#ifdef CLONE_NEWNET
else if (!strcmp(name, "net"))
return CLONE_NEWNET;
#endif
#ifdef CLONE_NEWPID
else if (!strcmp(name, "pid"))
return CLONE_NEWPID;
#endif
#ifdef CLONE_NEWUSER
else if (!strcmp(name, "user"))
return CLONE_NEWUSER;
#endif
#ifdef CLONE_NEWUTS
else if (!strcmp(name, "uts"))
return CLONE_NEWUTS;
#endif
/* If we don't recognise a name, fallback to 0. */
return 0;
}
static void nl_parse(int fd, struct nlconfig_t *config)
{
size_t len, size;
@ -328,8 +367,13 @@ static void nl_parse(int fd, struct nlconfig_t *config)
*/
char *saveptr = NULL;
char *ns = strtok_r(current, ",", &saveptr);
int *fds = NULL, num = 0, i;
char **paths = NULL;
int num = 0, i;
struct namespace_t {
int fd;
int ns;
char *path;
} *nses = NULL;
if (!ns || !strlen(current))
bail("ns paths are empty");
@ -341,32 +385,39 @@ static void nl_parse(int fd, struct nlconfig_t *config)
*/
do {
int fd;
char *path;
/* Resize fds. */
num++;
fds = realloc(fds, num * sizeof(int));
paths = realloc(paths, num * sizeof(char *));
/* Resize the namespace array. */
nses = realloc(nses, ++num * sizeof(struct namespace_t));
fd = open(ns, O_RDONLY);
/* Split 'ns:path'. */
path = strstr(ns, ":");
if (!path)
bail("failed to parse %s", ns);
*path++ = '\0';
fd = open(path, O_RDONLY);
if (fd < 0)
bail("failed to open %s", ns);
fds[num - 1] = fd;
paths[num - 1] = ns;
nses[num - 1] = (struct namespace_t) {
.fd = fd,
.ns = nsflag(ns),
.path = path,
};
} while ((ns = strtok_r(NULL, ",", &saveptr)) != NULL);
for (i = 0; i < num; i++) {
int fd = fds[i];
char *path = paths[i];
struct namespace_t ns = nses[i];
if (setns(fd, 0) < 0)
bail("failed to setns to %s", path);
/* Actually join the namespaces. */
if (setns(ns.fd, ns.ns) < 0)
bail("failed to setns to %s", ns.path);
close(fd);
close(ns.fd);
}
free(fds);
free(paths);
free(nses);
break;
}
case UIDMAP_ATTR: