diff options
3 files changed, 203 insertions, 0 deletions
diff --git a/recipes-containers/runc/files/0001-Only-allow-proc-mount-if-it-is-procfs.patch b/recipes-containers/runc/files/0001-Only-allow-proc-mount-if-it-is-procfs.patch new file mode 100644 index 00000000..5aca99e2 --- /dev/null +++ b/recipes-containers/runc/files/0001-Only-allow-proc-mount-if-it-is-procfs.patch | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | From d75b05441772417a0828465a9483f16287937724 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Michael Crosby <crosbymichael@gmail.com> | ||
| 3 | Date: Mon, 23 Sep 2019 16:45:45 -0400 | ||
| 4 | Subject: [PATCH] Only allow proc mount if it is procfs | ||
| 5 | |||
| 6 | Fixes #2128 | ||
| 7 | |||
| 8 | This allows proc to be bind mounted for host and rootless namespace usecases but | ||
| 9 | it removes the ability to mount over the top of proc with a directory. | ||
| 10 | |||
| 11 | ```bash | ||
| 12 | > sudo docker run --rm apparmor | ||
| 13 | docker: Error response from daemon: OCI runtime create failed: | ||
| 14 | container_linux.go:346: starting container process caused "process_linux.go:449: | ||
| 15 | container init caused \"rootfs_linux.go:58: mounting | ||
| 16 | \\\"/var/lib/docker/volumes/aae28ea068c33d60e64d1a75916cf3ec2dc3634f97571854c9ed30c8401460c1/_data\\\" | ||
| 17 | to rootfs | ||
| 18 | \\\"/var/lib/docker/overlay2/a6be5ae911bf19f8eecb23a295dec85be9a8ee8da66e9fb55b47c841d1e381b7/merged\\\" | ||
| 19 | at \\\"/proc\\\" caused | ||
| 20 | \\\"\\\\\\\"/var/lib/docker/overlay2/a6be5ae911bf19f8eecb23a295dec85be9a8ee8da66e9fb55b47c841d1e381b7/merged/proc\\\\\\\" | ||
| 21 | cannot be mounted because it is not of type proc\\\"\"": unknown. | ||
| 22 | |||
| 23 | > sudo docker run --rm -v /proc:/proc apparmor | ||
| 24 | |||
| 25 | docker-default (enforce) root 18989 0.9 0.0 1288 4 ? | ||
| 26 | Ss 16:47 0:00 sleep 20 | ||
| 27 | ``` | ||
| 28 | |||
| 29 | Signed-off-by: Michael Crosby <crosbymichael@gmail.com> | ||
| 30 | |||
| 31 | Upstream-Status: Backport [https://github.com/opencontainers/runc/pull/2129/commits/331692baa7afdf6c186f8667cb0e6362ea0802b3] | ||
| 32 | |||
| 33 | CVE: CVE-2019-16884 | ||
| 34 | |||
| 35 | Signed-off-by: Chen Qi <Qi.Chen@windriver.com> | ||
| 36 | --- | ||
| 37 | libcontainer/container_linux.go | 4 +-- | ||
| 38 | libcontainer/rootfs_linux.go | 50 +++++++++++++++++++++++-------- | ||
| 39 | libcontainer/rootfs_linux_test.go | 8 ++--- | ||
| 40 | 3 files changed, 43 insertions(+), 19 deletions(-) | ||
| 41 | |||
| 42 | diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go | ||
| 43 | index 7e58e5e0..d51e35df 100644 | ||
| 44 | --- a/src/import/libcontainer/container_linux.go | ||
| 45 | +++ b/src/import/libcontainer/container_linux.go | ||
| 46 | @@ -19,7 +19,7 @@ import ( | ||
| 47 | "syscall" // only for SysProcAttr and Signal | ||
| 48 | "time" | ||
| 49 | |||
| 50 | - "github.com/cyphar/filepath-securejoin" | ||
| 51 | + securejoin "github.com/cyphar/filepath-securejoin" | ||
| 52 | "github.com/opencontainers/runc/libcontainer/cgroups" | ||
| 53 | "github.com/opencontainers/runc/libcontainer/configs" | ||
| 54 | "github.com/opencontainers/runc/libcontainer/intelrdt" | ||
| 55 | @@ -1160,7 +1160,7 @@ func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { | ||
| 56 | if err != nil { | ||
| 57 | return err | ||
| 58 | } | ||
| 59 | - if err := checkMountDestination(c.config.Rootfs, dest); err != nil { | ||
| 60 | + if err := checkProcMount(c.config.Rootfs, dest, ""); err != nil { | ||
| 61 | return err | ||
| 62 | } | ||
| 63 | m.Destination = dest | ||
| 64 | diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go | ||
| 65 | index f13b226e..5650b0ac 100644 | ||
| 66 | --- a/src/import/libcontainer/rootfs_linux.go | ||
| 67 | +++ b/src/import/libcontainer/rootfs_linux.go | ||
| 68 | @@ -13,7 +13,7 @@ import ( | ||
| 69 | "strings" | ||
| 70 | "time" | ||
| 71 | |||
| 72 | - "github.com/cyphar/filepath-securejoin" | ||
| 73 | + securejoin "github.com/cyphar/filepath-securejoin" | ||
| 74 | "github.com/mrunalp/fileutils" | ||
| 75 | "github.com/opencontainers/runc/libcontainer/cgroups" | ||
| 76 | "github.com/opencontainers/runc/libcontainer/configs" | ||
| 77 | @@ -197,7 +197,7 @@ func prepareBindMount(m *configs.Mount, rootfs string) error { | ||
| 78 | if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil { | ||
| 79 | return err | ||
| 80 | } | ||
| 81 | - if err := checkMountDestination(rootfs, dest); err != nil { | ||
| 82 | + if err := checkProcMount(rootfs, dest, m.Source); err != nil { | ||
| 83 | return err | ||
| 84 | } | ||
| 85 | // update the mount with the correct dest after symlinks are resolved. | ||
| 86 | @@ -388,7 +388,7 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b | ||
| 87 | if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil { | ||
| 88 | return err | ||
| 89 | } | ||
| 90 | - if err := checkMountDestination(rootfs, dest); err != nil { | ||
| 91 | + if err := checkProcMount(rootfs, dest, m.Source); err != nil { | ||
| 92 | return err | ||
| 93 | } | ||
| 94 | // update the mount with the correct dest after symlinks are resolved. | ||
| 95 | @@ -435,12 +435,12 @@ func getCgroupMounts(m *configs.Mount) ([]*configs.Mount, error) { | ||
| 96 | return binds, nil | ||
| 97 | } | ||
| 98 | |||
| 99 | -// checkMountDestination checks to ensure that the mount destination is not over the top of /proc. | ||
| 100 | +// checkProcMount checks to ensure that the mount destination is not over the top of /proc. | ||
| 101 | // dest is required to be an abs path and have any symlinks resolved before calling this function. | ||
| 102 | -func checkMountDestination(rootfs, dest string) error { | ||
| 103 | - invalidDestinations := []string{ | ||
| 104 | - "/proc", | ||
| 105 | - } | ||
| 106 | +// | ||
| 107 | +// if source is nil, don't stat the filesystem. This is used for restore of a checkpoint. | ||
| 108 | +func checkProcMount(rootfs, dest, source string) error { | ||
| 109 | + const procPath = "/proc" | ||
| 110 | // White list, it should be sub directories of invalid destinations | ||
| 111 | validDestinations := []string{ | ||
| 112 | // These entries can be bind mounted by files emulated by fuse, | ||
| 113 | @@ -463,16 +463,40 @@ func checkMountDestination(rootfs, dest string) error { | ||
| 114 | return nil | ||
| 115 | } | ||
| 116 | } | ||
| 117 | - for _, invalid := range invalidDestinations { | ||
| 118 | - path, err := filepath.Rel(filepath.Join(rootfs, invalid), dest) | ||
| 119 | + path, err := filepath.Rel(filepath.Join(rootfs, procPath), dest) | ||
| 120 | + if err != nil { | ||
| 121 | + return err | ||
| 122 | + } | ||
| 123 | + // pass if the mount path is located outside of /proc | ||
| 124 | + if strings.HasPrefix(path, "..") { | ||
| 125 | + return nil | ||
| 126 | + } | ||
| 127 | + if path == "." { | ||
| 128 | + // an empty source is pasted on restore | ||
| 129 | + if source == "" { | ||
| 130 | + return nil | ||
| 131 | + } | ||
| 132 | + // only allow a mount on-top of proc if it's source is "proc" | ||
| 133 | + isproc, err := isProc(source) | ||
| 134 | if err != nil { | ||
| 135 | return err | ||
| 136 | } | ||
| 137 | - if path != "." && !strings.HasPrefix(path, "..") { | ||
| 138 | - return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid) | ||
| 139 | + // pass if the mount is happening on top of /proc and the source of | ||
| 140 | + // the mount is a proc filesystem | ||
| 141 | + if isproc { | ||
| 142 | + return nil | ||
| 143 | } | ||
| 144 | + return fmt.Errorf("%q cannot be mounted because it is not of type proc", dest) | ||
| 145 | } | ||
| 146 | - return nil | ||
| 147 | + return fmt.Errorf("%q cannot be mounted because it is inside /proc", dest) | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +func isProc(path string) (bool, error) { | ||
| 151 | + var s unix.Statfs_t | ||
| 152 | + if err := unix.Statfs(path, &s); err != nil { | ||
| 153 | + return false, err | ||
| 154 | + } | ||
| 155 | + return s.Type == unix.PROC_SUPER_MAGIC, nil | ||
| 156 | } | ||
| 157 | |||
| 158 | func setupDevSymlinks(rootfs string) error { | ||
| 159 | diff --git a/libcontainer/rootfs_linux_test.go b/libcontainer/rootfs_linux_test.go | ||
| 160 | index d755984b..1bfe7c66 100644 | ||
| 161 | --- a/src/import/libcontainer/rootfs_linux_test.go | ||
| 162 | +++ b/src/import/libcontainer/rootfs_linux_test.go | ||
| 163 | @@ -10,7 +10,7 @@ import ( | ||
| 164 | |||
| 165 | func TestCheckMountDestOnProc(t *testing.T) { | ||
| 166 | dest := "/rootfs/proc/sys" | ||
| 167 | - err := checkMountDestination("/rootfs", dest) | ||
| 168 | + err := checkProcMount("/rootfs", dest, "") | ||
| 169 | if err == nil { | ||
| 170 | t.Fatal("destination inside proc should return an error") | ||
| 171 | } | ||
| 172 | @@ -18,7 +18,7 @@ func TestCheckMountDestOnProc(t *testing.T) { | ||
| 173 | |||
| 174 | func TestCheckMountDestOnProcChroot(t *testing.T) { | ||
| 175 | dest := "/rootfs/proc/" | ||
| 176 | - err := checkMountDestination("/rootfs", dest) | ||
| 177 | + err := checkProcMount("/rootfs", dest, "/proc") | ||
| 178 | if err != nil { | ||
| 179 | t.Fatal("destination inside proc when using chroot should not return an error") | ||
| 180 | } | ||
| 181 | @@ -26,7 +26,7 @@ func TestCheckMountDestOnProcChroot(t *testing.T) { | ||
| 182 | |||
| 183 | func TestCheckMountDestInSys(t *testing.T) { | ||
| 184 | dest := "/rootfs//sys/fs/cgroup" | ||
| 185 | - err := checkMountDestination("/rootfs", dest) | ||
| 186 | + err := checkProcMount("/rootfs", dest, "") | ||
| 187 | if err != nil { | ||
| 188 | t.Fatal("destination inside /sys should not return an error") | ||
| 189 | } | ||
| 190 | @@ -34,7 +34,7 @@ func TestCheckMountDestInSys(t *testing.T) { | ||
| 191 | |||
| 192 | func TestCheckMountDestFalsePositive(t *testing.T) { | ||
| 193 | dest := "/rootfs/sysfiles/fs/cgroup" | ||
| 194 | - err := checkMountDestination("/rootfs", dest) | ||
| 195 | + err := checkProcMount("/rootfs", dest, "") | ||
| 196 | if err != nil { | ||
| 197 | t.Fatal(err) | ||
| 198 | } | ||
| 199 | -- | ||
| 200 | 2.17.1 | ||
| 201 | |||
diff --git a/recipes-containers/runc/runc-docker_git.bb b/recipes-containers/runc/runc-docker_git.bb index c9f460b2..8d810d01 100644 --- a/recipes-containers/runc/runc-docker_git.bb +++ b/recipes-containers/runc/runc-docker_git.bb | |||
| @@ -7,6 +7,7 @@ SRC_URI = "git://github.com/opencontainers/runc;nobranch=1;name=runc-docker \ | |||
| 7 | file://0001-runc-Add-console-socket-dev-null.patch \ | 7 | file://0001-runc-Add-console-socket-dev-null.patch \ |
| 8 | file://0001-Makefile-respect-GOBUILDFLAGS-for-runc-and-remove-re.patch \ | 8 | file://0001-Makefile-respect-GOBUILDFLAGS-for-runc-and-remove-re.patch \ |
| 9 | file://0001-runc-docker-SIGUSR1-daemonize.patch \ | 9 | file://0001-runc-docker-SIGUSR1-daemonize.patch \ |
| 10 | file://0001-Only-allow-proc-mount-if-it-is-procfs.patch \ | ||
| 10 | " | 11 | " |
| 11 | 12 | ||
| 12 | RUNC_VERSION = "1.0.0-rc8" | 13 | RUNC_VERSION = "1.0.0-rc8" |
diff --git a/recipes-containers/runc/runc-opencontainers_git.bb b/recipes-containers/runc/runc-opencontainers_git.bb index 361bc94b..3a7e7aaf 100644 --- a/recipes-containers/runc/runc-opencontainers_git.bb +++ b/recipes-containers/runc/runc-opencontainers_git.bb | |||
| @@ -4,5 +4,6 @@ SRCREV = "652297c7c7e6c94e8d064ad5916c32891a6fd388" | |||
| 4 | SRC_URI = " \ | 4 | SRC_URI = " \ |
| 5 | git://github.com/opencontainers/runc;branch=master \ | 5 | git://github.com/opencontainers/runc;branch=master \ |
| 6 | file://0001-Makefile-respect-GOBUILDFLAGS-for-runc-and-remove-re.patch \ | 6 | file://0001-Makefile-respect-GOBUILDFLAGS-for-runc-and-remove-re.patch \ |
| 7 | file://0001-Only-allow-proc-mount-if-it-is-procfs.patch \ | ||
| 7 | " | 8 | " |
| 8 | RUNC_VERSION = "1.0.0-rc8" | 9 | RUNC_VERSION = "1.0.0-rc8" |
