diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 1b2d7232d3..a8adda6ded 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -38,13 +38,16 @@ func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) } if c.ProcessConfig.Privileged { - // clear readonly for /sys - for i := range container.Mounts { - if container.Mounts[i].Destination == "/sys" { - container.Mounts[i].Flags &= ^syscall.MS_RDONLY + if !container.Readonlyfs { + // clear readonly for /sys + for i := range container.Mounts { + if container.Mounts[i].Destination == "/sys" { + container.Mounts[i].Flags &= ^syscall.MS_RDONLY + } } + container.ReadonlyPaths = nil } - container.ReadonlyPaths = nil + container.MaskPaths = nil if err := d.setPrivileged(container); err != nil { return nil, err @@ -63,6 +66,19 @@ func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) return nil, err } + if container.Readonlyfs { + for i := range container.Mounts { + switch container.Mounts[i].Destination { + case "/proc", "/dev", "/dev/pts": + continue + } + container.Mounts[i].Flags |= syscall.MS_RDONLY + } + + /* These paths must be remounted as r/o */ + container.ReadonlyPaths = append(container.ReadonlyPaths, "/proc", "/dev") + } + if err := d.setupMounts(container, c); err != nil { return nil, err } diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index f2edb0bf89..a6c6d1d2ba 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2789,11 +2789,25 @@ func (s *DockerSuite) TestRunContainerWithWritableRootfs(c *check.C) { func (s *DockerSuite) TestRunContainerWithReadonlyRootfs(c *check.C) { testRequires(c, NativeExecDriver) - for _, f := range []string{"/file", "/etc/hosts", "/etc/resolv.conf", "/etc/hostname"} { + for _, f := range []string{"/file", "/etc/hosts", "/etc/resolv.conf", "/etc/hostname", "/proc/uptime", "/sys/kernel", "/dev/.dont.touch.me"} { testReadOnlyFile(f, c) } } +func (s *DockerSuite) TestPermissionsPtsReadonlyRootfs(c *check.C) { + testRequires(c, NativeExecDriver) + + // Ensure we have not broken writing /dev/pts + out, status := dockerCmd(c, "run", "--read-only", "--rm", "busybox", "mount") + if status != 0 { + c.Fatal("Could not obtain mounts when checking /dev/pts mntpnt.") + } + expected := "type devpts (rw," + if !strings.Contains(string(out), expected) { + c.Fatalf("expected output to contain %s but contains %s", expected, out) + } +} + func testReadOnlyFile(filename string, c *check.C) { testRequires(c, NativeExecDriver) @@ -2805,6 +2819,15 @@ func testReadOnlyFile(filename string, c *check.C) { if !strings.Contains(string(out), expected) { c.Fatalf("expected output from failure to contain %s but contains %s", expected, out) } + + out, err = exec.Command(dockerBinary, "run", "--read-only", "--privileged", "--rm", "busybox", "touch", filename).CombinedOutput() + if err == nil { + c.Fatal("expected container to error on run with read only error") + } + expected = "Read-only file system" + if !strings.Contains(string(out), expected) { + c.Fatalf("expected output from failure to contain %s but contains %s", expected, out) + } } func (s *DockerSuite) TestRunContainerWithReadonlyEtcHostsAndLinkedContainer(c *check.C) {