From 16d03e20527cb4e56984907eddf318d02f4f5d09 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jun 10 2021 07:47:16 +0000 Subject: import runc-1.0.0-73.rc93.module+el8.4.0+11311+9da8acfb --- diff --git a/.gitignore b/.gitignore index ac15d58..7668d0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/v1.0.0-rc92.tar.gz +SOURCES/v1.0.0-rc93.tar.gz diff --git a/.runc.metadata b/.runc.metadata index 85883d8..0a7c604 100644 --- a/.runc.metadata +++ b/.runc.metadata @@ -1 +1 @@ -b5571f41bcc85be33a56122a30cb1a241476a8d1 SOURCES/v1.0.0-rc92.tar.gz +e8693109441696536710e5751e0fee6e6fa32590 SOURCES/v1.0.0-rc93.tar.gz diff --git a/SOURCES/rc93-0001-libct-newInitConfig-nit.patch b/SOURCES/rc93-0001-libct-newInitConfig-nit.patch new file mode 100644 index 0000000..2040586 --- /dev/null +++ b/SOURCES/rc93-0001-libct-newInitConfig-nit.patch @@ -0,0 +1,40 @@ +From 28daf0653d324fad545a7031e64b6891f399969b Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Tue, 23 Feb 2021 17:58:07 -0800 +Subject: [PATCH 1/5] libct/newInitConfig: nit + +Move the initialization of Console* fields as they are unconditional. + +Signed-off-by: Kir Kolyshkin +--- + libcontainer/container_linux.go | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go +index 3dca29e4c3f2..b6100aae9d5a 100644 +--- a/libcontainer/container_linux.go ++++ b/libcontainer/container_linux.go +@@ -594,6 +594,9 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { + AppArmorProfile: c.config.AppArmorProfile, + ProcessLabel: c.config.ProcessLabel, + Rlimits: c.config.Rlimits, ++ CreateConsole: process.ConsoleSocket != nil, ++ ConsoleWidth: process.ConsoleWidth, ++ ConsoleHeight: process.ConsoleHeight, + } + if process.NoNewPrivileges != nil { + cfg.NoNewPrivileges = *process.NoNewPrivileges +@@ -607,9 +610,7 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { + if len(process.Rlimits) > 0 { + cfg.Rlimits = process.Rlimits + } +- cfg.CreateConsole = process.ConsoleSocket != nil +- cfg.ConsoleWidth = process.ConsoleWidth +- cfg.ConsoleHeight = process.ConsoleHeight ++ + return cfg + } + +-- +2.31.1 + diff --git a/SOURCES/rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch b/SOURCES/rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch new file mode 100644 index 0000000..3df9b15 --- /dev/null +++ b/SOURCES/rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch @@ -0,0 +1,139 @@ +From 46ec7b5a94d370c4963ca361e9d96cb78d75d118 Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Tue, 23 Feb 2021 18:14:37 -0800 +Subject: [PATCH 2/5] libct/rootfs: introduce and use mountConfig + +The code is already passing three parameters around from +mountToRootfs to mountCgroupV* to mountToRootfs again. + +I am about to add another parameter, so let's introduce and +use struct mountConfig to pass around. + +Signed-off-by: Kir Kolyshkin +--- + libcontainer/rootfs_linux.go | 42 ++++++++++++++++++++++-------------- + 1 file changed, 26 insertions(+), 16 deletions(-) + +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index 411496ab7c6d..a384abb7e8a5 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -29,6 +29,12 @@ import ( + + const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV + ++type mountConfig struct { ++ root string ++ label string ++ cgroupns bool ++} ++ + // needsSetupDev returns true if /dev needs to be set up. + func needsSetupDev(config *configs.Config) bool { + for _, m := range config.Mounts { +@@ -48,7 +54,11 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) { + return newSystemErrorWithCause(err, "preparing rootfs") + } + +- hasCgroupns := config.Namespaces.Contains(configs.NEWCGROUP) ++ mountConfig := &mountConfig{ ++ root: config.Rootfs, ++ label: config.MountLabel, ++ cgroupns: config.Namespaces.Contains(configs.NEWCGROUP), ++ } + setupDev := needsSetupDev(config) + for _, m := range config.Mounts { + for _, precmd := range m.PremountCmds { +@@ -56,7 +66,7 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) { + return newSystemErrorWithCause(err, "running premount command") + } + } +- if err := mountToRootfs(m, config.Rootfs, config.MountLabel, hasCgroupns); err != nil { ++ if err := mountToRootfs(m, mountConfig); err != nil { + return newSystemErrorWithCausef(err, "mounting %q to rootfs at %q", m.Source, m.Destination) + } + +@@ -222,7 +232,7 @@ func prepareBindMount(m *configs.Mount, rootfs string) error { + return nil + } + +-func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns bool) error { ++func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + binds, err := getCgroupMounts(m) + if err != nil { + return err +@@ -242,12 +252,12 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b + Data: "mode=755", + PropagationFlags: m.PropagationFlags, + } +- if err := mountToRootfs(tmpfs, rootfs, mountLabel, enableCgroupns); err != nil { ++ if err := mountToRootfs(tmpfs, c); err != nil { + return err + } + for _, b := range binds { +- if enableCgroupns { +- subsystemPath := filepath.Join(rootfs, b.Destination) ++ if c.cgroupns { ++ subsystemPath := filepath.Join(c.root, b.Destination) + if err := os.MkdirAll(subsystemPath, 0755); err != nil { + return err + } +@@ -266,7 +276,7 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b + return err + } + } else { +- if err := mountToRootfs(b, rootfs, mountLabel, enableCgroupns); err != nil { ++ if err := mountToRootfs(b, c); err != nil { + return err + } + } +@@ -276,7 +286,7 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b + // symlink(2) is very dumb, it will just shove the path into + // the link and doesn't do any checks or relative path + // conversion. Also, don't error out if the cgroup already exists. +- if err := os.Symlink(mc, filepath.Join(rootfs, m.Destination, ss)); err != nil && !os.IsExist(err) { ++ if err := os.Symlink(mc, filepath.Join(c.root, m.Destination, ss)); err != nil && !os.IsExist(err) { + return err + } + } +@@ -284,8 +294,8 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b + return nil + } + +-func mountCgroupV2(m *configs.Mount, rootfs, mountLabel string, enableCgroupns bool) error { +- cgroupPath, err := securejoin.SecureJoin(rootfs, m.Destination) ++func mountCgroupV2(m *configs.Mount, c *mountConfig) error { ++ cgroupPath, err := securejoin.SecureJoin(c.root, m.Destination) + if err != nil { + return err + } +@@ -302,10 +312,10 @@ func mountCgroupV2(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b + return nil + } + +-func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns bool) error { +- var ( +- dest = m.Destination +- ) ++func mountToRootfs(m *configs.Mount, c *mountConfig) error { ++ rootfs := c.root ++ mountLabel := c.label ++ dest := m.Destination + if !strings.HasPrefix(dest, rootfs) { + dest = filepath.Join(rootfs, dest) + } +@@ -424,9 +434,9 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b + } + case "cgroup": + if cgroups.IsCgroup2UnifiedMode() { +- return mountCgroupV2(m, rootfs, mountLabel, enableCgroupns) ++ return mountCgroupV2(m, c) + } +- return mountCgroupV1(m, rootfs, mountLabel, enableCgroupns) ++ return mountCgroupV1(m, c) + default: + // ensure that the destination of the mount is resolved of symlinks at mount time because + // any previous mounts can invalidate the next mount's destination. +-- +2.31.1 + diff --git a/SOURCES/rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch b/SOURCES/rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch new file mode 100644 index 0000000..2ded660 --- /dev/null +++ b/SOURCES/rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch @@ -0,0 +1,52 @@ +From 198a2806b0b5522cff1c53bf4671cfee85e45608 Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Tue, 23 Feb 2021 18:25:56 -0800 +Subject: [PATCH 3/5] libct/rootfs/mountCgroupV2: minor refactor + +1. s/cgroupPath/dest/ + +2. don't hardcode /sys/fs/cgroup + +Signed-off-by: Kir Kolyshkin +--- + libcontainer/rootfs_linux.go | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index a384abb7e8a5..0f0495b93b3e 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -17,6 +17,7 @@ import ( + "github.com/moby/sys/mountinfo" + "github.com/mrunalp/fileutils" + "github.com/opencontainers/runc/libcontainer/cgroups" ++ "github.com/opencontainers/runc/libcontainer/cgroups/fs2" + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/devices" + "github.com/opencontainers/runc/libcontainer/system" +@@ -295,17 +296,18 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + } + + func mountCgroupV2(m *configs.Mount, c *mountConfig) error { +- cgroupPath, err := securejoin.SecureJoin(c.root, m.Destination) ++ dest, err := securejoin.SecureJoin(c.root, m.Destination) + if err != nil { + return err + } +- if err := os.MkdirAll(cgroupPath, 0755); err != nil { ++ if err := os.MkdirAll(dest, 0755); err != nil { + return err + } +- if err := unix.Mount(m.Source, cgroupPath, "cgroup2", uintptr(m.Flags), m.Data); err != nil { ++ if err := unix.Mount(m.Source, dest, "cgroup2", uintptr(m.Flags), m.Data); err != nil { + // when we are in UserNS but CgroupNS is not unshared, we cannot mount cgroup2 (#2158) + if err == unix.EPERM || err == unix.EBUSY { +- return unix.Mount("/sys/fs/cgroup", cgroupPath, "", uintptr(m.Flags)|unix.MS_BIND, "") ++ src := fs2.UnifiedMountpoint ++ return unix.Mount(src, dest, "", uintptr(m.Flags)|unix.MS_BIND, "") + } + return err + } +-- +2.31.1 + diff --git a/SOURCES/rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch b/SOURCES/rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch new file mode 100644 index 0000000..971ded9 --- /dev/null +++ b/SOURCES/rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch @@ -0,0 +1,180 @@ +From ce352accdfb07a91b5527e70ec8bce658a8b68de Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Tue, 23 Feb 2021 18:27:42 -0800 +Subject: [PATCH 4/5] Fix cgroup2 mount for rootless case + +In case of rootless, cgroup2 mount is not possible (see [1] for more +details), so since commit 9c81440fb5a7 runc bind-mounts the whole +/sys/fs/cgroup into container. + +Problem is, if cgroupns is enabled, /sys/fs/cgroup inside the container +is supposed to show the cgroup files for this cgroup, not the root one. + +The fix is to pass through and use the cgroup path in case cgroup2 +mount failed, cgroupns is enabled, and the path is non-empty. + +Surely this requires the /sys/fs/cgroup mount in the spec, so modify +runc spec --rootless to keep it. + +Before: + + $ ./runc run aaa + # find /sys/fs/cgroup/ -type d + /sys/fs/cgroup + /sys/fs/cgroup/user.slice + /sys/fs/cgroup/user.slice/user-1000.slice + /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service + ... + # ls -l /sys/fs/cgroup/cgroup.controllers + -r--r--r-- 1 nobody nogroup 0 Feb 24 02:22 /sys/fs/cgroup/cgroup.controllers + # wc -w /sys/fs/cgroup/cgroup.procs + 142 /sys/fs/cgroup/cgroup.procs + # cat /sys/fs/cgroup/memory.current + cat: can't open '/sys/fs/cgroup/memory.current': No such file or directory + +After: + + # find /sys/fs/cgroup/ -type d + /sys/fs/cgroup/ + # ls -l /sys/fs/cgroup/cgroup.controllers + -r--r--r-- 1 root root 0 Feb 24 02:43 /sys/fs/cgroup/cgroup.controllers + # wc -w /sys/fs/cgroup/cgroup.procs + 2 /sys/fs/cgroup/cgroup.procs + # cat /sys/fs/cgroup/memory.current + 577536 + +[1] https://github.com/opencontainers/runc/issues/2158 + +Signed-off-by: Kir Kolyshkin +--- + libcontainer/container_linux.go | 3 +++ + libcontainer/init_linux.go | 1 + + libcontainer/rootfs_linux.go | 28 +++++++++++++++++++++------- + libcontainer/specconv/example.go | 18 +++++++++--------- + 4 files changed, 34 insertions(+), 16 deletions(-) + +diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go +index b6100aae9d5a..1cbc734172d0 100644 +--- a/libcontainer/container_linux.go ++++ b/libcontainer/container_linux.go +@@ -610,6 +610,9 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { + if len(process.Rlimits) > 0 { + cfg.Rlimits = process.Rlimits + } ++ if cgroups.IsCgroup2UnifiedMode() { ++ cfg.Cgroup2Path = c.cgroupManager.Path("") ++ } + + return cfg + } +diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go +index c57af0eebb8b..681797099f46 100644 +--- a/libcontainer/init_linux.go ++++ b/libcontainer/init_linux.go +@@ -70,6 +70,7 @@ type initConfig struct { + RootlessEUID bool `json:"rootless_euid,omitempty"` + RootlessCgroups bool `json:"rootless_cgroups,omitempty"` + SpecState *specs.State `json:"spec_state,omitempty"` ++ Cgroup2Path string `json:"cgroup2_path,omitempty"` + } + + type initer interface { +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index 0f0495b93b3e..5d2d74cf924b 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -31,9 +31,11 @@ import ( + const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV + + type mountConfig struct { +- root string +- label string +- cgroupns bool ++ root string ++ label string ++ cgroup2Path string ++ rootlessCgroups bool ++ cgroupns bool + } + + // needsSetupDev returns true if /dev needs to be set up. +@@ -56,9 +58,11 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) { + } + + mountConfig := &mountConfig{ +- root: config.Rootfs, +- label: config.MountLabel, +- cgroupns: config.Namespaces.Contains(configs.NEWCGROUP), ++ root: config.Rootfs, ++ label: config.MountLabel, ++ cgroup2Path: iConfig.Cgroup2Path, ++ rootlessCgroups: iConfig.RootlessCgroups, ++ cgroupns: config.Namespaces.Contains(configs.NEWCGROUP), + } + setupDev := needsSetupDev(config) + for _, m := range config.Mounts { +@@ -307,7 +311,17 @@ func mountCgroupV2(m *configs.Mount, c *mountConfig) error { + // when we are in UserNS but CgroupNS is not unshared, we cannot mount cgroup2 (#2158) + if err == unix.EPERM || err == unix.EBUSY { + src := fs2.UnifiedMountpoint +- return unix.Mount(src, dest, "", uintptr(m.Flags)|unix.MS_BIND, "") ++ if c.cgroupns && c.cgroup2Path != "" { ++ // Emulate cgroupns by bind-mounting ++ // the container cgroup path rather than ++ // the whole /sys/fs/cgroup. ++ src = c.cgroup2Path ++ } ++ err = unix.Mount(src, dest, "", uintptr(m.Flags)|unix.MS_BIND, "") ++ if err == unix.ENOENT && c.rootlessCgroups { ++ err = nil ++ } ++ return err + } + return err + } +diff --git a/libcontainer/specconv/example.go b/libcontainer/specconv/example.go +index 8a201bc78dd9..56bab3bfbfa5 100644 +--- a/libcontainer/specconv/example.go ++++ b/libcontainer/specconv/example.go +@@ -2,6 +2,7 @@ package specconv + + import ( + "os" ++ "path/filepath" + "strings" + + "github.com/opencontainers/runc/libcontainer/cgroups" +@@ -200,8 +201,14 @@ func ToRootless(spec *specs.Spec) { + // Fix up mounts. + var mounts []specs.Mount + for _, mount := range spec.Mounts { +- // Ignore all mounts that are under /sys. +- if strings.HasPrefix(mount.Destination, "/sys") { ++ // Replace the /sys mount with an rbind. ++ if filepath.Clean(mount.Destination) == "/sys" { ++ mounts = append(mounts, specs.Mount{ ++ Source: "/sys", ++ Destination: "/sys", ++ Type: "none", ++ Options: []string{"rbind", "nosuid", "noexec", "nodev", "ro"}, ++ }) + continue + } + +@@ -216,13 +223,6 @@ func ToRootless(spec *specs.Spec) { + mount.Options = options + mounts = append(mounts, mount) + } +- // Add the sysfs mount as an rbind. +- mounts = append(mounts, specs.Mount{ +- Source: "/sys", +- Destination: "/sys", +- Type: "none", +- Options: []string{"rbind", "nosuid", "noexec", "nodev", "ro"}, +- }) + spec.Mounts = mounts + + // Remove cgroup settings. +-- +2.31.1 + diff --git a/SOURCES/rc93-0005-rootfs-add-mount-destination-validation.patch b/SOURCES/rc93-0005-rootfs-add-mount-destination-validation.patch new file mode 100644 index 0000000..6236232 --- /dev/null +++ b/SOURCES/rc93-0005-rootfs-add-mount-destination-validation.patch @@ -0,0 +1,562 @@ +From 14faf1c20948688a48edb9b41367ab07ac11ca91 Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Thu, 1 Apr 2021 12:00:31 -0700 +Subject: [PATCH 5/5] rootfs: add mount destination validation + +Because the target of a mount is inside a container (which may be a +volume that is shared with another container), there exists a race +condition where the target of the mount may change to a path containing +a symlink after we have sanitised the path -- resulting in us +inadvertently mounting the path outside of the container. + +This is not immediately useful because we are in a mount namespace with +MS_SLAVE mount propagation applied to "/", so we cannot mount on top of +host paths in the host namespace. However, if any subsequent mountpoints +in the configuration use a subdirectory of that host path as a source, +those subsequent mounts will use an attacker-controlled source path +(resolved within the host rootfs) -- allowing the bind-mounting of "/" +into the container. + +While arguably configuration issues like this are not entirely within +runc's threat model, within the context of Kubernetes (and possibly +other container managers that provide semi-arbitrary container creation +privileges to untrusted users) this is a legitimate issue. Since we +cannot block mounting from the host into the container, we need to block +the first stage of this attack (mounting onto a path outside the +container). + +The long-term plan to solve this would be to migrate to libpathrs, but +as a stop-gap we implement libpathrs-like path verification through +readlink(/proc/self/fd/$n) and then do mount operations through the +procfd once it's been verified to be inside the container. The target +could move after we've checked it, but if it is inside the container +then we can assume that it is safe for the same reason that libpathrs +operations would be safe. + +A slight wrinkle is the "copyup" functionality we provide for tmpfs, +which is the only case where we want to do a mount on the host +filesystem. To facilitate this, I split out the copy-up functionality +entirely so that the logic isn't interspersed with the regular tmpfs +logic. In addition, all dependencies on m.Destination being overwritten +have been removed since that pattern was just begging to be a source of +more mount-target bugs (we do still have to modify m.Destination for +tmpfs-copyup but we only do it temporarily). + +Fixes: CVE-2021-30465 +Reported-by: Etienne Champetier +Co-authored-by: Noah Meyerhans +Reviewed-by: Samuel Karp +Reviewed-by: Kir Kolyshkin (@kolyshkin) +Reviewed-by: Akihiro Suda +Signed-off-by: Aleksa Sarai +--- + libcontainer/container_linux.go | 1 - + libcontainer/rootfs_linux.go | 251 +++++++++++++++---------------- + libcontainer/utils/utils.go | 54 +++++++ + libcontainer/utils/utils_test.go | 35 +++++ + 4 files changed, 213 insertions(+), 128 deletions(-) + +diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go +index 1cbc734172d0..70b388b1252e 100644 +--- a/libcontainer/container_linux.go ++++ b/libcontainer/container_linux.go +@@ -1202,7 +1202,6 @@ func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { + if err := checkProcMount(c.config.Rootfs, dest, ""); err != nil { + return err + } +- m.Destination = dest + if err := os.MkdirAll(dest, 0755); err != nil { + return err + } +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index 5d2d74cf924b..96be669c365e 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -25,6 +25,7 @@ import ( + libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/selinux/go-selinux/label" ++ "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + ) + +@@ -228,8 +229,6 @@ func prepareBindMount(m *configs.Mount, rootfs string) error { + if err := checkProcMount(rootfs, dest, m.Source); err != nil { + return err + } +- // update the mount with the correct dest after symlinks are resolved. +- m.Destination = dest + if err := createIfNotExists(dest, stat.IsDir()); err != nil { + return err + } +@@ -266,18 +265,21 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + if err := os.MkdirAll(subsystemPath, 0755); err != nil { + return err + } +- flags := defaultMountFlags +- if m.Flags&unix.MS_RDONLY != 0 { +- flags = flags | unix.MS_RDONLY +- } +- cgroupmount := &configs.Mount{ +- Source: "cgroup", +- Device: "cgroup", // this is actually fstype +- Destination: subsystemPath, +- Flags: flags, +- Data: filepath.Base(subsystemPath), +- } +- if err := mountNewCgroup(cgroupmount); err != nil { ++ if err := utils.WithProcfd(c.root, b.Destination, func(procfd string) error { ++ flags := defaultMountFlags ++ if m.Flags&unix.MS_RDONLY != 0 { ++ flags = flags | unix.MS_RDONLY ++ } ++ var ( ++ source = "cgroup" ++ data = filepath.Base(subsystemPath) ++ ) ++ if data == "systemd" { ++ data = cgroups.CgroupNamePrefix + data ++ source = "systemd" ++ } ++ return unix.Mount(source, procfd, "cgroup", uintptr(flags), data) ++ }); err != nil { + return err + } + } else { +@@ -307,33 +309,79 @@ func mountCgroupV2(m *configs.Mount, c *mountConfig) error { + if err := os.MkdirAll(dest, 0755); err != nil { + return err + } +- if err := unix.Mount(m.Source, dest, "cgroup2", uintptr(m.Flags), m.Data); err != nil { +- // when we are in UserNS but CgroupNS is not unshared, we cannot mount cgroup2 (#2158) +- if err == unix.EPERM || err == unix.EBUSY { +- src := fs2.UnifiedMountpoint +- if c.cgroupns && c.cgroup2Path != "" { +- // Emulate cgroupns by bind-mounting +- // the container cgroup path rather than +- // the whole /sys/fs/cgroup. +- src = c.cgroup2Path +- } +- err = unix.Mount(src, dest, "", uintptr(m.Flags)|unix.MS_BIND, "") +- if err == unix.ENOENT && c.rootlessCgroups { +- err = nil ++ return utils.WithProcfd(c.root, m.Destination, func(procfd string) error { ++ if err := unix.Mount(m.Source, procfd, "cgroup2", uintptr(m.Flags), m.Data); err != nil { ++ // when we are in UserNS but CgroupNS is not unshared, we cannot mount cgroup2 (#2158) ++ if err == unix.EPERM || err == unix.EBUSY { ++ src := fs2.UnifiedMountpoint ++ if c.cgroupns && c.cgroup2Path != "" { ++ // Emulate cgroupns by bind-mounting ++ // the container cgroup path rather than ++ // the whole /sys/fs/cgroup. ++ src = c.cgroup2Path ++ } ++ err = unix.Mount(src, procfd, "", uintptr(m.Flags)|unix.MS_BIND, "") ++ if err == unix.ENOENT && c.rootlessCgroups { ++ err = nil ++ } + } + return err + } ++ return nil ++ }) ++} ++ ++func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) { ++ // Set up a scratch dir for the tmpfs on the host. ++ tmpdir, err := prepareTmp("/tmp") ++ if err != nil { ++ return newSystemErrorWithCause(err, "tmpcopyup: failed to setup tmpdir") ++ } ++ defer cleanupTmp(tmpdir) ++ tmpDir, err := ioutil.TempDir(tmpdir, "runctmpdir") ++ if err != nil { ++ return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir") ++ } ++ defer os.RemoveAll(tmpDir) ++ ++ // Configure the *host* tmpdir as if it's the container mount. We change ++ // m.Destination since we are going to mount *on the host*. ++ oldDest := m.Destination ++ m.Destination = tmpDir ++ err = mountPropagate(m, "/", mountLabel) ++ m.Destination = oldDest ++ if err != nil { + return err + } +- return nil ++ defer func() { ++ if Err != nil { ++ if err := unix.Unmount(tmpDir, unix.MNT_DETACH); err != nil { ++ logrus.Warnf("tmpcopyup: failed to unmount tmpdir on error: %v", err) ++ } ++ } ++ }() ++ ++ return utils.WithProcfd(rootfs, m.Destination, func(procfd string) (Err error) { ++ // Copy the container data to the host tmpdir. We append "/" to force ++ // CopyDirectory to resolve the symlink rather than trying to copy the ++ // symlink itself. ++ if err := fileutils.CopyDirectory(procfd+"/", tmpDir); err != nil { ++ return fmt.Errorf("tmpcopyup: failed to copy %s to %s (%s): %w", m.Destination, procfd, tmpDir, err) ++ } ++ // Now move the mount into the container. ++ if err := unix.Mount(tmpDir, procfd, "", unix.MS_MOVE, ""); err != nil { ++ return fmt.Errorf("tmpcopyup: failed to move mount %s to %s (%s): %w", tmpDir, procfd, m.Destination, err) ++ } ++ return nil ++ }) + } + + func mountToRootfs(m *configs.Mount, c *mountConfig) error { + rootfs := c.root + mountLabel := c.label +- dest := m.Destination +- if !strings.HasPrefix(dest, rootfs) { +- dest = filepath.Join(rootfs, dest) ++ dest, err := securejoin.SecureJoin(rootfs, m.Destination) ++ if err != nil { ++ return err + } + + switch m.Device { +@@ -364,53 +412,21 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { + } + return label.SetFileLabel(dest, mountLabel) + case "tmpfs": +- copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP +- tmpDir := "" +- // dest might be an absolute symlink, so it needs +- // to be resolved under rootfs. +- dest, err := securejoin.SecureJoin(rootfs, m.Destination) +- if err != nil { +- return err +- } +- m.Destination = dest + stat, err := os.Stat(dest) + if err != nil { + if err := os.MkdirAll(dest, 0755); err != nil { + return err + } + } +- if copyUp { +- tmpdir, err := prepareTmp("/tmp") +- if err != nil { +- return newSystemErrorWithCause(err, "tmpcopyup: failed to setup tmpdir") +- } +- defer cleanupTmp(tmpdir) +- tmpDir, err = ioutil.TempDir(tmpdir, "runctmpdir") +- if err != nil { +- return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir") +- } +- defer os.RemoveAll(tmpDir) +- m.Destination = tmpDir ++ ++ if m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP { ++ err = doTmpfsCopyUp(m, rootfs, mountLabel) ++ } else { ++ err = mountPropagate(m, rootfs, mountLabel) + } +- if err := mountPropagate(m, rootfs, mountLabel); err != nil { ++ if err != nil { + return err + } +- if copyUp { +- if err := fileutils.CopyDirectory(dest, tmpDir); err != nil { +- errMsg := fmt.Errorf("tmpcopyup: failed to copy %s to %s: %v", dest, tmpDir, err) +- if err1 := unix.Unmount(tmpDir, unix.MNT_DETACH); err1 != nil { +- return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) +- } +- return errMsg +- } +- if err := unix.Mount(tmpDir, dest, "", unix.MS_MOVE, ""); err != nil { +- errMsg := fmt.Errorf("tmpcopyup: failed to move mount %s to %s: %v", tmpDir, dest, err) +- if err1 := unix.Unmount(tmpDir, unix.MNT_DETACH); err1 != nil { +- return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) +- } +- return errMsg +- } +- } + if stat != nil { + if err = os.Chmod(dest, stat.Mode()); err != nil { + return err +@@ -454,19 +470,9 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { + } + return mountCgroupV1(m, c) + default: +- // ensure that the destination of the 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. +- var err error +- if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil { +- return err +- } + if err := checkProcMount(rootfs, dest, m.Source); err != nil { + return err + } +- // update the mount with the correct dest after symlinks are resolved. +- m.Destination = dest + if err := os.MkdirAll(dest, 0755); err != nil { + return err + } +@@ -649,7 +655,7 @@ func createDevices(config *configs.Config) error { + return nil + } + +-func bindMountDeviceNode(dest string, node *devices.Device) error { ++func bindMountDeviceNode(rootfs, dest string, node *devices.Device) error { + f, err := os.Create(dest) + if err != nil && !os.IsExist(err) { + return err +@@ -657,7 +663,9 @@ func bindMountDeviceNode(dest string, node *devices.Device) error { + if f != nil { + f.Close() + } +- return unix.Mount(node.Path, dest, "bind", unix.MS_BIND, "") ++ return utils.WithProcfd(rootfs, dest, func(procfd string) error { ++ return unix.Mount(node.Path, procfd, "bind", unix.MS_BIND, "") ++ }) + } + + // Creates the device node in the rootfs of the container. +@@ -666,18 +674,21 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error { + // The node only exists for cgroup reasons, ignore it here. + return nil + } +- dest := filepath.Join(rootfs, node.Path) ++ dest, err := securejoin.SecureJoin(rootfs, node.Path) ++ if err != nil { ++ return err ++ } + if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { + return err + } + if bind { +- return bindMountDeviceNode(dest, node) ++ return bindMountDeviceNode(rootfs, dest, node) + } + if err := mknodDevice(dest, node); err != nil { + if os.IsExist(err) { + return nil + } else if os.IsPermission(err) { +- return bindMountDeviceNode(dest, node) ++ return bindMountDeviceNode(rootfs, dest, node) + } + return err + } +@@ -1013,61 +1024,47 @@ func writeSystemProperty(key, value string) error { + } + + func remount(m *configs.Mount, rootfs string) error { +- var ( +- dest = m.Destination +- ) +- if !strings.HasPrefix(dest, rootfs) { +- dest = filepath.Join(rootfs, dest) +- } +- return unix.Mount(m.Source, dest, m.Device, uintptr(m.Flags|unix.MS_REMOUNT), "") ++ return utils.WithProcfd(rootfs, m.Destination, func(procfd string) error { ++ return unix.Mount(m.Source, procfd, m.Device, uintptr(m.Flags|unix.MS_REMOUNT), "") ++ }) + } + + // Do the mount operation followed by additional mounts required to take care +-// of propagation flags. ++// of propagation flags. This will always be scoped inside the container rootfs. + func mountPropagate(m *configs.Mount, rootfs string, mountLabel string) error { + var ( +- dest = m.Destination + data = label.FormatMountLabel(m.Data, mountLabel) + flags = m.Flags + ) +- if libcontainerUtils.CleanPath(dest) == "/dev" { +- flags &= ^unix.MS_RDONLY +- } +- +- // Mount it rw to allow chmod operation. A remount will be performed +- // later to make it ro if set. +- if m.Device == "tmpfs" { ++ // Delay mounting the filesystem read-only if we need to do further ++ // operations on it. We need to set up files in "/dev" and tmpfs mounts may ++ // need to be chmod-ed after mounting. The mount will be remounted ro later ++ // in finalizeRootfs() if necessary. ++ if libcontainerUtils.CleanPath(m.Destination) == "/dev" || m.Device == "tmpfs" { + flags &= ^unix.MS_RDONLY + } + +- copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP +- if !(copyUp || strings.HasPrefix(dest, rootfs)) { +- dest = filepath.Join(rootfs, dest) +- } +- +- if err := unix.Mount(m.Source, dest, m.Device, uintptr(flags), data); err != nil { +- return err +- } +- +- for _, pflag := range m.PropagationFlags { +- if err := unix.Mount("", dest, "", uintptr(pflag), ""); err != nil { +- return err ++ // Because the destination is inside a container path which might be ++ // mutating underneath us, we verify that we are actually going to mount ++ // inside the container with WithProcfd() -- mounting through a procfd ++ // mounts on the target. ++ if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error { ++ return unix.Mount(m.Source, procfd, m.Device, uintptr(flags), data) ++ }); err != nil { ++ return fmt.Errorf("mount through procfd: %w", err) ++ } ++ // We have to apply mount propagation flags in a separate WithProcfd() call ++ // because the previous call invalidates the passed procfd -- the mount ++ // target needs to be re-opened. ++ if err := utils.WithProcfd(rootfs, m.Destination, func(procfd string) error { ++ for _, pflag := range m.PropagationFlags { ++ if err := unix.Mount("", procfd, "", uintptr(pflag), ""); err != nil { ++ return err ++ } + } +- } +- return nil +-} +- +-func mountNewCgroup(m *configs.Mount) error { +- var ( +- data = m.Data +- source = m.Source +- ) +- if data == "systemd" { +- data = cgroups.CgroupNamePrefix + data +- source = "systemd" +- } +- if err := unix.Mount(source, m.Destination, m.Device, uintptr(m.Flags), data); err != nil { +- return err ++ return nil ++ }); err != nil { ++ return fmt.Errorf("change mount propagation through procfd: %w", err) + } + return nil + } +diff --git a/libcontainer/utils/utils.go b/libcontainer/utils/utils.go +index 1b72b7a1c1ba..cd78f23e1bd0 100644 +--- a/libcontainer/utils/utils.go ++++ b/libcontainer/utils/utils.go +@@ -3,12 +3,15 @@ package utils + import ( + "encoding/binary" + "encoding/json" ++ "fmt" + "io" + "os" + "path/filepath" ++ "strconv" + "strings" + "unsafe" + ++ "github.com/cyphar/filepath-securejoin" + "golang.org/x/sys/unix" + ) + +@@ -88,6 +91,57 @@ func CleanPath(path string) string { + return filepath.Clean(path) + } + ++// stripRoot returns the passed path, stripping the root path if it was ++// (lexicially) inside it. Note that both passed paths will always be treated ++// as absolute, and the returned path will also always be absolute. In ++// addition, the paths are cleaned before stripping the root. ++func stripRoot(root, path string) string { ++ // Make the paths clean and absolute. ++ root, path = CleanPath("/"+root), CleanPath("/"+path) ++ switch { ++ case path == root: ++ path = "/" ++ case root == "/": ++ // do nothing ++ case strings.HasPrefix(path, root+"/"): ++ path = strings.TrimPrefix(path, root+"/") ++ } ++ return CleanPath("/" + path) ++} ++ ++// WithProcfd runs the passed closure with a procfd path (/proc/self/fd/...) ++// corresponding to the unsafePath resolved within the root. Before passing the ++// fd, this path is verified to have been inside the root -- so operating on it ++// through the passed fdpath should be safe. Do not access this path through ++// the original path strings, and do not attempt to use the pathname outside of ++// the passed closure (the file handle will be freed once the closure returns). ++func WithProcfd(root, unsafePath string, fn func(procfd string) error) error { ++ // Remove the root then forcefully resolve inside the root. ++ unsafePath = stripRoot(root, unsafePath) ++ path, err := securejoin.SecureJoin(root, unsafePath) ++ if err != nil { ++ return fmt.Errorf("resolving path inside rootfs failed: %v", err) ++ } ++ ++ // Open the target path. ++ fh, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return fmt.Errorf("open o_path procfd: %w", err) ++ } ++ defer fh.Close() ++ ++ // Double-check the path is the one we expected. ++ procfd := "/proc/self/fd/" + strconv.Itoa(int(fh.Fd())) ++ if realpath, err := os.Readlink(procfd); err != nil { ++ return fmt.Errorf("procfd verification failed: %w", err) ++ } else if realpath != path { ++ return fmt.Errorf("possibly malicious path detected -- refusing to operate on %s", realpath) ++ } ++ ++ // Run the closure. ++ return fn(procfd) ++} ++ + // SearchLabels searches a list of key-value pairs for the provided key and + // returns the corresponding value. The pairs must be separated with '='. + func SearchLabels(labels []string, query string) string { +diff --git a/libcontainer/utils/utils_test.go b/libcontainer/utils/utils_test.go +index 7f38ed169a6b..d33662238d36 100644 +--- a/libcontainer/utils/utils_test.go ++++ b/libcontainer/utils/utils_test.go +@@ -143,3 +143,38 @@ func TestCleanPath(t *testing.T) { + t.Errorf("expected to receive '/foo' and received %s", path) + } + } ++ ++func TestStripRoot(t *testing.T) { ++ for _, test := range []struct { ++ root, path, out string ++ }{ ++ // Works with multiple components. ++ {"/a/b", "/a/b/c", "/c"}, ++ {"/hello/world", "/hello/world/the/quick-brown/fox", "/the/quick-brown/fox"}, ++ // '/' must be a no-op. ++ {"/", "/a/b/c", "/a/b/c"}, ++ // Must be the correct order. ++ {"/a/b", "/a/c/b", "/a/c/b"}, ++ // Must be at start. ++ {"/abc/def", "/foo/abc/def/bar", "/foo/abc/def/bar"}, ++ // Must be a lexical parent. ++ {"/foo/bar", "/foo/barSAMECOMPONENT", "/foo/barSAMECOMPONENT"}, ++ // Must only strip the root once. ++ {"/foo/bar", "/foo/bar/foo/bar/baz", "/foo/bar/baz"}, ++ // Deal with .. in a fairly sane way. ++ {"/foo/bar", "/foo/bar/../baz", "/foo/baz"}, ++ {"/foo/bar", "../../../../../../foo/bar/baz", "/baz"}, ++ {"/foo/bar", "/../../../../../../foo/bar/baz", "/baz"}, ++ {"/foo/bar/../baz", "/foo/baz/bar", "/bar"}, ++ {"/foo/bar/../baz", "/foo/baz/../bar/../baz/./foo", "/foo"}, ++ // All paths are made absolute before stripping. ++ {"foo/bar", "/foo/bar/baz/bee", "/baz/bee"}, ++ {"/foo/bar", "foo/bar/baz/beef", "/baz/beef"}, ++ {"foo/bar", "foo/bar/baz/beets", "/baz/beets"}, ++ } { ++ got := stripRoot(test.root, test.path) ++ if got != test.out { ++ t.Errorf("stripRoot(%q, %q) -- got %q, expected %q", test.root, test.path, got, test.out) ++ } ++ } ++} +-- +2.31.1 + diff --git a/SOURCES/runc-1947432.patch b/SOURCES/runc-1947432.patch new file mode 100644 index 0000000..79ae82e --- /dev/null +++ b/SOURCES/runc-1947432.patch @@ -0,0 +1,102 @@ +From a2050ea471b0eb3c7240282219773c0f1d7ec554 Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Wed, 7 Apr 2021 16:45:39 -0700 +Subject: [PATCH 1/2] runc run: fix start for rootless + host pidns + +Currently, runc fails like this when used from rootless podman +with host PID namespace: + +> $ podman --runtime=runc run --pid=host --rm -it busybox sh +> WARN[0000] additional gid=10 is not present in the user namespace, skip setting it +> Error: container_linux.go:380: starting container process caused: +> process_linux.go:545: container init caused: readonly path /proc/asound: +> operation not permitted: OCI permission denied + +(Here /proc/asound is the first path from OCI spec's readonlyPaths). + +The code uses MS_BIND|MS_REMOUNT flags that have a special meaning in +the kernel ("keep the flags like nodev, nosuid, noexec as is"). +For some reason, this "special meaning" trick is not working for the +above use case (rootless podman + no PID namespace), and I don't know +how to reproduce this without podman. + +Instead of relying on the kernel feature, let's just get the current +mount flags using fstatfs(2) and add those that needs to be preserved. + +While at it, wrap errors from unix.Mount into os.PathError to make +errors a bit less cryptic. + +Signed-off-by: Kir Kolyshkin +--- + libcontainer/rootfs_linux.go | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index ed38e77219..3d67287926 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -931,9 +931,20 @@ func readonlyPath(path string) error { + if os.IsNotExist(err) { + return nil + } +- return err ++ return &os.PathError{Op: "bind-mount", Path: path, Err: err} ++ } ++ ++ var s unix.Statfs_t ++ if err := unix.Statfs(path, &s); err != nil { ++ return &os.PathError{Op: "statfs", Path: path, Err: err} + } +- return unix.Mount(path, path, "", unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY|unix.MS_REC, "") ++ flags := uintptr(s.Flags) & (unix.MS_NOSUID | unix.MS_NODEV | unix.MS_NOEXEC) ++ ++ if err := unix.Mount(path, path, "", flags|unix.MS_BIND|unix.MS_REMOUNT|unix.MS_RDONLY, ""); err != nil { ++ return &os.PathError{Op: "bind-mount-ro", Path: path, Err: err} ++ } ++ ++ return nil + } + + // remountReadonly will remount an existing mount point and ensure that it is read-only. + +From 31dd1e499b2f590cd3bcf59153491967ea2a8e1f Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Wed, 14 Apr 2021 10:58:42 -0700 +Subject: [PATCH 2/2] tests/int: add rootless + host pidns test case + +For the fix, see previous commit. Without the fix, this test case fails: + +> container_linux.go:380: starting container process caused: +> process_linux.go:545: container init caused: readonly path /proc/bus: +> operation not permitted + +Signed-off-by: Kir Kolyshkin +--- + tests/integration/start_hello.bats | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/tests/integration/start_hello.bats b/tests/integration/start_hello.bats +index 5c2a66fdb1..858847032c 100644 +--- a/tests/integration/start_hello.bats ++++ b/tests/integration/start_hello.bats +@@ -59,3 +59,20 @@ function teardown() { + + [[ "$(cat pid.txt)" =~ [0-9]+ ]] + } ++ ++# https://github.com/opencontainers/runc/pull/2897 ++@test "runc run [rootless with host pidns]" { ++ requires rootless_no_features ++ ++ # Remove pid namespace, and replace /proc mount ++ # with a bind mount from the host. ++ update_config ' .linux.namespaces -= [{"type": "pid"}] ++ | .mounts |= map((select(.type == "proc") ++ | .type = "none" ++ | .source = "/proc" ++ | .options = ["rbind", "nosuid", "nodev", "noexec"] ++ ) // .)' ++ ++ runc run test_hello ++ [ "$status" -eq 0 ] ++} diff --git a/SPECS/runc.spec b/SPECS/runc.spec index 6e5f001..5f6e4f7 100644 --- a/SPECS/runc.spec +++ b/SPECS/runc.spec @@ -19,11 +19,11 @@ go build -buildmode pie -compiler gc -tags="rpm_crashtraceback libtrust_openssl # https://github.com/opencontainers/runc %global import_path %{provider}.%{provider_tld}/%{project}/%{repo} %global git0 https://%{import_path} -%global release_candidate rc92 +%global release_candidate rc93 Name: %{repo} Version: 1.0.0 -Release: 70.%{release_candidate}%{?dist} +Release: 73.%{release_candidate}%{?dist} Summary: CLI for running Open Containers # https://fedoraproject.org/wiki/PackagingDrafts/Go#Go_Language_Architectures #ExclusiveArch: %%{go_arches} @@ -33,7 +33,14 @@ ExcludeArch: %{ix86} License: ASL 2.0 URL: %{git0} Source0: %{git0}/archive/v1.0.0-%{release_candidate}.tar.gz -#Patch0: 1807.patch +Patch1: rc93-0001-libct-newInitConfig-nit.patch +Patch2: rc93-0002-libct-rootfs-introduce-and-use-mountConfig.patch +Patch3: rc93-0003-libct-rootfs-mountCgroupV2-minor-refactor.patch +Patch4: rc93-0004-Fix-cgroup2-mount-for-rootless-case.patch +Patch5: rc93-0005-rootfs-add-mount-destination-validation.patch +# related bug: https://bugzilla.redhat.com/show_bug.cgi?id=1947432 +# patch: https://github.com/opencontainers/runc/pull/2897.patch +Patch6: runc-1947432.patch Provides: oci-runtime = 1 BuildRequires: golang >= 1.12.12-4 BuildRequires: git @@ -91,6 +98,18 @@ install -p -m 0644 contrib/completions/bash/%{name} %{buildroot}%{_datadir}/bash %{_datadir}/bash-completion/completions/%{name} %changelog +* Thu May 13 2021 Jindrich Novy - 1.0.0-73.rc93 +- fix "podman run --pid=host command causes OCI permission error" +- Related: #1954702 + +* Tue May 11 2021 Jindrich Novy - 1.0.0-72.rc93 +- fix CVE-2021-30465 +- Related: #1954702 + +* Thu Apr 29 2021 Jindrich Novy - 1.0.0-71.rc93 +- upload rc93 tarball +- Related: #1954702 + * Fri Jan 29 2021 Jindrich Novy - 1.0.0-70.rc92 - add missing Provides: oci-runtime = 1 - Related: #1883490