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:
parent
02f8fa7863
commit
ed053a740c
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue