Skip to content

Commit 157a96a

Browse files
authored
Merge pull request #977 from cyphar/nsenter-userns-ordering
nsenter: guarantee correct user namespace ordering
2 parents d6b68e8 + e3cd191 commit 157a96a

File tree

5 files changed

+577
-222
lines changed

5 files changed

+577
-222
lines changed

libcontainer/configs/namespaces_unix.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ var (
2222
supportedNamespaces = make(map[NamespaceType]bool)
2323
)
2424

25-
// nsToFile converts the namespace type to its filename
26-
func nsToFile(ns NamespaceType) string {
25+
// NsName converts the namespace type to its filename
26+
func NsName(ns NamespaceType) string {
2727
switch ns {
2828
case NEWNET:
2929
return "net"
@@ -50,7 +50,7 @@ func IsNamespaceSupported(ns NamespaceType) bool {
5050
if ok {
5151
return supported
5252
}
53-
nsFile := nsToFile(ns)
53+
nsFile := NsName(ns)
5454
// if the namespace type is unknown, just return false
5555
if nsFile == "" {
5656
return false
@@ -84,7 +84,7 @@ func (n *Namespace) GetPath(pid int) string {
8484
if n.Path != "" {
8585
return n.Path
8686
}
87-
return fmt.Sprintf("/proc/%d/ns/%s", pid, nsToFile(n.Type))
87+
return fmt.Sprintf("/proc/%d/ns/%s", pid, NsName(n.Type))
8888
}
8989

9090
func (n *Namespaces) Remove(t NamespaceType) bool {

libcontainer/container_linux.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,16 +1223,22 @@ func (c *linuxContainer) currentState() (*State, error) {
12231223
// can setns in order.
12241224
func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceType]string) ([]string, error) {
12251225
paths := []string{}
1226-
nsTypes := []configs.NamespaceType{
1226+
order := []configs.NamespaceType{
1227+
// The user namespace *must* be done first.
1228+
configs.NEWUSER,
12271229
configs.NEWIPC,
12281230
configs.NEWUTS,
12291231
configs.NEWNET,
12301232
configs.NEWPID,
12311233
configs.NEWNS,
12321234
}
1233-
// join userns if the init process explicitly requires NEWUSER
1234-
if c.config.Namespaces.Contains(configs.NEWUSER) {
1235-
nsTypes = append(nsTypes, configs.NEWUSER)
1235+
1236+
// Remove namespaces that we don't need to join.
1237+
var nsTypes []configs.NamespaceType
1238+
for _, ns := range order {
1239+
if c.config.Namespaces.Contains(ns) {
1240+
nsTypes = append(nsTypes, ns)
1241+
}
12361242
}
12371243
for _, nsType := range nsTypes {
12381244
if p, ok := namespaces[nsType]; ok && p != "" {
@@ -1249,7 +1255,7 @@ func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceTyp
12491255
if strings.ContainsRune(p, ',') {
12501256
return nil, newSystemError(fmt.Errorf("invalid path %s", p))
12511257
}
1252-
paths = append(paths, p)
1258+
paths = append(paths, fmt.Sprintf("%s:%s", configs.NsName(nsType), p))
12531259
}
12541260
}
12551261
return paths, nil

libcontainer/nsenter/namespace.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef NSENTER_NAMESPACE_H
2+
#define NSENTER_NAMESPACE_H
3+
4+
#ifndef _GNU_SOURCE
5+
# define _GNU_SOURCE
6+
#endif
7+
#include <sched.h>
8+
9+
/* All of these are taken from include/uapi/linux/sched.h */
10+
#ifndef CLONE_NEWNS
11+
# define CLONE_NEWNS 0x00020000 /* New mount namespace group */
12+
#endif
13+
#ifndef CLONE_NEWCGROUP
14+
# define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */
15+
#endif
16+
#ifndef CLONE_NEWUTS
17+
# define CLONE_NEWUTS 0x04000000 /* New utsname namespace */
18+
#endif
19+
#ifndef CLONE_NEWIPC
20+
# define CLONE_NEWIPC 0x08000000 /* New ipc namespace */
21+
#endif
22+
#ifndef CLONE_NEWUSER
23+
# define CLONE_NEWUSER 0x10000000 /* New user namespace */
24+
#endif
25+
#ifndef CLONE_NEWPID
26+
# define CLONE_NEWPID 0x20000000 /* New pid namespace */
27+
#endif
28+
#ifndef CLONE_NEWNET
29+
# define CLONE_NEWNET 0x40000000 /* New network namespace */
30+
#endif
31+
32+
#endif /* NSENTER_NAMESPACE_H */

libcontainer/nsenter/nsenter_test.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestNsenterValidPaths(t *testing.T) {
2929

3030
namespaces := []string{
3131
// join pid ns of the current process
32-
fmt.Sprintf("/proc/%d/ns/pid", os.Getpid()),
32+
fmt.Sprintf("pid:/proc/%d/ns/pid", os.Getpid()),
3333
}
3434
cmd := &exec.Cmd{
3535
Path: os.Args[0],
@@ -87,7 +87,47 @@ func TestNsenterInvalidPaths(t *testing.T) {
8787

8888
namespaces := []string{
8989
// join pid ns of the current process
90-
fmt.Sprintf("/proc/%d/ns/pid", -1),
90+
fmt.Sprintf("pid:/proc/%d/ns/pid", -1),
91+
}
92+
cmd := &exec.Cmd{
93+
Path: os.Args[0],
94+
Args: args,
95+
ExtraFiles: []*os.File{child},
96+
Env: []string{"_LIBCONTAINER_INITPIPE=3"},
97+
}
98+
99+
if err := cmd.Start(); err != nil {
100+
t.Fatal(err)
101+
}
102+
// write cloneFlags
103+
r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0)
104+
r.AddData(&libcontainer.Int32msg{
105+
Type: libcontainer.CloneFlagsAttr,
106+
Value: uint32(syscall.CLONE_NEWNET),
107+
})
108+
r.AddData(&libcontainer.Bytemsg{
109+
Type: libcontainer.NsPathsAttr,
110+
Value: []byte(strings.Join(namespaces, ",")),
111+
})
112+
if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil {
113+
t.Fatal(err)
114+
}
115+
116+
if err := cmd.Wait(); err == nil {
117+
t.Fatalf("nsenter exits with a zero exit status")
118+
}
119+
}
120+
121+
func TestNsenterIncorrectPathType(t *testing.T) {
122+
args := []string{"nsenter-exec"}
123+
parent, child, err := newPipe()
124+
if err != nil {
125+
t.Fatalf("failed to create pipe %v", err)
126+
}
127+
128+
namespaces := []string{
129+
// join pid ns of the current process
130+
fmt.Sprintf("net:/proc/%d/ns/pid", os.Getpid()),
91131
}
92132
cmd := &exec.Cmd{
93133
Path: os.Args[0],

0 commit comments

Comments
 (0)