| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/dotcloud/docker/daemon/execdriver/native/template" |
| 11 | 11 |
"github.com/dotcloud/docker/pkg/apparmor" |
| 12 | 12 |
"github.com/dotcloud/docker/pkg/libcontainer" |
| 13 |
+ "github.com/dotcloud/docker/pkg/libcontainer/mount/nodes" |
|
| 13 | 14 |
) |
| 14 | 15 |
|
| 15 | 16 |
// createContainer populates and configures the container type with the |
| ... | ... |
@@ -34,8 +35,6 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container |
| 34 | 34 |
if err := d.setPrivileged(container); err != nil {
|
| 35 | 35 |
return nil, err |
| 36 | 36 |
} |
| 37 |
- } else {
|
|
| 38 |
- container.Mounts = append(container.Mounts, libcontainer.Mount{Type: "devtmpfs"})
|
|
| 39 | 37 |
} |
| 40 | 38 |
if err := d.setupCgroups(container, c); err != nil {
|
| 41 | 39 |
return nil, err |
| ... | ... |
@@ -97,11 +96,16 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver. |
| 97 | 97 |
return nil |
| 98 | 98 |
} |
| 99 | 99 |
|
| 100 |
-func (d *driver) setPrivileged(container *libcontainer.Container) error {
|
|
| 100 |
+func (d *driver) setPrivileged(container *libcontainer.Container) (err error) {
|
|
| 101 | 101 |
container.Capabilities = libcontainer.GetAllCapabilities() |
| 102 | 102 |
container.Cgroups.DeviceAccess = true |
| 103 | 103 |
|
| 104 | 104 |
delete(container.Context, "restrictions") |
| 105 |
+ delete(container.DeviceNodes, "additional") |
|
| 106 |
+ |
|
| 107 |
+ if container.DeviceNodes["required"], err = nodes.GetHostDeviceNodes(); err != nil {
|
|
| 108 |
+ return err |
|
| 109 |
+ } |
|
| 105 | 110 |
|
| 106 | 111 |
if apparmor.IsEnabled() {
|
| 107 | 112 |
container.Context["apparmor_profile"] = "unconfined" |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"github.com/dotcloud/docker/pkg/apparmor" |
| 5 | 5 |
"github.com/dotcloud/docker/pkg/libcontainer" |
| 6 | 6 |
"github.com/dotcloud/docker/pkg/libcontainer/cgroups" |
| 7 |
+ "github.com/dotcloud/docker/pkg/libcontainer/mount/nodes" |
|
| 7 | 8 |
) |
| 8 | 9 |
|
| 9 | 10 |
// New returns the docker default configuration for libcontainer |
| ... | ... |
@@ -33,6 +34,10 @@ func New() *libcontainer.Container {
|
| 33 | 33 |
DeviceAccess: false, |
| 34 | 34 |
}, |
| 35 | 35 |
Context: libcontainer.Context{},
|
| 36 |
+ DeviceNodes: map[string][]string{
|
|
| 37 |
+ "required": nodes.DefaultNodes, |
|
| 38 |
+ "additional": {"fuse"},
|
|
| 39 |
+ }, |
|
| 36 | 40 |
} |
| 37 | 41 |
if apparmor.IsEnabled() {
|
| 38 | 42 |
container.Context["apparmor_profile"] = "docker-default" |
| ... | ... |
@@ -11,19 +11,20 @@ type Context map[string]string |
| 11 | 11 |
// Container defines configuration options for how a |
| 12 | 12 |
// container is setup inside a directory and how a process should be executed |
| 13 | 13 |
type Container struct {
|
| 14 |
- Hostname string `json:"hostname,omitempty"` // hostname |
|
| 15 |
- ReadonlyFs bool `json:"readonly_fs,omitempty"` // set the containers rootfs as readonly |
|
| 16 |
- NoPivotRoot bool `json:"no_pivot_root,omitempty"` // this can be enabled if you are running in ramdisk |
|
| 17 |
- User string `json:"user,omitempty"` // user to execute the process as |
|
| 18 |
- WorkingDir string `json:"working_dir,omitempty"` // current working directory |
|
| 19 |
- Env []string `json:"environment,omitempty"` // environment to set |
|
| 20 |
- Tty bool `json:"tty,omitempty"` // setup a proper tty or not |
|
| 21 |
- Namespaces map[string]bool `json:"namespaces,omitempty"` // namespaces to apply |
|
| 22 |
- Capabilities []string `json:"capabilities,omitempty"` // capabilities given to the container |
|
| 23 |
- Networks []*Network `json:"networks,omitempty"` // nil for host's network stack |
|
| 24 |
- Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"` // cgroups |
|
| 25 |
- Context Context `json:"context,omitempty"` // generic context for specific options (apparmor, selinux) |
|
| 26 |
- Mounts Mounts `json:"mounts,omitempty"` |
|
| 14 |
+ Hostname string `json:"hostname,omitempty"` // hostname |
|
| 15 |
+ ReadonlyFs bool `json:"readonly_fs,omitempty"` // set the containers rootfs as readonly |
|
| 16 |
+ NoPivotRoot bool `json:"no_pivot_root,omitempty"` // this can be enabled if you are running in ramdisk |
|
| 17 |
+ User string `json:"user,omitempty"` // user to execute the process as |
|
| 18 |
+ WorkingDir string `json:"working_dir,omitempty"` // current working directory |
|
| 19 |
+ Env []string `json:"environment,omitempty"` // environment to set |
|
| 20 |
+ Tty bool `json:"tty,omitempty"` // setup a proper tty or not |
|
| 21 |
+ Namespaces map[string]bool `json:"namespaces,omitempty"` // namespaces to apply |
|
| 22 |
+ Capabilities []string `json:"capabilities,omitempty"` // capabilities given to the container |
|
| 23 |
+ Networks []*Network `json:"networks,omitempty"` // nil for host's network stack |
|
| 24 |
+ Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"` // cgroups |
|
| 25 |
+ Context Context `json:"context,omitempty"` // generic context for specific options (apparmor, selinux) |
|
| 26 |
+ Mounts Mounts `json:"mounts,omitempty"` |
|
| 27 |
+ DeviceNodes map[string][]string `json:"device_nodes,omitempty"` // device nodes to add to the container's /dev |
|
| 27 | 28 |
} |
| 28 | 29 |
|
| 29 | 30 |
// Network defines configuration for a container's networking stack |
| ... | ... |
@@ -4,12 +4,14 @@ import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"os" |
| 6 | 6 |
"testing" |
| 7 |
+ |
|
| 8 |
+ "github.com/dotcloud/docker/pkg/libcontainer/mount/nodes" |
|
| 7 | 9 |
) |
| 8 | 10 |
|
| 9 | 11 |
// Checks whether the expected capability is specified in the capabilities. |
| 10 |
-func hasCapability(expected string, capabilities []string) bool {
|
|
| 11 |
- for _, capability := range capabilities {
|
|
| 12 |
- if capability == expected {
|
|
| 12 |
+func contains(expected string, values []string) bool {
|
|
| 13 |
+ for _, v := range values {
|
|
| 14 |
+ if v == expected {
|
|
| 13 | 15 |
return true |
| 14 | 16 |
} |
| 15 | 17 |
} |
| ... | ... |
@@ -47,18 +49,25 @@ func TestContainerJsonFormat(t *testing.T) {
|
| 47 | 47 |
t.Fail() |
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 |
- if hasCapability("SYS_ADMIN", container.Capabilities) {
|
|
| 50 |
+ if contains("SYS_ADMIN", container.Capabilities) {
|
|
| 51 | 51 |
t.Log("SYS_ADMIN should not be enabled in capabilities mask")
|
| 52 | 52 |
t.Fail() |
| 53 | 53 |
} |
| 54 | 54 |
|
| 55 |
- if !hasCapability("MKNOD", container.Capabilities) {
|
|
| 55 |
+ if !contains("MKNOD", container.Capabilities) {
|
|
| 56 | 56 |
t.Log("MKNOD should be enabled in capabilities mask")
|
| 57 | 57 |
t.Fail() |
| 58 | 58 |
} |
| 59 | 59 |
|
| 60 |
- if hasCapability("SYS_CHROOT", container.Capabilities) {
|
|
| 60 |
+ if contains("SYS_CHROOT", container.Capabilities) {
|
|
| 61 | 61 |
t.Log("capabilities mask should not contain SYS_CHROOT")
|
| 62 | 62 |
t.Fail() |
| 63 | 63 |
} |
| 64 |
+ |
|
| 65 |
+ for _, n := range nodes.DefaultNodes {
|
|
| 66 |
+ if !contains(n, container.DeviceNodes["required"]) {
|
|
| 67 |
+ t.Logf("devices should contain %s", n)
|
|
| 68 |
+ t.Fail() |
|
| 69 |
+ } |
|
| 70 |
+ } |
|
| 64 | 71 |
} |
| ... | ... |
@@ -48,10 +48,10 @@ func InitializeMountNamespace(rootfs, console string, container *libcontainer.Co |
| 48 | 48 |
if err := setupBindmounts(rootfs, container.Mounts); err != nil {
|
| 49 | 49 |
return fmt.Errorf("bind mounts %s", err)
|
| 50 | 50 |
} |
| 51 |
- if err := nodes.CopyN(rootfs, nodes.DefaultNodes, true); err != nil {
|
|
| 52 |
- return fmt.Errorf("copy dev nodes %s", err)
|
|
| 51 |
+ if err := nodes.CopyN(rootfs, container.DeviceNodes["required"], true); err != nil {
|
|
| 52 |
+ return fmt.Errorf("copy required dev nodes %s", err)
|
|
| 53 | 53 |
} |
| 54 |
- if err := nodes.CopyN(rootfs, nodes.AdditionalNodes, false); err != nil {
|
|
| 54 |
+ if err := nodes.CopyN(rootfs, container.DeviceNodes["additional"], false); err != nil {
|
|
| 55 | 55 |
return fmt.Errorf("copy additional dev nodes %s", err)
|
| 56 | 56 |
} |
| 57 | 57 |
if err := SetupPtmx(rootfs, console, container.Context["mount_label"]); err != nil {
|
| ... | ... |
@@ -195,13 +195,11 @@ func newSystemMounts(rootfs, mountLabel string, mounts libcontainer.Mounts) []mo |
| 195 | 195 |
systemMounts := []mount{
|
| 196 | 196 |
{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
|
| 197 | 197 |
{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags},
|
| 198 |
+ {source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: label.FormatMountLabel("mode=755", mountLabel)},
|
|
| 198 | 199 |
{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: label.FormatMountLabel("mode=1777,size=65536k", mountLabel)},
|
| 199 | 200 |
{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)},
|
| 200 | 201 |
{source: "tmpfs", path: filepath.Join(rootfs, "run"), device: "tmpfs", flags: defaultMountFlags},
|
| 201 | 202 |
} |
| 202 | 203 |
|
| 203 |
- if len(mounts.OfType("devtmpfs")) == 1 {
|
|
| 204 |
- systemMounts = append([]mount{{source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: label.FormatMountLabel("mode=755", mountLabel)}}, systemMounts...)
|
|
| 205 |
- } |
|
| 206 | 204 |
return systemMounts |
| 207 | 205 |
} |
| ... | ... |
@@ -4,6 +4,7 @@ package nodes |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 |
+ "io/ioutil" |
|
| 7 | 8 |
"os" |
| 8 | 9 |
"path/filepath" |
| 9 | 10 |
"syscall" |
| ... | ... |
@@ -21,11 +22,6 @@ var DefaultNodes = []string{
|
| 21 | 21 |
"tty", |
| 22 | 22 |
} |
| 23 | 23 |
|
| 24 |
-// AdditionalNodes includes nodes that are not required |
|
| 25 |
-var AdditionalNodes = []string{
|
|
| 26 |
- "fuse", |
|
| 27 |
-} |
|
| 28 |
- |
|
| 29 | 24 |
// CopyN copies the device node from the host into the rootfs |
| 30 | 25 |
func CopyN(rootfs string, nodesToCopy []string, shouldExist bool) error {
|
| 31 | 26 |
oldMask := system.Umask(0000) |
| ... | ... |
@@ -61,3 +57,18 @@ func Copy(rootfs, node string, shouldExist bool) error {
|
| 61 | 61 |
} |
| 62 | 62 |
return nil |
| 63 | 63 |
} |
| 64 |
+ |
|
| 65 |
+func GetHostDeviceNodes() ([]string, error) {
|
|
| 66 |
+ files, err := ioutil.ReadDir("/dev")
|
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return nil, err |
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ out := []string{}
|
|
| 72 |
+ for _, f := range files {
|
|
| 73 |
+ if f.Mode()&os.ModeDevice == os.ModeDevice {
|
|
| 74 |
+ out = append(out, f.Name()) |
|
| 75 |
+ } |
|
| 76 |
+ } |
|
| 77 |
+ return out, nil |
|
| 78 |
+} |