Browse Source

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 11 years ago
parent
commit
69989b7c06

+ 2 - 2
daemon/execdriver/driver.go

@@ -136,8 +136,8 @@ type Command struct {
 	Config             map[string][]string `json:"config"` //  generic values that specific drivers can consume
 	Resources          *Resources          `json:"resources"`
 	Mounts             []Mount             `json:"mounts"`
-	AllowedDevices     []devices.Device    `json:"allowed_devices"`
-	AutoCreatedDevices []devices.Device    `json:"autocreated_devices"`
+	AllowedDevices     []*devices.Device   `json:"allowed_devices"`
+	AutoCreatedDevices []*devices.Device   `json:"autocreated_devices"`
 
 	Terminal     Terminal `json:"-"`             // standard or tty terminal
 	Console      string   `json:"-"`             // dev/console path

+ 2 - 2
daemon/execdriver/lxc/lxc_template_unit_test.go

@@ -3,7 +3,6 @@ package lxc
 import (
 	"bufio"
 	"fmt"
-	"github.com/dotcloud/docker/daemon/execdriver"
 	"io/ioutil"
 	"math/rand"
 	"os"
@@ -12,6 +11,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/dotcloud/docker/daemon/execdriver"
 	"github.com/dotcloud/docker/pkg/libcontainer/devices"
 )
 
@@ -49,7 +49,7 @@ func TestLXCConfig(t *testing.T) {
 			Mtu:       1500,
 			Interface: nil,
 		},
-		AllowedDevices: make([]devices.Device, 0),
+		AllowedDevices: make([]*devices.Device, 0),
 	}
 	p, err := driver.generateLXCConfig(command)
 	if err != nil {

+ 7 - 0
daemon/execdriver/native/create.go

@@ -11,6 +11,7 @@ import (
 	"github.com/dotcloud/docker/daemon/execdriver/native/template"
 	"github.com/dotcloud/docker/pkg/apparmor"
 	"github.com/dotcloud/docker/pkg/libcontainer"
+	"github.com/dotcloud/docker/pkg/libcontainer/devices"
 )
 
 // createContainer populates and configures the container type with the
@@ -112,6 +113,12 @@ func (d *driver) setPrivileged(container *libcontainer.Container) (err error) {
 	container.Capabilities = libcontainer.GetAllCapabilities()
 	container.Cgroups.AllowAllDevices = true
 
+	hostDeviceNodes, err := devices.GetHostDeviceNodes()
+	if err != nil {
+		return err
+	}
+	container.DeviceNodes = hostDeviceNodes
+
 	delete(container.Context, "restrictions")
 
 	if apparmor.IsEnabled() {

+ 10 - 10
pkg/libcontainer/cgroups/cgroups.go

@@ -14,16 +14,16 @@ type Cgroup struct {
 	Name   string `json:"name,omitempty"`
 	Parent string `json:"parent,omitempty"` // name of parent cgroup or slice
 
-	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.
-	AllowedDevices    []devices.Device `json:"allowed_devices,omitempty"`
-	Memory            int64            `json:"memory,omitempty"`             // Memory limit (in bytes)
-	MemoryReservation int64            `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes)
-	MemorySwap        int64            `json:"memory_swap,omitempty"`        // Total memory usage (memory + swap); set `-1' to disable swap
-	CpuShares         int64            `json:"cpu_shares,omitempty"`         // CPU shares (relative weight vs. other containers)
-	CpuQuota          int64            `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
-	CpuPeriod         int64            `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
-	CpusetCpus        string           `json:"cpuset_cpus,omitempty"`        // CPU to use
-	Freezer           string           `json:"freezer,omitempty"`            // set the freeze value for the process
+	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.
+	AllowedDevices    []*devices.Device `json:"allowed_devices,omitempty"`
+	Memory            int64             `json:"memory,omitempty"`             // Memory limit (in bytes)
+	MemoryReservation int64             `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes)
+	MemorySwap        int64             `json:"memory_swap,omitempty"`        // Total memory usage (memory + swap); set `-1' to disable swap
+	CpuShares         int64             `json:"cpu_shares,omitempty"`         // CPU shares (relative weight vs. other containers)
+	CpuQuota          int64             `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
+	CpuPeriod         int64             `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
+	CpusetCpus        string            `json:"cpuset_cpus,omitempty"`        // CPU to use
+	Freezer           string            `json:"freezer,omitempty"`            // set the freeze value for the process
 
 	Slice string `json:"slice,omitempty"` // Parent slice to use for systemd
 }

+ 1 - 1
pkg/libcontainer/container.go

@@ -65,7 +65,7 @@ type Container struct {
 	Mounts Mounts `json:"mounts,omitempty"`
 
 	// 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!
-	DeviceNodes []devices.Device `json:"device_nodes,omitempty"`
+	DeviceNodes []*devices.Device `json:"device_nodes,omitempty"`
 }
 
 // Network defines configuration for a container's networking stack

+ 159 - 0
pkg/libcontainer/devices/defaults.go

@@ -0,0 +1,159 @@
+package devices
+
+var (
+	// These are devices that are to be both allowed and created.
+
+	DefaultSimpleDevices = []*Device{
+		// /dev/null and zero
+		{
+			Path:              "/dev/null",
+			Type:              'c',
+			MajorNumber:       1,
+			MinorNumber:       3,
+			CgroupPermissions: "rwm",
+			FileMode:          0666,
+		},
+		{
+			Path:              "/dev/zero",
+			Type:              'c',
+			MajorNumber:       1,
+			MinorNumber:       5,
+			CgroupPermissions: "rwm",
+			FileMode:          0666,
+		},
+
+		{
+			Path:              "/dev/full",
+			Type:              'c',
+			MajorNumber:       1,
+			MinorNumber:       7,
+			CgroupPermissions: "rwm",
+			FileMode:          0666,
+		},
+
+		// consoles and ttys
+		{
+			Path:              "/dev/tty",
+			Type:              'c',
+			MajorNumber:       5,
+			MinorNumber:       0,
+			CgroupPermissions: "rwm",
+			FileMode:          0666,
+		},
+
+		// /dev/urandom,/dev/random
+		{
+			Path:              "/dev/urandom",
+			Type:              'c',
+			MajorNumber:       1,
+			MinorNumber:       9,
+			CgroupPermissions: "rwm",
+			FileMode:          0666,
+		},
+		{
+			Path:              "/dev/random",
+			Type:              'c',
+			MajorNumber:       1,
+			MinorNumber:       8,
+			CgroupPermissions: "rwm",
+			FileMode:          0666,
+		},
+	}
+
+	DefaultAllowedDevices = append([]*Device{
+		// allow mknod for any device
+		{
+			Type:              'c',
+			MajorNumber:       Wildcard,
+			MinorNumber:       Wildcard,
+			CgroupPermissions: "m",
+		},
+		{
+			Type:              'b',
+			MajorNumber:       Wildcard,
+			MinorNumber:       Wildcard,
+			CgroupPermissions: "m",
+		},
+
+		{
+			Path:              "/dev/console",
+			Type:              'c',
+			MajorNumber:       5,
+			MinorNumber:       1,
+			CgroupPermissions: "rwm",
+		},
+		{
+			Path:              "/dev/tty0",
+			Type:              'c',
+			MajorNumber:       4,
+			MinorNumber:       0,
+			CgroupPermissions: "rwm",
+		},
+		{
+			Path:              "/dev/tty1",
+			Type:              'c',
+			MajorNumber:       4,
+			MinorNumber:       1,
+			CgroupPermissions: "rwm",
+		},
+		// /dev/pts/ - pts namespaces are "coming soon"
+		{
+			Path:              "",
+			Type:              'c',
+			MajorNumber:       136,
+			MinorNumber:       Wildcard,
+			CgroupPermissions: "rwm",
+		},
+		{
+			Path:              "",
+			Type:              'c',
+			MajorNumber:       5,
+			MinorNumber:       2,
+			CgroupPermissions: "rwm",
+		},
+
+		// tuntap
+		{
+			Path:              "",
+			Type:              'c',
+			MajorNumber:       10,
+			MinorNumber:       200,
+			CgroupPermissions: "rwm",
+		},
+
+		/*// fuse
+		   {
+		    Path: "",
+		    Type: 'c',
+		    MajorNumber: 10,
+		    MinorNumber: 229,
+		    CgroupPermissions: "rwm",
+		   },
+
+		// rtc
+		   {
+		    Path: "",
+		    Type: 'c',
+		    MajorNumber: 254,
+		    MinorNumber: 0,
+		    CgroupPermissions: "rwm",
+		   },
+		*/
+	}, DefaultSimpleDevices...)
+
+	DefaultAutoCreatedDevices = append([]*Device{
+		{
+			// /dev/fuse is created but not allowed.
+			// This is to allow java to work.  Because java
+			// Insists on there being a /dev/fuse
+			// https://github.com/dotcloud/docker/issues/514
+			// https://github.com/dotcloud/docker/issues/2393
+			//
+			Path:              "/dev/fuse",
+			Type:              'c',
+			MajorNumber:       10,
+			MinorNumber:       229,
+			CgroupPermissions: "rwm",
+		},
+	}, DefaultSimpleDevices...)
+)

+ 61 - 181
pkg/libcontainer/devices/devices.go

@@ -1,8 +1,11 @@
 package devices
 
 import (
+	"errors"
 	"fmt"
+	"io/ioutil"
 	"os"
+	"path/filepath"
 	"syscall"
 )
 
@@ -10,6 +13,10 @@ const (
 	Wildcard = -1
 )
 
+var (
+	ErrNotADeviceNode = errors.New("not a device node")
+)
+
 type Device struct {
 	Type              rune        `json:"type,omitempty"`
 	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 {
 	}
 }
 
-func (device Device) GetCgroupAllowString() string {
+func (device *Device) GetCgroupAllowString() string {
 	return fmt.Sprintf("%c %s:%s %s", device.Type, GetDeviceNumberString(device.MajorNumber), GetDeviceNumberString(device.MinorNumber), device.CgroupPermissions)
 }
 
 // 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.
-func GetDevice(path string, cgroupPermissions string) (Device, error) {
+func GetDevice(path string, cgroupPermissions string) (*Device, error) {
+	fileInfo, err := os.Stat(path)
+	if err != nil {
+		return nil, err
+	}
+
 	var (
-		err                    error
-		fileInfo               os.FileInfo
-		mode                   os.FileMode
-		fileModePermissionBits os.FileMode
 		devType                rune
-		devNumber              int
-		stat_t                 *syscall.Stat_t
-		ok                     bool
-		device                 Device
+		mode                   = fileInfo.Mode()
+		fileModePermissionBits = os.FileMode.Perm(mode)
 	)
 
-	fileInfo, err = os.Stat(path)
-	if err != nil {
-		return Device{}, err
-	}
-
-	mode = fileInfo.Mode()
-	fileModePermissionBits = os.FileMode.Perm(mode)
 	switch {
-	case (mode & os.ModeDevice) == 0:
-		return Device{}, fmt.Errorf("%s is not a device", path)
-	case (mode & os.ModeCharDevice) != 0:
+	case mode&os.ModeDevice == 0:
+		return nil, ErrNotADeviceNode
+	case mode&os.ModeCharDevice != 0:
 		fileModePermissionBits |= syscall.S_IFCHR
 		devType = 'c'
 	default:
@@ -63,177 +62,58 @@ func GetDevice(path string, cgroupPermissions string) (Device, error) {
 		devType = 'b'
 	}
 
-	stat_t, ok = fileInfo.Sys().(*syscall.Stat_t)
+	stat_t, ok := fileInfo.Sys().(*syscall.Stat_t)
 	if !ok {
-		return Device{}, fmt.Errorf("cannot determine the device number for device %s", path)
+		return nil, fmt.Errorf("cannot determine the device number for device %s", path)
 	}
-	devNumber = int(stat_t.Rdev)
+	devNumber := int(stat_t.Rdev)
 
-	device = Device{
+	return &Device{
 		Type:              devType,
 		Path:              path,
 		MajorNumber:       Major(devNumber),
 		MinorNumber:       Minor(devNumber),
 		CgroupPermissions: cgroupPermissions,
 		FileMode:          fileModePermissionBits,
-	}
-	return device, nil
+	}, nil
 }
 
-var (
-	// These are devices that are to be both allowed and created.
-
-	DefaultSimpleDevices = []Device{
-		// /dev/null and zero
-		{
-			Path:              "/dev/null",
-			Type:              'c',
-			MajorNumber:       1,
-			MinorNumber:       3,
-			CgroupPermissions: "rwm",
-			FileMode:          0666,
-		},
-		{
-			Path:              "/dev/zero",
-			Type:              'c',
-			MajorNumber:       1,
-			MinorNumber:       5,
-			CgroupPermissions: "rwm",
-			FileMode:          0666,
-		},
-
-		{
-			Path:              "/dev/full",
-			Type:              'c',
-			MajorNumber:       1,
-			MinorNumber:       7,
-			CgroupPermissions: "rwm",
-			FileMode:          0666,
-		},
-
-		// consoles and ttys
-		{
-			Path:              "/dev/tty",
-			Type:              'c',
-			MajorNumber:       5,
-			MinorNumber:       0,
-			CgroupPermissions: "rwm",
-			FileMode:          0666,
-		},
-
-		// /dev/urandom,/dev/random
-		{
-			Path:              "/dev/urandom",
-			Type:              'c',
-			MajorNumber:       1,
-			MinorNumber:       9,
-			CgroupPermissions: "rwm",
-			FileMode:          0666,
-		},
-		{
-			Path:              "/dev/random",
-			Type:              'c',
-			MajorNumber:       1,
-			MinorNumber:       8,
-			CgroupPermissions: "rwm",
-			FileMode:          0666,
-		},
+func GetHostDeviceNodes() ([]*Device, error) {
+	return getDeviceNodes("/dev")
+}
+
+func getDeviceNodes(path string) ([]*Device, error) {
+	files, err := ioutil.ReadDir(path)
+	if err != nil {
+		return nil, err
 	}
 
-	DefaultAllowedDevices = append([]Device{
-		// allow mknod for any device
-		{
-			Type:              'c',
-			MajorNumber:       Wildcard,
-			MinorNumber:       Wildcard,
-			CgroupPermissions: "m",
-		},
-		{
-			Type:              'b',
-			MajorNumber:       Wildcard,
-			MinorNumber:       Wildcard,
-			CgroupPermissions: "m",
-		},
-
-		{
-			Path:              "/dev/console",
-			Type:              'c',
-			MajorNumber:       5,
-			MinorNumber:       1,
-			CgroupPermissions: "rwm",
-		},
-		{
-			Path:              "/dev/tty0",
-			Type:              'c',
-			MajorNumber:       4,
-			MinorNumber:       0,
-			CgroupPermissions: "rwm",
-		},
-		{
-			Path:              "/dev/tty1",
-			Type:              'c',
-			MajorNumber:       4,
-			MinorNumber:       1,
-			CgroupPermissions: "rwm",
-		},
-		// /dev/pts/ - pts namespaces are "coming soon"
-		{
-			Path:              "",
-			Type:              'c',
-			MajorNumber:       136,
-			MinorNumber:       Wildcard,
-			CgroupPermissions: "rwm",
-		},
-		{
-			Path:              "",
-			Type:              'c',
-			MajorNumber:       5,
-			MinorNumber:       2,
-			CgroupPermissions: "rwm",
-		},
-
-		// tuntap
-		{
-			Path:              "",
-			Type:              'c',
-			MajorNumber:       10,
-			MinorNumber:       200,
-			CgroupPermissions: "rwm",
-		},
-
-		/*// fuse
-		   {
-		    Path: "",
-		    Type: 'c',
-		    MajorNumber: 10,
-		    MinorNumber: 229,
-		    CgroupPermissions: "rwm",
-		   },
-
-		// rtc
-		   {
-		    Path: "",
-		    Type: 'c',
-		    MajorNumber: 254,
-		    MinorNumber: 0,
-		    CgroupPermissions: "rwm",
-		   },
-		*/
-	}, DefaultSimpleDevices...)
-
-	DefaultAutoCreatedDevices = append([]Device{
-		{
-			// /dev/fuse is created but not allowed.
-			// This is to allow java to work.  Because java
-			// Insists on there being a /dev/fuse
-			// https://github.com/dotcloud/docker/issues/514
-			// https://github.com/dotcloud/docker/issues/2393
-			//
-			Path:              "/dev/fuse",
-			Type:              'c',
-			MajorNumber:       10,
-			MinorNumber:       229,
-			CgroupPermissions: "rwm",
-		},
-	}, DefaultSimpleDevices...)
-)
+	out := []*Device{}
+	for _, f := range files {
+		if f.IsDir() {
+			switch f.Name() {
+			case "pts", "shm", "fd":
+				continue
+			default:
+				sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
+				if err != nil {
+					return nil, err
+				}
+
+				out = append(out, sub...)
+				continue
+			}
+		}
+
+		device, err := GetDevice(filepath.Join(path, f.Name()), "rwm")
+		if err != nil {
+			if err == ErrNotADeviceNode {
+				continue
+			}
+			return nil, err
+		}
+		out = append(out, device)
+	}
+
+	return out, nil
+}

+ 2 - 27
pkg/libcontainer/mount/nodes/nodes.go

@@ -4,7 +4,6 @@ package nodes
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"syscall"
@@ -14,7 +13,7 @@ import (
 )
 
 // Create the device nodes in the container.
-func CreateDeviceNodes(rootfs string, nodesToCreate []devices.Device) error {
+func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error {
 	oldMask := system.Umask(0000)
 	defer system.Umask(oldMask)
 
@@ -27,7 +26,7 @@ func CreateDeviceNodes(rootfs string, nodesToCreate []devices.Device) error {
 }
 
 // Creates the device node in the rootfs of the container.
-func CreateDeviceNode(rootfs string, node devices.Device) error {
+func CreateDeviceNode(rootfs string, node *devices.Device) error {
 	var (
 		dest   = filepath.Join(rootfs, node.Path)
 		parent = filepath.Dir(dest)
@@ -52,27 +51,3 @@ func CreateDeviceNode(rootfs string, node devices.Device) error {
 	}
 	return nil
 }
-
-func getDeviceNodes(path string) ([]string, error) {
-	out := []string{}
-	files, err := ioutil.ReadDir(path)
-	if err != nil {
-		return nil, err
-	}
-	for _, f := range files {
-		if f.IsDir() && f.Name() != "pts" && f.Name() != "shm" {
-			sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
-			if err != nil {
-				return nil, err
-			}
-			out = append(out, sub...)
-		} else if f.Mode()&os.ModeDevice == os.ModeDevice {
-			out = append(out, filepath.Join(path, f.Name()))
-		}
-	}
-	return out, nil
-}
-
-func GetHostDeviceNodes() ([]string, error) {
-	return getDeviceNodes("/dev")
-}

+ 1 - 5
pkg/libcontainer/mount/nodes/nodes_unsupported.go

@@ -7,10 +7,6 @@ import (
 	"github.com/dotcloud/docker/pkg/libcontainer/devices"
 )
 
-func GetHostDeviceNodes() ([]string, error) {
-	return nil, libcontainer.ErrUnsupported
-}
-
-func CreateDeviceNodes(rootfs string, nodesToCreate []devices.Device) error {
+func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error {
 	return libcontainer.ErrUnsupported
 }