runc: update patches
0118-runc-don-t-deny-all-devices-when-update-cgroup-resou.patch 0119-runc-rootfs-do-not-permit-proc-mounts-to-no.patch 0120-runc-fix-permission-denied.patch Signed-off-by: xiadanni <xiadanni1@huawei.com>
This commit is contained in:
parent
fabf83fd21
commit
874ff09e56
@ -0,0 +1,180 @@
|
||||
From eb576e28687555ba9253a2679706bf5491be0c11 Mon Sep 17 00:00:00 2001
|
||||
From: panwenxiang <panwenxiang@huawei.com>
|
||||
Date: Fri, 24 Apr 2020 23:05:31 +0800
|
||||
Subject: [PATCH] runc: don't deny all devices when update cgroup resource
|
||||
|
||||
reason: runc Update command causes 'Operation not permitted
|
||||
|
||||
cherry-pick from https://github.com/opencontainers/runc/pull/2204
|
||||
cherry-pick from https://github.com/opencontainers/runc/pull/2205
|
||||
|
||||
It's first seen in a kubernetes cluster with docker as container runtime.
|
||||
Our users reported that in some situation their bash script failed with message
|
||||
can't create /dev/null: Operation not permitted.
|
||||
But /dev/null is default device with permission rwm,
|
||||
After digging some logs, we found that it can be reproduced in runc by following steps.
|
||||
|
||||
- Run a runc container like "busybox". Suppose this container is called A
|
||||
- run while true;do echo >/dev/null;done in container
|
||||
- runc update --cpu-share 1024 A
|
||||
- You will see sh: can't create /dev/null: Operation not permitted
|
||||
|
||||
Change-Id: I4f374eed5033b9f3eb47c31b622c408d24142473
|
||||
Signed-off-by: panwenxiang <panwenxiang@huawei.com>
|
||||
---
|
||||
libcontainer/cgroups/fs/devices.go | 72 +++++++++++++++++++++----
|
||||
libcontainer/cgroups/fs/devices_test.go | 8 +++
|
||||
2 files changed, 71 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/libcontainer/cgroups/fs/devices.go b/libcontainer/cgroups/fs/devices.go
|
||||
index 478b5db7..c5ca11f5 100644
|
||||
--- a/libcontainer/cgroups/fs/devices.go
|
||||
+++ b/libcontainer/cgroups/fs/devices.go
|
||||
@@ -10,11 +10,18 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
+ "strings"
|
||||
)
|
||||
|
||||
type DevicesGroup struct {
|
||||
}
|
||||
|
||||
+type Empty struct{}
|
||||
+
|
||||
+var (
|
||||
+ defaultDevice = &configs.Device{Type: 'a', Major: -1, Minor: -1, Permissions: "rwm"}
|
||||
+)
|
||||
+
|
||||
func (s *DevicesGroup) Name() string {
|
||||
return "devices"
|
||||
}
|
||||
@@ -61,6 +68,10 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
|
||||
return err
|
||||
}
|
||||
|
||||
+ oldAllowedDevices, err := readDevicesExceptDefault(path)
|
||||
+ if err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
devices := cgroup.Resources.Devices
|
||||
if len(devices) > 0 {
|
||||
for _, dev := range devices {
|
||||
@@ -68,10 +79,17 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
|
||||
if dev.Allow {
|
||||
file = "devices.allow"
|
||||
}
|
||||
- if err := writeFile(path, file, dev.CgroupString()); err != nil {
|
||||
- return err
|
||||
+ // For the second time set, we don't deny all devices, skip
|
||||
+ if dev.Type == defaultDevice.Type && len(oldAllowedDevices) != 0 {
|
||||
+ file = ""
|
||||
+ }
|
||||
+
|
||||
+ if len(file) > 0 {
|
||||
+ if err := writeFile(path, file, dev.CgroupString()); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ delete(deviceMap, dev.CgroupString())
|
||||
}
|
||||
- delete(deviceMap, dev.CgroupString())
|
||||
}
|
||||
for item, _ := range deviceMap {
|
||||
if item[0] == 'b' || item[0] == 'c' {
|
||||
@@ -84,13 +102,31 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
|
||||
}
|
||||
if cgroup.Resources.AllowAllDevices != nil {
|
||||
if *cgroup.Resources.AllowAllDevices == false {
|
||||
- if err := writeFile(path, "devices.deny", "a"); err != nil {
|
||||
- return err
|
||||
+ if len(oldAllowedDevices) == 0 {
|
||||
+ if err := writeFile(path, "devices.deny", "a"); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
}
|
||||
|
||||
- for _, dev := range cgroup.Resources.AllowedDevices {
|
||||
- if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil {
|
||||
- return err
|
||||
+ newAllowedDevices := make(map[string]Empty)
|
||||
+ for _, dev := range cgroup.AllowedDevices {
|
||||
+ newAllowedDevices[dev.CgroupString()] = Empty{}
|
||||
+ }
|
||||
+ // Deny no longer allowed devices
|
||||
+ for cgroupString := range oldAllowedDevices {
|
||||
+ if _, found := newAllowedDevices[cgroupString]; !found {
|
||||
+ if err := writeFile(path, "devices.deny", cgroupString); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Allow new devices
|
||||
+ for cgroupString := range newAllowedDevices {
|
||||
+ if _, found := oldAllowedDevices[cgroupString]; !found {
|
||||
+ if err := writeFile(path, "devices.allow", cgroupString); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -100,7 +136,6 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
-
|
||||
for _, dev := range cgroup.Resources.DeniedDevices {
|
||||
if err := writeFile(path, "devices.deny", dev.CgroupString()); err != nil {
|
||||
return err
|
||||
@@ -117,3 +152,22 @@ func (s *DevicesGroup) Remove(d *cgroupData) error {
|
||||
func (s *DevicesGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
+
|
||||
+func readDevicesExceptDefault(path string) (allowed map[string]Empty, err error) {
|
||||
+ cgroupData, err := readFile(path, "devices.list")
|
||||
+ if err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
+
|
||||
+ allowedDevices := make(map[string]Empty)
|
||||
+ defaultDeviceString := defaultDevice.CgroupString()
|
||||
+ for _, data := range strings.Split(cgroupData, "\n") {
|
||||
+ // skip allow all devices
|
||||
+ if len(data) == 0 || data == defaultDeviceString {
|
||||
+ continue
|
||||
+ }
|
||||
+ allowedDevices[data] = Empty{}
|
||||
+ }
|
||||
+
|
||||
+ return allowedDevices, nil
|
||||
+}
|
||||
diff --git a/libcontainer/cgroups/fs/devices_test.go b/libcontainer/cgroups/fs/devices_test.go
|
||||
index fc635b99..1184c459 100644
|
||||
--- a/libcontainer/cgroups/fs/devices_test.go
|
||||
+++ b/libcontainer/cgroups/fs/devices_test.go
|
||||
@@ -37,6 +37,10 @@ func TestDevicesSetAllow(t *testing.T) {
|
||||
helper := NewCgroupTestUtil("devices", t)
|
||||
defer helper.cleanup()
|
||||
|
||||
+ helper.writeFileContents(map[string]string{
|
||||
+ "devices.list": "a *:* rwm",
|
||||
+ })
|
||||
+
|
||||
helper.writeFileContents(map[string]string{
|
||||
"devices.deny": "a",
|
||||
})
|
||||
@@ -75,6 +79,10 @@ func TestDevicesSetDeny(t *testing.T) {
|
||||
helper := NewCgroupTestUtil("devices", t)
|
||||
defer helper.cleanup()
|
||||
|
||||
+ helper.writeFileContents(map[string]string{
|
||||
+ "devices.list": "a *:* rwm",
|
||||
+ })
|
||||
+
|
||||
helper.writeFileContents(map[string]string{
|
||||
"devices.allow": "a",
|
||||
})
|
||||
--
|
||||
2.21.0
|
||||
|
||||
49
patch/0119-runc-rootfs-do-not-permit-proc-mounts-to-no.patch
Normal file
49
patch/0119-runc-rootfs-do-not-permit-proc-mounts-to-no.patch
Normal file
@ -0,0 +1,49 @@
|
||||
From 78b4cdf24a75950da64dab9146984b448497cc28 Mon Sep 17 00:00:00 2001
|
||||
From: xiadanni1 <xiadanni1@huawei.com>
|
||||
Date: Wed, 15 Apr 2020 16:58:02 +0800
|
||||
Subject: [PATCH] rootfs: do not permit /proc mounts to non-directories
|
||||
|
||||
mount(2) will blindly follow symlinks, which is a problem because it
|
||||
allows a malicious container to trick runc into mounting /proc to an
|
||||
entirely different location (and thus within the attacker's control for
|
||||
a rename-exchange attack).
|
||||
|
||||
This is just a hotfix (to "stop the bleeding"), and the more complete
|
||||
fix would be finish libpathrs and port runc to it (to avoid these types
|
||||
of attacks entirely, and defend against a variety of other /proc-related
|
||||
attacks). It can be bypased by someone having "/" be a volume controlled
|
||||
by another container.
|
||||
|
||||
Fixes: CVE-2019-19921
|
||||
Signed-off-by: Aleksa Sarai <asarai@suse.de>
|
||||
Signed-off-by: xiadanni1 <xiadanni1@huawei.com>
|
||||
---
|
||||
libcontainer/rootfs_linux.go | 12 ++++++++++++
|
||||
1 file changed, 12 insertions(+)
|
||||
|
||||
diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
|
||||
index 4c18482..67cf0bf 100644
|
||||
--- a/libcontainer/rootfs_linux.go
|
||||
+++ b/libcontainer/rootfs_linux.go
|
||||
@@ -168,6 +168,18 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
|
||||
|
||||
switch m.Device {
|
||||
case "proc", "sysfs":
|
||||
+ // If the destination already exists and is not a directory, we bail
|
||||
+ // out This is to avoid mounting through a symlink or similar -- which
|
||||
+ // has been a "fun" attack scenario in the past.
|
||||
+ // TODO: This won't be necessary once we switch to libpathrs and we can
|
||||
+ // stop all of these symlink-exchange attacks.
|
||||
+ if fi, err := os.Lstat(dest); err != nil {
|
||||
+ if !os.IsNotExist(err) {
|
||||
+ return err
|
||||
+ }
|
||||
+ } else if fi.Mode()&os.ModeDir == 0 {
|
||||
+ return fmt.Errorf("filesystem %q must be mounted on ordinary directory", m.Device)
|
||||
+ }
|
||||
if strings.HasPrefix(m.Destination, "/proc/sys/") {
|
||||
return nil
|
||||
}
|
||||
--
|
||||
1.8.3.1
|
||||
|
||||
45
patch/0120-runc-fix-permission-denied.patch
Normal file
45
patch/0120-runc-fix-permission-denied.patch
Normal file
@ -0,0 +1,45 @@
|
||||
From 6594d5c042a2253386820a640b3a7087e07d0df2 Mon Sep 17 00:00:00 2001
|
||||
From: xiadanni <xiadanni1@huawei.com>
|
||||
Date: Thu, 9 Jul 2020 15:56:54 +0800
|
||||
Subject: [PATCH] runc: fix permission denied
|
||||
|
||||
reason: when exec as root and config.Cwd is not owned by root,
|
||||
exec will fail because root doesn't have the caps.
|
||||
|
||||
Signed-off-by: Kurnia D Win <kurnia.d.win@gmail.com>
|
||||
Signed-off-by: xiadanni <xiadanni1@huawei.com>
|
||||
---
|
||||
libcontainer/init_linux.go | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go
|
||||
index 2a93431..73505ef 100644
|
||||
--- a/libcontainer/init_linux.go
|
||||
+++ b/libcontainer/init_linux.go
|
||||
@@ -118,6 +118,11 @@ func finalizeNamespace(config *initConfig) error {
|
||||
if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
|
||||
return err
|
||||
}
|
||||
+ if config.Cwd != "" {
|
||||
+ if err := syscall.Chdir(config.Cwd); err != nil {
|
||||
+ return fmt.Errorf("chdir to cwd (%q) set in config.json failed: %v", config.Cwd, err)
|
||||
+ }
|
||||
+ }
|
||||
|
||||
capabilities := &configs.Capabilities{}
|
||||
if config.Capabilities != nil {
|
||||
@@ -146,11 +151,6 @@ func finalizeNamespace(config *initConfig) error {
|
||||
if err := w.ApplyCaps(); err != nil {
|
||||
return err
|
||||
}
|
||||
- if config.Cwd != "" {
|
||||
- if err := syscall.Chdir(config.Cwd); err != nil {
|
||||
- return fmt.Errorf("chdir to cwd (%q) set in config.json failed: %v", config.Cwd, err)
|
||||
- }
|
||||
- }
|
||||
return nil
|
||||
}
|
||||
|
||||
--
|
||||
1.8.3.1
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Name: docker-runc
|
||||
Version: 1.0.0.rc3
|
||||
Release: 103
|
||||
Release: 104
|
||||
Summary: runc is a CLI tool for spawning and running containers according to the OCI specification.
|
||||
|
||||
License: ASL 2.0
|
||||
|
||||
@ -114,3 +114,6 @@
|
||||
0115-runc-Fix-cgroup-hugetlb-size-prefix-for-kB.patch
|
||||
0116-runc-check-nil-pointers-in-cgroup-manager.patch
|
||||
0117-runc-Pass-back-the-pid-of-runc-1-CHILD-so-w.patch
|
||||
0118-runc-don-t-deny-all-devices-when-update-cgroup-resou.patch
|
||||
0119-runc-rootfs-do-not-permit-proc-mounts-to-no.patch
|
||||
0120-runc-fix-permission-denied.patch
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user