Browse code

Ensure all dev nodes are copied for privileged

This also makes sure that devices are pointers to avoid copies
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/05/31 10:30:27
Showing 9 changed files
... ...
@@ -136,8 +136,8 @@ type Command struct {
136 136
 	Config             map[string][]string `json:"config"` //  generic values that specific drivers can consume
137 137
 	Resources          *Resources          `json:"resources"`
138 138
 	Mounts             []Mount             `json:"mounts"`
139
-	AllowedDevices     []devices.Device    `json:"allowed_devices"`
140
-	AutoCreatedDevices []devices.Device    `json:"autocreated_devices"`
139
+	AllowedDevices     []*devices.Device   `json:"allowed_devices"`
140
+	AutoCreatedDevices []*devices.Device   `json:"autocreated_devices"`
141 141
 
142 142
 	Terminal     Terminal `json:"-"`             // standard or tty terminal
143 143
 	Console      string   `json:"-"`             // dev/console path
... ...
@@ -3,7 +3,6 @@ package lxc
3 3
 import (
4 4
 	"bufio"
5 5
 	"fmt"
6
-	"github.com/dotcloud/docker/daemon/execdriver"
7 6
 	"io/ioutil"
8 7
 	"math/rand"
9 8
 	"os"
... ...
@@ -12,6 +11,7 @@ import (
12 12
 	"testing"
13 13
 	"time"
14 14
 
15
+	"github.com/dotcloud/docker/daemon/execdriver"
15 16
 	"github.com/dotcloud/docker/pkg/libcontainer/devices"
16 17
 )
17 18
 
... ...
@@ -49,7 +49,7 @@ func TestLXCConfig(t *testing.T) {
49 49
 			Mtu:       1500,
50 50
 			Interface: nil,
51 51
 		},
52
-		AllowedDevices: make([]devices.Device, 0),
52
+		AllowedDevices: make([]*devices.Device, 0),
53 53
 	}
54 54
 	p, err := driver.generateLXCConfig(command)
55 55
 	if err != nil {
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/dotcloud/docker/daemon/execdriver/native/template"
12 12
 	"github.com/dotcloud/docker/pkg/apparmor"
13 13
 	"github.com/dotcloud/docker/pkg/libcontainer"
14
+	"github.com/dotcloud/docker/pkg/libcontainer/devices"
14 15
 )
15 16
 
16 17
 // createContainer populates and configures the container type with the
... ...
@@ -112,6 +113,12 @@ func (d *driver) setPrivileged(container *libcontainer.Container) (err error) {
112 112
 	container.Capabilities = libcontainer.GetAllCapabilities()
113 113
 	container.Cgroups.AllowAllDevices = true
114 114
 
115
+	hostDeviceNodes, err := devices.GetHostDeviceNodes()
116
+	if err != nil {
117
+		return err
118
+	}
119
+	container.DeviceNodes = hostDeviceNodes
120
+
115 121
 	delete(container.Context, "restrictions")
116 122
 
117 123
 	if apparmor.IsEnabled() {
... ...
@@ -14,16 +14,16 @@ type Cgroup struct {
14 14
 	Name   string `json:"name,omitempty"`
15 15
 	Parent string `json:"parent,omitempty"` // name of parent cgroup or slice
16 16
 
17
-	AllowAllDevices   bool             `json:"allow_all_devices,omitempty"` // If this is true allow access to any kind of device within the container.  If false, allow access only to devices explicitly listed in the allowed_devices list.
18
-	AllowedDevices    []devices.Device `json:"allowed_devices,omitempty"`
19
-	Memory            int64            `json:"memory,omitempty"`             // Memory limit (in bytes)
20
-	MemoryReservation int64            `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes)
21
-	MemorySwap        int64            `json:"memory_swap,omitempty"`        // Total memory usage (memory + swap); set `-1' to disable swap
22
-	CpuShares         int64            `json:"cpu_shares,omitempty"`         // CPU shares (relative weight vs. other containers)
23
-	CpuQuota          int64            `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
24
-	CpuPeriod         int64            `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
25
-	CpusetCpus        string           `json:"cpuset_cpus,omitempty"`        // CPU to use
26
-	Freezer           string           `json:"freezer,omitempty"`            // set the freeze value for the process
17
+	AllowAllDevices   bool              `json:"allow_all_devices,omitempty"` // If this is true allow access to any kind of device within the container.  If false, allow access only to devices explicitly listed in the allowed_devices list.
18
+	AllowedDevices    []*devices.Device `json:"allowed_devices,omitempty"`
19
+	Memory            int64             `json:"memory,omitempty"`             // Memory limit (in bytes)
20
+	MemoryReservation int64             `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes)
21
+	MemorySwap        int64             `json:"memory_swap,omitempty"`        // Total memory usage (memory + swap); set `-1' to disable swap
22
+	CpuShares         int64             `json:"cpu_shares,omitempty"`         // CPU shares (relative weight vs. other containers)
23
+	CpuQuota          int64             `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
24
+	CpuPeriod         int64             `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
25
+	CpusetCpus        string            `json:"cpuset_cpus,omitempty"`        // CPU to use
26
+	Freezer           string            `json:"freezer,omitempty"`            // set the freeze value for the process
27 27
 
28 28
 	Slice string `json:"slice,omitempty"` // Parent slice to use for systemd
29 29
 }
... ...
@@ -65,7 +65,7 @@ type Container struct {
65 65
 	Mounts Mounts `json:"mounts,omitempty"`
66 66
 
67 67
 	// The device nodes that should be automatically created within the container upon container start.  Note, make sure that the node is marked as allowed in the cgroup as well!
68
-	DeviceNodes []devices.Device `json:"device_nodes,omitempty"`
68
+	DeviceNodes []*devices.Device `json:"device_nodes,omitempty"`
69 69
 }
70 70
 
71 71
 // Network defines configuration for a container's networking stack
72 72
new file mode 100644
... ...
@@ -0,0 +1,159 @@
0
+package devices
1
+
2
+var (
3
+	// These are devices that are to be both allowed and created.
4
+
5
+	DefaultSimpleDevices = []*Device{
6
+		// /dev/null and zero
7
+		{
8
+			Path:              "/dev/null",
9
+			Type:              'c',
10
+			MajorNumber:       1,
11
+			MinorNumber:       3,
12
+			CgroupPermissions: "rwm",
13
+			FileMode:          0666,
14
+		},
15
+		{
16
+			Path:              "/dev/zero",
17
+			Type:              'c',
18
+			MajorNumber:       1,
19
+			MinorNumber:       5,
20
+			CgroupPermissions: "rwm",
21
+			FileMode:          0666,
22
+		},
23
+
24
+		{
25
+			Path:              "/dev/full",
26
+			Type:              'c',
27
+			MajorNumber:       1,
28
+			MinorNumber:       7,
29
+			CgroupPermissions: "rwm",
30
+			FileMode:          0666,
31
+		},
32
+
33
+		// consoles and ttys
34
+		{
35
+			Path:              "/dev/tty",
36
+			Type:              'c',
37
+			MajorNumber:       5,
38
+			MinorNumber:       0,
39
+			CgroupPermissions: "rwm",
40
+			FileMode:          0666,
41
+		},
42
+
43
+		// /dev/urandom,/dev/random
44
+		{
45
+			Path:              "/dev/urandom",
46
+			Type:              'c',
47
+			MajorNumber:       1,
48
+			MinorNumber:       9,
49
+			CgroupPermissions: "rwm",
50
+			FileMode:          0666,
51
+		},
52
+		{
53
+			Path:              "/dev/random",
54
+			Type:              'c',
55
+			MajorNumber:       1,
56
+			MinorNumber:       8,
57
+			CgroupPermissions: "rwm",
58
+			FileMode:          0666,
59
+		},
60
+	}
61
+
62
+	DefaultAllowedDevices = append([]*Device{
63
+		// allow mknod for any device
64
+		{
65
+			Type:              'c',
66
+			MajorNumber:       Wildcard,
67
+			MinorNumber:       Wildcard,
68
+			CgroupPermissions: "m",
69
+		},
70
+		{
71
+			Type:              'b',
72
+			MajorNumber:       Wildcard,
73
+			MinorNumber:       Wildcard,
74
+			CgroupPermissions: "m",
75
+		},
76
+
77
+		{
78
+			Path:              "/dev/console",
79
+			Type:              'c',
80
+			MajorNumber:       5,
81
+			MinorNumber:       1,
82
+			CgroupPermissions: "rwm",
83
+		},
84
+		{
85
+			Path:              "/dev/tty0",
86
+			Type:              'c',
87
+			MajorNumber:       4,
88
+			MinorNumber:       0,
89
+			CgroupPermissions: "rwm",
90
+		},
91
+		{
92
+			Path:              "/dev/tty1",
93
+			Type:              'c',
94
+			MajorNumber:       4,
95
+			MinorNumber:       1,
96
+			CgroupPermissions: "rwm",
97
+		},
98
+		// /dev/pts/ - pts namespaces are "coming soon"
99
+		{
100
+			Path:              "",
101
+			Type:              'c',
102
+			MajorNumber:       136,
103
+			MinorNumber:       Wildcard,
104
+			CgroupPermissions: "rwm",
105
+		},
106
+		{
107
+			Path:              "",
108
+			Type:              'c',
109
+			MajorNumber:       5,
110
+			MinorNumber:       2,
111
+			CgroupPermissions: "rwm",
112
+		},
113
+
114
+		// tuntap
115
+		{
116
+			Path:              "",
117
+			Type:              'c',
118
+			MajorNumber:       10,
119
+			MinorNumber:       200,
120
+			CgroupPermissions: "rwm",
121
+		},
122
+
123
+		/*// fuse
124
+		   {
125
+		    Path: "",
126
+		    Type: 'c',
127
+		    MajorNumber: 10,
128
+		    MinorNumber: 229,
129
+		    CgroupPermissions: "rwm",
130
+		   },
131
+
132
+		// rtc
133
+		   {
134
+		    Path: "",
135
+		    Type: 'c',
136
+		    MajorNumber: 254,
137
+		    MinorNumber: 0,
138
+		    CgroupPermissions: "rwm",
139
+		   },
140
+		*/
141
+	}, DefaultSimpleDevices...)
142
+
143
+	DefaultAutoCreatedDevices = append([]*Device{
144
+		{
145
+			// /dev/fuse is created but not allowed.
146
+			// This is to allow java to work.  Because java
147
+			// Insists on there being a /dev/fuse
148
+			// https://github.com/dotcloud/docker/issues/514
149
+			// https://github.com/dotcloud/docker/issues/2393
150
+			//
151
+			Path:              "/dev/fuse",
152
+			Type:              'c',
153
+			MajorNumber:       10,
154
+			MinorNumber:       229,
155
+			CgroupPermissions: "rwm",
156
+		},
157
+	}, DefaultSimpleDevices...)
158
+)
... ...
@@ -1,8 +1,11 @@
1 1
 package devices
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"fmt"
6
+	"io/ioutil"
5 7
 	"os"
8
+	"path/filepath"
6 9
 	"syscall"
7 10
 )
8 11
 
... ...
@@ -10,6 +13,10 @@ const (
10 10
 	Wildcard = -1
11 11
 )
12 12
 
13
+var (
14
+	ErrNotADeviceNode = errors.New("not a device node")
15
+)
16
+
13 17
 type Device struct {
14 18
 	Type              rune        `json:"type,omitempty"`
15 19
 	Path              string      `json:"path,omitempty"`               // It is fine if this is an empty string in the case that you are using Wildcards
... ...
@@ -27,35 +34,27 @@ func GetDeviceNumberString(deviceNumber int64) string {
27 27
 	}
28 28
 }
29 29
 
30
-func (device Device) GetCgroupAllowString() string {
30
+func (device *Device) GetCgroupAllowString() string {
31 31
 	return fmt.Sprintf("%c %s:%s %s", device.Type, GetDeviceNumberString(device.MajorNumber), GetDeviceNumberString(device.MinorNumber), device.CgroupPermissions)
32 32
 }
33 33
 
34 34
 // Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct.
35
-func GetDevice(path string, cgroupPermissions string) (Device, error) {
35
+func GetDevice(path string, cgroupPermissions string) (*Device, error) {
36
+	fileInfo, err := os.Stat(path)
37
+	if err != nil {
38
+		return nil, err
39
+	}
40
+
36 41
 	var (
37
-		err                    error
38
-		fileInfo               os.FileInfo
39
-		mode                   os.FileMode
40
-		fileModePermissionBits os.FileMode
41 42
 		devType                rune
42
-		devNumber              int
43
-		stat_t                 *syscall.Stat_t
44
-		ok                     bool
45
-		device                 Device
43
+		mode                   = fileInfo.Mode()
44
+		fileModePermissionBits = os.FileMode.Perm(mode)
46 45
 	)
47 46
 
48
-	fileInfo, err = os.Stat(path)
49
-	if err != nil {
50
-		return Device{}, err
51
-	}
52
-
53
-	mode = fileInfo.Mode()
54
-	fileModePermissionBits = os.FileMode.Perm(mode)
55 47
 	switch {
56
-	case (mode & os.ModeDevice) == 0:
57
-		return Device{}, fmt.Errorf("%s is not a device", path)
58
-	case (mode & os.ModeCharDevice) != 0:
48
+	case mode&os.ModeDevice == 0:
49
+		return nil, ErrNotADeviceNode
50
+	case mode&os.ModeCharDevice != 0:
59 51
 		fileModePermissionBits |= syscall.S_IFCHR
60 52
 		devType = 'c'
61 53
 	default:
... ...
@@ -63,177 +62,58 @@ func GetDevice(path string, cgroupPermissions string) (Device, error) {
63 63
 		devType = 'b'
64 64
 	}
65 65
 
66
-	stat_t, ok = fileInfo.Sys().(*syscall.Stat_t)
66
+	stat_t, ok := fileInfo.Sys().(*syscall.Stat_t)
67 67
 	if !ok {
68
-		return Device{}, fmt.Errorf("cannot determine the device number for device %s", path)
68
+		return nil, fmt.Errorf("cannot determine the device number for device %s", path)
69 69
 	}
70
-	devNumber = int(stat_t.Rdev)
70
+	devNumber := int(stat_t.Rdev)
71 71
 
72
-	device = Device{
72
+	return &Device{
73 73
 		Type:              devType,
74 74
 		Path:              path,
75 75
 		MajorNumber:       Major(devNumber),
76 76
 		MinorNumber:       Minor(devNumber),
77 77
 		CgroupPermissions: cgroupPermissions,
78 78
 		FileMode:          fileModePermissionBits,
79
-	}
80
-	return device, nil
79
+	}, nil
81 80
 }
82 81
 
83
-var (
84
-	// These are devices that are to be both allowed and created.
85
-
86
-	DefaultSimpleDevices = []Device{
87
-		// /dev/null and zero
88
-		{
89
-			Path:              "/dev/null",
90
-			Type:              'c',
91
-			MajorNumber:       1,
92
-			MinorNumber:       3,
93
-			CgroupPermissions: "rwm",
94
-			FileMode:          0666,
95
-		},
96
-		{
97
-			Path:              "/dev/zero",
98
-			Type:              'c',
99
-			MajorNumber:       1,
100
-			MinorNumber:       5,
101
-			CgroupPermissions: "rwm",
102
-			FileMode:          0666,
103
-		},
104
-
105
-		{
106
-			Path:              "/dev/full",
107
-			Type:              'c',
108
-			MajorNumber:       1,
109
-			MinorNumber:       7,
110
-			CgroupPermissions: "rwm",
111
-			FileMode:          0666,
112
-		},
113
-
114
-		// consoles and ttys
115
-		{
116
-			Path:              "/dev/tty",
117
-			Type:              'c',
118
-			MajorNumber:       5,
119
-			MinorNumber:       0,
120
-			CgroupPermissions: "rwm",
121
-			FileMode:          0666,
122
-		},
123
-
124
-		// /dev/urandom,/dev/random
125
-		{
126
-			Path:              "/dev/urandom",
127
-			Type:              'c',
128
-			MajorNumber:       1,
129
-			MinorNumber:       9,
130
-			CgroupPermissions: "rwm",
131
-			FileMode:          0666,
132
-		},
133
-		{
134
-			Path:              "/dev/random",
135
-			Type:              'c',
136
-			MajorNumber:       1,
137
-			MinorNumber:       8,
138
-			CgroupPermissions: "rwm",
139
-			FileMode:          0666,
140
-		},
82
+func GetHostDeviceNodes() ([]*Device, error) {
83
+	return getDeviceNodes("/dev")
84
+}
85
+
86
+func getDeviceNodes(path string) ([]*Device, error) {
87
+	files, err := ioutil.ReadDir(path)
88
+	if err != nil {
89
+		return nil, err
141 90
 	}
142 91
 
143
-	DefaultAllowedDevices = append([]Device{
144
-		// allow mknod for any device
145
-		{
146
-			Type:              'c',
147
-			MajorNumber:       Wildcard,
148
-			MinorNumber:       Wildcard,
149
-			CgroupPermissions: "m",
150
-		},
151
-		{
152
-			Type:              'b',
153
-			MajorNumber:       Wildcard,
154
-			MinorNumber:       Wildcard,
155
-			CgroupPermissions: "m",
156
-		},
157
-
158
-		{
159
-			Path:              "/dev/console",
160
-			Type:              'c',
161
-			MajorNumber:       5,
162
-			MinorNumber:       1,
163
-			CgroupPermissions: "rwm",
164
-		},
165
-		{
166
-			Path:              "/dev/tty0",
167
-			Type:              'c',
168
-			MajorNumber:       4,
169
-			MinorNumber:       0,
170
-			CgroupPermissions: "rwm",
171
-		},
172
-		{
173
-			Path:              "/dev/tty1",
174
-			Type:              'c',
175
-			MajorNumber:       4,
176
-			MinorNumber:       1,
177
-			CgroupPermissions: "rwm",
178
-		},
179
-		// /dev/pts/ - pts namespaces are "coming soon"
180
-		{
181
-			Path:              "",
182
-			Type:              'c',
183
-			MajorNumber:       136,
184
-			MinorNumber:       Wildcard,
185
-			CgroupPermissions: "rwm",
186
-		},
187
-		{
188
-			Path:              "",
189
-			Type:              'c',
190
-			MajorNumber:       5,
191
-			MinorNumber:       2,
192
-			CgroupPermissions: "rwm",
193
-		},
194
-
195
-		// tuntap
196
-		{
197
-			Path:              "",
198
-			Type:              'c',
199
-			MajorNumber:       10,
200
-			MinorNumber:       200,
201
-			CgroupPermissions: "rwm",
202
-		},
203
-
204
-		/*// fuse
205
-		   {
206
-		    Path: "",
207
-		    Type: 'c',
208
-		    MajorNumber: 10,
209
-		    MinorNumber: 229,
210
-		    CgroupPermissions: "rwm",
211
-		   },
212
-
213
-		// rtc
214
-		   {
215
-		    Path: "",
216
-		    Type: 'c',
217
-		    MajorNumber: 254,
218
-		    MinorNumber: 0,
219
-		    CgroupPermissions: "rwm",
220
-		   },
221
-		*/
222
-	}, DefaultSimpleDevices...)
223
-
224
-	DefaultAutoCreatedDevices = append([]Device{
225
-		{
226
-			// /dev/fuse is created but not allowed.
227
-			// This is to allow java to work.  Because java
228
-			// Insists on there being a /dev/fuse
229
-			// https://github.com/dotcloud/docker/issues/514
230
-			// https://github.com/dotcloud/docker/issues/2393
231
-			//
232
-			Path:              "/dev/fuse",
233
-			Type:              'c',
234
-			MajorNumber:       10,
235
-			MinorNumber:       229,
236
-			CgroupPermissions: "rwm",
237
-		},
238
-	}, DefaultSimpleDevices...)
239
-)
92
+	out := []*Device{}
93
+	for _, f := range files {
94
+		if f.IsDir() {
95
+			switch f.Name() {
96
+			case "pts", "shm", "fd":
97
+				continue
98
+			default:
99
+				sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
100
+				if err != nil {
101
+					return nil, err
102
+				}
103
+
104
+				out = append(out, sub...)
105
+				continue
106
+			}
107
+		}
108
+
109
+		device, err := GetDevice(filepath.Join(path, f.Name()), "rwm")
110
+		if err != nil {
111
+			if err == ErrNotADeviceNode {
112
+				continue
113
+			}
114
+			return nil, err
115
+		}
116
+		out = append(out, device)
117
+	}
118
+
119
+	return out, nil
120
+}
... ...
@@ -4,7 +4,6 @@ package nodes
4 4
 
5 5
 import (
6 6
 	"fmt"
7
-	"io/ioutil"
8 7
 	"os"
9 8
 	"path/filepath"
10 9
 	"syscall"
... ...
@@ -14,7 +13,7 @@ import (
14 14
 )
15 15
 
16 16
 // Create the device nodes in the container.
17
-func CreateDeviceNodes(rootfs string, nodesToCreate []devices.Device) error {
17
+func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error {
18 18
 	oldMask := system.Umask(0000)
19 19
 	defer system.Umask(oldMask)
20 20
 
... ...
@@ -27,7 +26,7 @@ func CreateDeviceNodes(rootfs string, nodesToCreate []devices.Device) error {
27 27
 }
28 28
 
29 29
 // Creates the device node in the rootfs of the container.
30
-func CreateDeviceNode(rootfs string, node devices.Device) error {
30
+func CreateDeviceNode(rootfs string, node *devices.Device) error {
31 31
 	var (
32 32
 		dest   = filepath.Join(rootfs, node.Path)
33 33
 		parent = filepath.Dir(dest)
... ...
@@ -52,27 +51,3 @@ func CreateDeviceNode(rootfs string, node devices.Device) error {
52 52
 	}
53 53
 	return nil
54 54
 }
55
-
56
-func getDeviceNodes(path string) ([]string, error) {
57
-	out := []string{}
58
-	files, err := ioutil.ReadDir(path)
59
-	if err != nil {
60
-		return nil, err
61
-	}
62
-	for _, f := range files {
63
-		if f.IsDir() && f.Name() != "pts" && f.Name() != "shm" {
64
-			sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
65
-			if err != nil {
66
-				return nil, err
67
-			}
68
-			out = append(out, sub...)
69
-		} else if f.Mode()&os.ModeDevice == os.ModeDevice {
70
-			out = append(out, filepath.Join(path, f.Name()))
71
-		}
72
-	}
73
-	return out, nil
74
-}
75
-
76
-func GetHostDeviceNodes() ([]string, error) {
77
-	return getDeviceNodes("/dev")
78
-}
... ...
@@ -7,10 +7,6 @@ import (
7 7
 	"github.com/dotcloud/docker/pkg/libcontainer/devices"
8 8
 )
9 9
 
10
-func GetHostDeviceNodes() ([]string, error) {
11
-	return nil, libcontainer.ErrUnsupported
12
-}
13
-
14
-func CreateDeviceNodes(rootfs string, nodesToCreate []devices.Device) error {
10
+func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error {
15 11
 	return libcontainer.ErrUnsupported
16 12
 }