Merge pull request #13072 from jfrazelle/bump_v1.6.1
Bump v1.6.1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c54138..a2e22a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## 1.6.1 (2015-05-07)
+
+#### Security
+- Fix read/write /proc paths (CVE-2015-3630)
+- Prohibit VOLUME /proc and VOLUME / (CVE-2015-3631)
+- Fix opening of file-descriptor 1 (CVE-2015-3627)
+- Fix symlink traversal on container respawn allowing local privilege escalation (CVE-2015-3629)
+- Prohibit mount of /sys
+
+#### Runtime
+- Update Apparmor policy to not allow mounts
+
## 1.6.0 (2015-04-07)
#### Builder
diff --git a/VERSION b/VERSION
index dc1e644..9c6d629 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.6.0
+1.6.1
diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go
index a988fba..d278249 100644
--- a/daemon/execdriver/native/create.go
+++ b/daemon/execdriver/native/create.go
@@ -6,12 +6,10 @@
"errors"
"fmt"
"net"
- "path/filepath"
"strings"
"syscall"
"github.com/docker/docker/daemon/execdriver"
- "github.com/docker/docker/pkg/symlink"
"github.com/docker/libcontainer/apparmor"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/devices"
@@ -228,10 +226,6 @@
container.Mounts = defaultMounts
for _, m := range c.Mounts {
- dest, err := symlink.FollowSymlinkInScope(filepath.Join(c.Rootfs, m.Destination), c.Rootfs)
- if err != nil {
- return err
- }
flags := syscall.MS_BIND | syscall.MS_REC
if !m.Writable {
flags |= syscall.MS_RDONLY
@@ -239,10 +233,9 @@
if m.Slave {
flags |= syscall.MS_SLAVE
}
-
container.Mounts = append(container.Mounts, &configs.Mount{
Source: m.Source,
- Destination: dest,
+ Destination: m.Destination,
Device: "bind",
Flags: flags,
})
diff --git a/daemon/execdriver/native/template/default_template.go b/daemon/execdriver/native/template/default_template.go
index 76e3cea..ecedcfc 100644
--- a/daemon/execdriver/native/template/default_template.go
+++ b/daemon/execdriver/native/template/default_template.go
@@ -82,9 +82,16 @@
},
MaskPaths: []string{
"/proc/kcore",
+ "/proc/latency_stats",
+ "/proc/timer_stats",
},
ReadonlyPaths: []string{
- "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
+ "/proc/asound",
+ "/proc/bus",
+ "/proc/fs",
+ "/proc/irq",
+ "/proc/sys",
+ "/proc/sysrq-trigger",
},
}
diff --git a/hack/vendor.sh b/hack/vendor.sh
index bb43771..2a6c409 100755
--- a/hack/vendor.sh
+++ b/hack/vendor.sh
@@ -75,7 +75,7 @@
mkdir -p src/github.com/docker/distribution
mv tmp-digest src/github.com/docker/distribution/digest
-clone git github.com/docker/libcontainer bd8ec36106086f72b66e1be85a81202b93503e44
+clone git github.com/docker/libcontainer 1b471834b45063b61e0aedefbb1739a8f34b414e
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
rm -rf src/github.com/docker/libcontainer/vendor
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli' | grep -v 'github.com/Sirupsen/logrus')"
diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go
index 9224764..9f7f578 100644
--- a/integration-cli/docker_cli_run_test.go
+++ b/integration-cli/docker_cli_run_test.go
@@ -3436,3 +3436,72 @@
}
logDone("run - can kill container with pid-host and some childs of pid 1")
}
+
+func TestRunWithTooSmallMemoryLimit(t *testing.T) {
+ defer deleteAllContainers()
+ // this memory limit is 1 byte less than the min, which is 4MB
+ // https://github.com/docker/docker/blob/v1.5.0/daemon/create.go#L22
+ out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-m", "4194303", "busybox"))
+ if err == nil || !strings.Contains(out, "Minimum memory limit allowed is 4MB") {
+ t.Fatalf("expected run to fail when using too low a memory limit: %q", out)
+ }
+
+ logDone("run - can't set too low memory limit")
+}
+
+func TestRunWriteToProcAsound(t *testing.T) {
+ defer deleteAllContainers()
+ code, err := runCommand(exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "echo 111 >> /proc/asound/version"))
+ if err == nil || code == 0 {
+ t.Fatal("standard container should not be able to write to /proc/asound")
+ }
+ logDone("run - ro write to /proc/asound")
+}
+
+func TestRunReadProcTimer(t *testing.T) {
+ defer deleteAllContainers()
+ out, code, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "busybox", "cat", "/proc/timer_stats"))
+ if err != nil || code != 0 {
+ t.Fatal(err)
+ }
+ if strings.Trim(out, "\n ") != "" {
+ t.Fatalf("expected to receive no output from /proc/timer_stats but received %q", out)
+ }
+ logDone("run - read /proc/timer_stats")
+}
+
+func TestRunReadProcLatency(t *testing.T) {
+ // some kernels don't have this configured so skip the test if this file is not found
+ // on the host running the tests.
+ if _, err := os.Stat("/proc/latency_stats"); err != nil {
+ t.Skip()
+ return
+ }
+ defer deleteAllContainers()
+ out, code, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "busybox", "cat", "/proc/latency_stats"))
+ if err != nil || code != 0 {
+ t.Fatal(err)
+ }
+ if strings.Trim(out, "\n ") != "" {
+ t.Fatalf("expected to receive no output from /proc/latency_stats but received %q", out)
+ }
+ logDone("run - read /proc/latency_stats")
+}
+
+func TestMountIntoProc(t *testing.T) {
+ defer deleteAllContainers()
+ code, err := runCommand(exec.Command(dockerBinary, "run", "-v", "/proc//sys", "busybox", "true"))
+ if err == nil || code == 0 {
+ t.Fatal("container should not be able to mount into /proc")
+ }
+ logDone("run - mount into proc")
+}
+
+func TestMountIntoSys(t *testing.T) {
+ defer deleteAllContainers()
+ code, err := runCommand(exec.Command(dockerBinary, "run", "-v", "/sys/", "busybox", "true"))
+ if err == nil || code == 0 {
+ t.Fatal("container should not be able to mount into /sys")
+ }
+ logDone("run - mount into sys")
+}
diff --git a/vendor/src/github.com/docker/libcontainer/SPEC.md b/vendor/src/github.com/docker/libcontainer/SPEC.md
index d83d758..3ca90d6 100644
--- a/vendor/src/github.com/docker/libcontainer/SPEC.md
+++ b/vendor/src/github.com/docker/libcontainer/SPEC.md
@@ -217,17 +217,6 @@
file,
umount,
- mount fstype=tmpfs,
- mount fstype=mqueue,
- mount fstype=fuse.*,
- mount fstype=binfmt_misc -> /proc/sys/fs/binfmt_misc/,
- mount fstype=efivarfs -> /sys/firmware/efi/efivars/,
- mount fstype=fusectl -> /sys/fs/fuse/connections/,
- mount fstype=securityfs -> /sys/kernel/security/,
- mount fstype=debugfs -> /sys/kernel/debug/,
- mount fstype=proc -> /proc/,
- mount fstype=sysfs -> /sys/,
-
deny @{PROC}/sys/fs/** wklx,
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
@@ -235,9 +224,7 @@
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
deny @{PROC}/sys/kernel/*/** wklx,
- deny mount options=(ro, remount) -> /,
- deny mount fstype=debugfs -> /var/lib/ureadahead/debugfs/,
- deny mount fstype=devpts,
+ deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
diff --git a/vendor/src/github.com/docker/libcontainer/apparmor/gen.go b/vendor/src/github.com/docker/libcontainer/apparmor/gen.go
index 4565f6d..a3192e2 100644
--- a/vendor/src/github.com/docker/libcontainer/apparmor/gen.go
+++ b/vendor/src/github.com/docker/libcontainer/apparmor/gen.go
@@ -27,17 +27,6 @@
file,
umount,
- mount fstype=tmpfs,
- mount fstype=mqueue,
- mount fstype=fuse.*,
- mount fstype=binfmt_misc -> /proc/sys/fs/binfmt_misc/,
- mount fstype=efivarfs -> /sys/firmware/efi/efivars/,
- mount fstype=fusectl -> /sys/fs/fuse/connections/,
- mount fstype=securityfs -> /sys/kernel/security/,
- mount fstype=debugfs -> /sys/kernel/debug/,
- mount fstype=proc -> /proc/,
- mount fstype=sysfs -> /sys/,
-
deny @{PROC}/sys/fs/** wklx,
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
@@ -45,9 +34,7 @@
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
deny @{PROC}/sys/kernel/*/** wklx,
- deny mount options=(ro, remount) -> /,
- deny mount fstype=debugfs -> /var/lib/ureadahead/debugfs/,
- deny mount fstype=devpts,
+ deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
diff --git a/vendor/src/github.com/docker/libcontainer/rootfs_linux.go b/vendor/src/github.com/docker/libcontainer/rootfs_linux.go
index ab1a9a5..7a82edb 100644
--- a/vendor/src/github.com/docker/libcontainer/rootfs_linux.go
+++ b/vendor/src/github.com/docker/libcontainer/rootfs_linux.go
@@ -11,6 +11,7 @@
"syscall"
"time"
+ "github.com/docker/docker/pkg/symlink"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/label"
)
@@ -34,11 +35,6 @@
if err := setupPtmx(config, console); err != nil {
return newSystemError(err)
}
- // stdin, stdout and stderr could be pointing to /dev/null from parent namespace.
- // re-open them inside this namespace.
- if err := reOpenDevNull(config.Rootfs); err != nil {
- return newSystemError(err)
- }
if err := setupDevSymlinks(config.Rootfs); err != nil {
return newSystemError(err)
}
@@ -53,6 +49,9 @@
if err != nil {
return newSystemError(err)
}
+ if err := reOpenDevNull(config.Rootfs); err != nil {
+ return newSystemError(err)
+ }
if config.Readonlyfs {
if err := setReadonly(); err != nil {
return newSystemError(err)
@@ -105,6 +104,16 @@
// unable to bind anything to it.
return err
}
+ // ensure that the destination of the bind mount is resolved of symlinks at mount time because
+ // any previous mounts can invalidate the next mount's destination.
+ // this can happen when a user specifies mounts within other mounts to cause breakouts or other
+ // evil stuff to try to escape the container's rootfs.
+ if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil {
+ return err
+ }
+ if err := checkMountDestination(rootfs, dest); err != nil {
+ return err
+ }
if err := createIfNotExists(dest, stat.IsDir()); err != nil {
return err
}
@@ -132,6 +141,29 @@
return nil
}
+// checkMountDestination checks to ensure that the mount destination is not over the
+// top of /proc or /sys.
+// dest is required to be an abs path and have any symlinks resolved before calling this function.
+func checkMountDestination(rootfs, dest string) error {
+ if filepath.Clean(rootfs) == filepath.Clean(dest) {
+ return fmt.Errorf("mounting into / is prohibited")
+ }
+ invalidDestinations := []string{
+ "/proc",
+ "/sys",
+ }
+ for _, invalid := range invalidDestinations {
+ path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest)
+ if err != nil {
+ return err
+ }
+ if path == "." || !strings.HasPrefix(path, "..") {
+ return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid)
+ }
+ }
+ return nil
+}
+
func setupDevSymlinks(rootfs string) error {
var links = [][2]string{
{"/proc/self/fd", "/dev/fd"},
@@ -156,11 +188,13 @@
return nil
}
-// If stdin, stdout or stderr are pointing to '/dev/null' in the global mount namespace,
-// this method will make them point to '/dev/null' in this namespace.
+// If stdin, stdout, and/or stderr are pointing to `/dev/null` in the parent's rootfs
+// this method will make them point to `/dev/null` in this container's rootfs. This
+// needs to be called after we chroot/pivot into the container's rootfs so that any
+// symlinks are resolved locally.
func reOpenDevNull(rootfs string) error {
var stat, devNullStat syscall.Stat_t
- file, err := os.Open(filepath.Join(rootfs, "/dev/null"))
+ file, err := os.Open("/dev/null")
if err != nil {
return fmt.Errorf("Failed to open /dev/null - %s", err)
}
diff --git a/vendor/src/github.com/docker/libcontainer/rootfs_linux_test.go b/vendor/src/github.com/docker/libcontainer/rootfs_linux_test.go
new file mode 100644
index 0000000..54df065
--- /dev/null
+++ b/vendor/src/github.com/docker/libcontainer/rootfs_linux_test.go
@@ -0,0 +1,37 @@
+// +build linux
+
+package libcontainer
+
+import "testing"
+
+func TestCheckMountDestOnProc(t *testing.T) {
+ dest := "/rootfs/proc/"
+ err := checkMountDestination("/rootfs", dest)
+ if err == nil {
+ t.Fatal("destination inside proc should return an error")
+ }
+}
+
+func TestCheckMountDestInSys(t *testing.T) {
+ dest := "/rootfs//sys/fs/cgroup"
+ err := checkMountDestination("/rootfs", dest)
+ if err == nil {
+ t.Fatal("destination inside proc should return an error")
+ }
+}
+
+func TestCheckMountDestFalsePositive(t *testing.T) {
+ dest := "/rootfs/sysfiles/fs/cgroup"
+ err := checkMountDestination("/rootfs", dest)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestCheckMountRoot(t *testing.T) {
+ dest := "/rootfs"
+ err := checkMountDestination("/rootfs", dest)
+ if err == nil {
+ t.Fatal(err)
+ }
+}