From c2a80eb59ff210376bac41143a6e4ab0f0b28085 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 30 May 2014 18:30:27 -0700 Subject: [PATCH] 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 (github: crosbymichael) --- cgroups/cgroups.go | 20 +-- container.go | 2 +- devices/defaults.go | 159 +++++++++++++++++++++ devices/devices.go | 236 ++++++++----------------------- mount/nodes/nodes.go | 29 +--- mount/nodes/nodes_unsupported.go | 6 +- 6 files changed, 231 insertions(+), 221 deletions(-) create mode 100644 devices/defaults.go diff --git a/cgroups/cgroups.go b/cgroups/cgroups.go index 537a27f2..905d0ca0 100644 --- a/cgroups/cgroups.go +++ b/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 } diff --git a/container.go b/container.go index d56e037b..c5864e94 100644 --- a/container.go +++ b/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 diff --git a/devices/defaults.go b/devices/defaults.go new file mode 100644 index 00000000..393c438c --- /dev/null +++ b/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...) +) diff --git a/devices/devices.go b/devices/devices.go index 6423337d..f6bee56d 100644 --- a/devices/devices.go +++ b/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) { - 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 - ) - - fileInfo, err = os.Stat(path) +func GetDevice(path string, cgroupPermissions string) (*Device, error) { + fileInfo, err := os.Stat(path) if err != nil { - return Device{}, err + return nil, err } - mode = fileInfo.Mode() - fileModePermissionBits = os.FileMode.Perm(mode) + var ( + devType rune + 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. +func GetHostDeviceNodes() ([]*Device, error) { + return getDeviceNodes("/dev") +} - 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 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", - }, + 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 + } - { - 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", - }, + out = append(out, sub...) + continue + } + } - // tuntap - { - Path: "", - Type: 'c', - MajorNumber: 10, - MinorNumber: 200, - CgroupPermissions: "rwm", - }, + device, err := GetDevice(filepath.Join(path, f.Name()), "rwm") + if err != nil { + if err == ErrNotADeviceNode { + continue + } + return nil, err + } + out = append(out, device) + } - /*// 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...) -) + return out, nil +} diff --git a/mount/nodes/nodes.go b/mount/nodes/nodes.go index 18ef487a..dd67ae2d 100644 --- a/mount/nodes/nodes.go +++ b/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") -} diff --git a/mount/nodes/nodes_unsupported.go b/mount/nodes/nodes_unsupported.go index b92f89b0..0e5d12c7 100644 --- a/mount/nodes/nodes_unsupported.go +++ b/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 }