import runc-1.0.0-66.rc10.module+el8.5.0+11041+627a5cdc

c8-stream-2.0 imports/c8-stream-2.0/runc-1.0.0-66.rc10.module+el8.5.0+14796+e4386a39
CentOS Sources 3 years ago committed by MSVSphere Packaging Team
commit 7927c3ecf8

1
.gitignore vendored

@ -0,0 +1 @@
SOURCES/runc-dc9208a.tar.gz

@ -0,0 +1 @@
32859590dea35b77eed012c388d97fc12fdfdb93 SOURCES/runc-dc9208a.tar.gz

@ -0,0 +1,540 @@
From 2dd156b190c02476191fc2522f9b0e0a1a098608 Mon Sep 17 00:00:00 2001
From: Kir Kolyshkin <kolyshkin@gmail.com>
Date: Mon, 17 May 2021 16:11:35 -0700
Subject: [PATCH] rootfs: add mount destination validation
This is a manual backport of fix for CVE-2021-30465 to runc-1.0.0-rc10
(aka -rc90), upstream commit 84c14b43fa703db7 by Aleksa Sarai.
Original description follows.
----
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 <champetier.etienne@gmail.com>
Co-authored-by: Noah Meyerhans <nmeyerha@amazon.com>
Reviewed-by: Samuel Karp <skarp@amazon.com>
Reviewed-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
---
libcontainer/rootfs_linux.go | 225 ++++++++++++++++---------------
libcontainer/utils/utils.go | 54 ++++++++
libcontainer/utils/utils_test.go | 35 +++++
3 files changed, 204 insertions(+), 110 deletions(-)
diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
index 106c4c2b..fe9afe48 100644
--- a/libcontainer/rootfs_linux.go
+++ b/libcontainer/rootfs_linux.go
@@ -19,8 +19,9 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/mount"
"github.com/opencontainers/runc/libcontainer/system"
- libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
+ "github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/selinux/go-selinux/label"
+ "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -30,7 +31,7 @@ const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
// needsSetupDev returns true if /dev needs to be set up.
func needsSetupDev(config *configs.Config) bool {
for _, m := range config.Mounts {
- if m.Device == "bind" && libcontainerUtils.CleanPath(m.Destination) == "/dev" {
+ if m.Device == "bind" && utils.CleanPath(m.Destination) == "/dev" {
return false
}
}
@@ -131,7 +132,7 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) {
func finalizeRootfs(config *configs.Config) (err error) {
// remount dev as ro if specified
for _, m := range config.Mounts {
- if libcontainerUtils.CleanPath(m.Destination) == "/dev" {
+ if utils.CleanPath(m.Destination) == "/dev" {
if m.Flags&unix.MS_RDONLY == unix.MS_RDONLY {
if err := remountReadonly(m); err != nil {
return newSystemErrorWithCausef(err, "remounting %q as readonly", m.Destination)
@@ -200,8 +201,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
}
@@ -238,18 +237,21 @@ func mountCgroupV1(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
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",
- Destination: subsystemPath,
- Flags: flags,
- Data: filepath.Base(subsystemPath),
- }
- if err := mountNewCgroup(cgroupmount); err != nil {
+ if err := utils.WithProcfd(rootfs, 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 {
@@ -279,22 +281,67 @@ func mountCgroupV2(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
if err := os.MkdirAll(cgroupPath, 0755); err != nil {
return err
}
- if err := unix.Mount(m.Source, cgroupPath, "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, "")
+ return utils.WithProcfd(rootfs, 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 {
+ return unix.Mount("/sys/fs/cgroup", procfd, "", uintptr(m.Flags)|unix.MS_BIND, "")
+ }
+ 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): %v", 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): %v", tmpDir, procfd, m.Destination, err)
+ }
+ return nil
+ })
}
func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns bool) error {
- var (
- 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 {
@@ -329,46 +376,21 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
}
return nil
case "tmpfs":
- copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP
- tmpDir := ""
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
@@ -424,19 +446,9 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string, enableCgroupns b
}
}
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
}
@@ -611,7 +623,7 @@ func createDevices(config *configs.Config) error {
return nil
}
-func bindMountDeviceNode(dest string, node *configs.Device) error {
+func bindMountDeviceNode(rootfs, dest string, node *configs.Device) error {
f, err := os.Create(dest)
if err != nil && !os.IsExist(err) {
return err
@@ -619,24 +631,29 @@ func bindMountDeviceNode(dest string, node *configs.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.
func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
- 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
}
@@ -955,55 +972,43 @@ 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" {
+ if utils.CleanPath(m.Destination) == "/dev" {
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: %v", 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: %v", err)
}
return nil
}
diff --git a/libcontainer/utils/utils.go b/libcontainer/utils/utils.go
index 40ccfaa1..c1418ef9 100644
--- a/libcontainer/utils/utils.go
+++ b/libcontainer/utils/utils.go
@@ -2,12 +2,15 @@ package utils
import (
"encoding/json"
+ "fmt"
"io"
"os"
"path/filepath"
+ "strconv"
"strings"
"unsafe"
+ securejoin "github.com/cyphar/filepath-securejoin"
"golang.org/x/sys/unix"
)
@@ -73,6 +76,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: %v", 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: %v", 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 395eedcf..5b80cac6 100644
--- a/libcontainer/utils/utils_test.go
+++ b/libcontainer/utils/utils_test.go
@@ -140,3 +140,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

@ -0,0 +1,278 @@
From 3d99c51e1b38a440804a55c9f314f62cc50b8902 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <gscrivan@redhat.com>
Date: Fri, 25 May 2018 18:04:06 +0200
Subject: [PATCH] sd-notify: do not hang when NOTIFY_SOCKET is used with create
if NOTIFY_SOCKET is used, do not block the main runc process waiting
for events on the notify socket. Bind mount the parent directory of
the notify socket, so that "start" can create the socket and it is
still accessible from the container.
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
---
notify_socket.go | 112 ++++++++++++++++++++++++++++++++++-------------
signals.go | 4 +-
start.go | 13 +++++-
utils_linux.go | 12 ++++-
4 files changed, 105 insertions(+), 36 deletions(-)
diff --git a/notify_socket.go b/notify_socket.go
index e7453c62..d961453a 100644
--- a/notify_socket.go
+++ b/notify_socket.go
@@ -7,11 +7,13 @@ import (
"fmt"
"net"
"os"
+ "path"
"path/filepath"
+ "strconv"
+ "time"
+ "github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runtime-spec/specs-go"
-
- "github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -27,12 +29,12 @@ func newNotifySocket(context *cli.Context, notifySocketHost string, id string) *
}
root := filepath.Join(context.GlobalString("root"), id)
- path := filepath.Join(root, "notify.sock")
+ socketPath := filepath.Join(root, "notify", "notify.sock")
notifySocket := &notifySocket{
socket: nil,
host: notifySocketHost,
- socketPath: path,
+ socketPath: socketPath,
}
return notifySocket
@@ -44,13 +46,19 @@ func (s *notifySocket) Close() error {
// If systemd is supporting sd_notify protocol, this function will add support
// for sd_notify protocol from within the container.
-func (s *notifySocket) setupSpec(context *cli.Context, spec *specs.Spec) {
- mount := specs.Mount{Destination: s.host, Source: s.socketPath, Options: []string{"bind"}}
+func (s *notifySocket) setupSpec(context *cli.Context, spec *specs.Spec) error {
+ pathInContainer := filepath.Join("/run/notify", path.Base(s.socketPath))
+ mount := specs.Mount{
+ Destination: path.Dir(pathInContainer),
+ Source: path.Dir(s.socketPath),
+ Options: []string{"bind", "nosuid", "noexec", "nodev", "ro"},
+ }
spec.Mounts = append(spec.Mounts, mount)
- spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", s.host))
+ spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", pathInContainer))
+ return nil
}
-func (s *notifySocket) setupSocket() error {
+func (s *notifySocket) bindSocket() error {
addr := net.UnixAddr{
Name: s.socketPath,
Net: "unixgram",
@@ -71,45 +79,89 @@ func (s *notifySocket) setupSocket() error {
return nil
}
-// pid1 must be set only with -d, as it is used to set the new process as the main process
-// for the service in systemd
-func (s *notifySocket) run(pid1 int) {
- buf := make([]byte, 512)
- notifySocketHostAddr := net.UnixAddr{Name: s.host, Net: "unixgram"}
+func (s *notifySocket) setupSocketDirectory() error {
+ return os.Mkdir(path.Dir(s.socketPath), 0755)
+}
+
+func notifySocketStart(context *cli.Context, notifySocketHost, id string) (*notifySocket, error) {
+ notifySocket := newNotifySocket(context, notifySocketHost, id)
+ if notifySocket == nil {
+ return nil, nil
+ }
+
+ if err := notifySocket.bindSocket(); err != nil {
+ return nil, err
+ }
+ return notifySocket, nil
+}
+
+func (n *notifySocket) waitForContainer(container libcontainer.Container) error {
+ s, err := container.State()
+ if err != nil {
+ return err
+ }
+ return n.run(s.InitProcessPid)
+}
+
+func (n *notifySocket) run(pid1 int) error {
+ if n.socket == nil {
+ return nil
+ }
+ notifySocketHostAddr := net.UnixAddr{Name: n.host, Net: "unixgram"}
client, err := net.DialUnix("unixgram", nil, &notifySocketHostAddr)
if err != nil {
- logrus.Error(err)
- return
+ return err
}
- for {
- r, err := s.socket.Read(buf)
- if err != nil {
- break
+
+ ticker := time.NewTicker(time.Millisecond * 100)
+ defer ticker.Stop()
+
+ fileChan := make(chan []byte)
+ go func() {
+ for {
+ buf := make([]byte, 512)
+ r, err := n.socket.Read(buf)
+ if err != nil {
+ return
+ }
+ got := buf[0:r]
+ if !bytes.HasPrefix(got, []byte("READY=")) {
+ continue
+ }
+ fileChan <- got
+ return
}
- var out bytes.Buffer
- for _, line := range bytes.Split(buf[0:r], []byte{'\n'}) {
- if bytes.HasPrefix(line, []byte("READY=")) {
+ }()
+
+ for {
+ select {
+ case <-ticker.C:
+ _, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid1)))
+ if err != nil {
+ return nil
+ }
+ case b := <-fileChan:
+ for _, line := range bytes.Split(b, []byte{'\n'}) {
+ var out bytes.Buffer
_, err = out.Write(line)
if err != nil {
- return
+ return err
}
_, err = out.Write([]byte{'\n'})
if err != nil {
- return
+ return err
}
_, err = client.Write(out.Bytes())
if err != nil {
- return
+ return err
}
// now we can inform systemd to use pid1 as the pid to monitor
- if pid1 > 0 {
- newPid := fmt.Sprintf("MAINPID=%d\n", pid1)
- client.Write([]byte(newPid))
- }
- return
+ newPid := fmt.Sprintf("MAINPID=%d\n", pid1)
+ client.Write([]byte(newPid))
+ return nil
}
}
}
diff --git a/signals.go b/signals.go
index b67f65a0..dd25e094 100644
--- a/signals.go
+++ b/signals.go
@@ -70,6 +70,7 @@ func (h *signalHandler) forward(process *libcontainer.Process, tty *tty, detach
h.notifySocket.run(pid1)
return 0, nil
}
+ h.notifySocket.run(os.Getpid())
go h.notifySocket.run(0)
}
@@ -97,9 +98,6 @@ func (h *signalHandler) forward(process *libcontainer.Process, tty *tty, detach
// status because we must ensure that any of the go specific process
// fun such as flushing pipes are complete before we return.
process.Wait()
- if h.notifySocket != nil {
- h.notifySocket.Close()
- }
return e.status, nil
}
}
diff --git a/start.go b/start.go
index 2bb698b2..3a1769a4 100644
--- a/start.go
+++ b/start.go
@@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
+ "os"
"github.com/opencontainers/runc/libcontainer"
"github.com/urfave/cli"
@@ -31,7 +32,17 @@ your host.`,
}
switch status {
case libcontainer.Created:
- return container.Exec()
+ notifySocket, err := notifySocketStart(context, os.Getenv("NOTIFY_SOCKET"), container.ID())
+ if err != nil {
+ return err
+ }
+ if err := container.Exec(); err != nil {
+ return err
+ }
+ if notifySocket != nil {
+ return notifySocket.waitForContainer(container)
+ }
+ return nil
case libcontainer.Stopped:
return errors.New("cannot start a container that has stopped")
case libcontainer.Running:
diff --git a/utils_linux.go b/utils_linux.go
index 984e6b0f..46c26246 100644
--- a/utils_linux.go
+++ b/utils_linux.go
@@ -408,7 +408,9 @@ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOp
notifySocket := newNotifySocket(context, os.Getenv("NOTIFY_SOCKET"), id)
if notifySocket != nil {
- notifySocket.setupSpec(context, spec)
+ if err := notifySocket.setupSpec(context, spec); err != nil {
+ return -1, err
+ }
}
container, err := createContainer(context, id, spec)
@@ -417,10 +419,16 @@ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOp
}
if notifySocket != nil {
- err := notifySocket.setupSocket()
+ err := notifySocket.setupSocketDirectory()
if err != nil {
return -1, err
}
+ if action == CT_ACT_RUN {
+ err := notifySocket.bindSocket()
+ if err != nil {
+ return -1, err
+ }
+ }
}
// Support on-demand socket activation by passing file descriptors into the container init process.
--
2.21.0

@ -0,0 +1,294 @@
%global with_debug 1
%global with_bundled 1
%global with_check 0
%if 0%{?with_debug}
%global _find_debuginfo_dwz_opts %{nil}
%global _dwz_low_mem_die_limit 0
%else
%global debug_package %{nil}
%endif
%if 0%{?rhel} > 7 && ! 0%{?fedora}
%define gobuild(o:) \
go build -buildmode pie -compiler gc -tags="rpm_crashtraceback no_openssl ${BUILDTAGS:-}" -ldflags "${LDFLAGS:-} -compressdwarf=false -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '%__global_ldflags'" -a -v -x %{?**};
%endif
%global provider github
%global provider_tld com
%global project opencontainers
%global repo runc
# https://github.com/opencontainers/runc
%global import_path %{provider}.%{provider_tld}/%{project}/%{repo}
%global git0 https://%{import_path}
%global commit0 dc9208a3303feef5b3839f4323d9beb36df0a9dd
%global shortcommit0 %(c=%{commit0}; echo ${c:0:7})
Name: %{repo}
Version: 1.0.0
Release: 66.rc10%{?dist}
Summary: CLI for running Open Containers
ExcludeArch: %{ix86}
License: ASL 2.0
URL: %{git0}
Source0: %{git0}/archive/%{commit0}/%{name}-%{shortcommit0}.tar.gz
Patch0: 1807.patch
Patch1: 0001-rootfs-add-mount-destination-validation.patch
BuildRequires: golang >= 1.12.12-4
BuildRequires: git
BuildRequires: go-md2man
BuildRequires: libseccomp-devel
Requires: criu
Requires(pre): container-selinux >= 2:2.2-2
%description
The runc command can be used to start containers which are packaged
in accordance with the Open Container Initiative's specifications,
and to manage containers running under runc.
%prep
%autosetup -Sgit -n %{repo}-%{commit0}
sed -i '/\#\!\/bin\/bash/d' contrib/completions/bash/%{name}
%build
mkdir -p GOPATH
pushd GOPATH
mkdir -p src/%{provider}.%{provider_tld}/%{project}
ln -s $(dirs +1 -l) src/%{import_path}
popd
pushd GOPATH/src/%{import_path}
export GO111MODULE=off
export GOPATH=%{gopath}:$(pwd)/GOPATH
export BUILDTAGS="selinux seccomp"
%gobuild -o %{name} %{import_path}
pushd man
./md2man-all.sh
popd
%install
install -d -p %{buildroot}%{_bindir}
install -p -m 755 %{name} %{buildroot}%{_bindir}
# install man pages
install -d -p %{buildroot}%{_mandir}/man8
install -p -m 644 man/man8/* %{buildroot}%{_mandir}/man8
# install bash completion
install -d -p %{buildroot}%{_datadir}/bash-completion/completions
install -p -m 0644 contrib/completions/bash/%{name} %{buildroot}%{_datadir}/bash-completion/completions
%check
#define license tag if not already defined
%{!?_licensedir:%global license %doc}
%files
%license LICENSE
%doc MAINTAINERS_GUIDE.md PRINCIPLES.md README.md CONTRIBUTING.md
%{_bindir}/%{name}
%{_mandir}/man8/%{name}*
%{_datadir}/bash-completion/completions/%{name}
%changelog
* Wed May 19 2021 Jindrich Novy <jnovy@redhat.com> - 1.0.0-66.rc10
- set GO111MODULE=off to fix build
- Related: #1955651
* Wed May 19 2021 Jindrich Novy <jnovy@redhat.com> - 1.0.0-65.rc10
- fix CVE-2021-30465
- Resolves: #1955651
* Thu Feb 13 2020 Jindrich Novy <jnovy@redhat.com> - 1.0.0-64.rc10
- address CVE-2019-19921 by updating to rc10
- Resolves: #1801888
* Wed Dec 11 2019 Jindrich Novy <jnovy@redhat.com> - 1.0.0-63.rc9
- use no_openssl in BUILDTAGS (no vendored crypto in runc)
- Related: RHELPLAN-25139
* Mon Dec 09 2019 Jindrich Novy <jnovy@redhat.com> - 1.0.0-62.rc9
- be sure to use golang >= 1.12.12-4
- Related: RHELPLAN-25139
* Thu Nov 21 2019 Jindrich Novy <jnovy@redhat.com> - 1.0.0-61.rc9
- update to runc 1.0.0-rc9 release
- amend golang deps
- fixes CVE-2019-16884
- Resolves: #1759651
* Mon Jun 17 2019 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-60.rc8
- Resolves: #1721247 - enable fips mode
* Mon Jun 17 2019 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-59.rc8
- Resolves: #1720654 - rebase to v1.0.0-rc8
* Thu Apr 11 2019 Eduardo Santiago <santiago@redhat.com> - 1.0.0-57.rc5.dev.git2abd837
- Resolves: #1693424 - podman rootless: cannot specify gid= mount options
* Wed Feb 27 2019 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-56.rc5.dev.git2abd837
- change-default-root patch not needed as there's no docker on rhel8
* Tue Feb 12 2019 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-55.rc5.dev.git2abd837
- Resolves: CVE-2019-5736
* Tue Dec 18 2018 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.0.0-54.rc5.dev.git2abd837
- re-enable debuginfo
* Mon Dec 17 2018 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.0.0-53.rc5.dev.git2abd837
- go toolset not in scl anymore
* Wed Sep 26 2018 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.0.0-52.rc5.dev.git2abd837
- rebase
* Fri Aug 31 2018 Dan Walsh <dwalsh@redhat.name> - 2:1.0.0-51.dev.gitfdd8055
- Fix handling of tmpcopyup
* Fri Aug 24 2018 Lokesh Mandvekar <lsm5@redhat.com> - 2:1.0.0-49.rc5.dev.gitb4e2ecb
- %%gobuild uses no_openssl
- remove unused devel and unit-test subpackages
* Tue Aug 07 2018 Lokesh Mandvekar <lsm5@redhat.com> - 2:1.0.0-48.rc5.dev.gitad0f525
- build with %%gobuild
- exlude i686 temporarily because of go-toolset issues
* Mon Jul 30 2018 Florian Weimer <fweimer@redhat.com> - 1.0.0-47.dev.gitb4e2ecb
- Rebuild with fixed binutils
* Fri Jul 27 2018 Dan Walsh <dwalsh@redhat.name> - 2:1.0.0-46.dev.gitb4e2ecb
- Add patch https://github.com/opencontainers/runc/pull/1807 to allow
- runc and podman to work with sd_notify
* Wed Jul 18 2018 Dan Walsh <dwalsh@redhat.com> - 2:1.0.0-40.rc5.dev.gitad0f525
- Remove sysclt handling, not needed in RHEL8
- Make sure package built with seccomp flags
- Remove rectty
- Add completions
* Fri Jun 15 2018 Dan Walsh <dwalsh@redhat.com> - 2:1.0.0-36.rc5.dev.gitad0f525
- Better handling of user namespace
* Tue May 1 2018 Dan Walsh <dwalsh@redhat.name> - 2:1.0.0-31.rc5.git0cbfd83
- Fix issues between SELinux and UserNamespace
* Tue Apr 17 2018 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.0.0-27.rc5.dev.git4bb1fe4
- rebuilt, placed missing changelog entry back
* Tue Feb 27 2018 Dan Walsh <dwalsh@redhat.name> - 2:1.0.0-26.rc5.git4bb1fe4
- release v1.0.0~rc5
* Wed Jan 24 2018 Dan Walsh <dwalsh@redhat.name> - 1.0.0-26.rc4.git9f9c962
- Bump to the latest from upstream
* Mon Dec 18 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-25.rc4.gite6516b3
- built commit e6516b3
* Fri Dec 15 2017 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.0.0-24.rc4.dev.gitc6e4a1e.1
- rebase to c6e4a1ebeb1a72b529c6f1b6ee2b1ae5b868b14f
- https://github.com/opencontainers/runc/pull/1651
* Tue Dec 12 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-23.rc4.git1d3ab6d
- Resolves: #1524654
* Sun Dec 10 2017 Dan Walsh <dwalsh@redhat.name> - 1.0.0-22.rc4.git1d3ab6d
- Many Stability fixes
- Many fixes for rootless containers
- Many fixes for static builds
* Thu Nov 09 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-21.rc4.dev.gitaea4f21
- enable debuginfo and include -buildmode=pie for go build
* Tue Nov 07 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-20.rc4.dev.gitaea4f21
- use Makefile
* Tue Nov 07 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-19.rc4.dev.gitaea4f21
- disable debuginfo temporarily
* Fri Nov 03 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-18.rc4.dev.gitaea4f21
- enable debuginfo
* Wed Oct 25 2017 Dan Walsh <dwalsh@redhat.name> - 1.0.0-17.rc4.gitaea4f21
- Add container-selinux prerequires to make sure runc is labeled correctly
* Thu Oct 19 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-16.rc4.dev.gitaea4f21
- correct the release tag "rc4dev" -> "rc4.dev" cause I'm OCD
* Mon Oct 16 2017 Dan Walsh <dwalsh@redhat.com> - 1.0.0-15.rc4dev.gitaea4f21
- Use the same checkout as Fedora for lates CRI-O
* Fri Sep 22 2017 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.0.0-14.rc4dev.git84a082b
- rebase to 84a082bfef6f932de921437815355186db37aeb1
* Tue Jun 13 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-13.rc3.gitd40db12
- Resolves: #1479489
- built commit d40db12
* Tue Jun 13 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-12.1.gitf8ce01d
- disable s390x temporarily because of indefinite wait times on brew
* Tue Jun 13 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-11.1.gitf8ce01d
- correct previous bogus date :\
* Mon Jun 12 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-10.1.gitf8ce01d
- Resolves: #1441737 - run sysctl_apply for sysctl knob
* Tue May 09 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-9.1.gitf8ce01d
- Resolves: #1447078 - change default root path
- add commit e800860 from runc @projectatomic/change-root-path
* Fri May 05 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-8.1.gitf8ce01d
- Resolves: #1441737 - enable kernel sysctl knob /proc/sys/fs/may_detach_mounts
* Thu Apr 13 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-7.1.gitf8ce01d
- Resolves: #1429675
- built @opencontainers/master commit f8ce01d
* Thu Mar 16 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-4.1.gitee992e5
- built @projectatomic/master commit ee992e5
* Fri Feb 24 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-3.rc2
- Resolves: #1426674
- built projectatomic/runc_rhel_7 commit 5d93f81
* Mon Feb 06 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-2.rc2
- Resolves: #1419702 - rebase to latest upstream master
- built commit b263a43
* Wed Jan 11 2017 Lokesh Mandvekar <lsm5@redhat.com> - 1.0.0-1.rc2
- Resolves: #1412239 - *CVE-2016-9962* - set init processes as non-dumpable,
runc patch from Michael Crosby <crosbymichael@gmail.com>
* Wed Sep 07 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.1.1-6
- Resolves: #1373980 - rebuild for 7.3.0
* Sat Jun 25 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.1.1-5
- build with golang >= 1.6.2
* Tue May 31 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.1.1-4
- release tags were inconsistent in the previous build
* Tue May 31 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.1.1-1
- Resolves: #1341267 - rebase runc to v0.1.1
* Tue May 03 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.1.0-3
- add selinux build tag
- add BR: libseccomp-devel
* Tue May 03 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.1.0-2
- Resolves: #1328970 - add seccomp buildtag
* Tue Apr 19 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.1.0-1
- Resolves: rhbz#1328616 - rebase to v0.1.0
* Tue Mar 08 2016 Lokesh Mandvekar <lsm5@redhat.com> - 0.0.8-1.git4155b68
- Resolves: rhbz#1277245 - bump to 0.0.8
- Resolves: rhbz#1302363 - criu is a runtime dep
- Resolves: rhbz#1302348 - libseccomp-golang is bundled in Godeps
- manpages included
* Wed Nov 25 2015 jchaloup <jchaloup@redhat.com> - 1:0.0.5-0.1.git97bc9a7
- Update to 0.0.5, introduce Epoch for Fedora due to 0.2 version instead of 0.0.2
* Fri Aug 21 2015 Jan Chaloupka <jchaloup@redhat.com> - 0.2-0.2.git90e6d37
- First package for Fedora
resolves: #1255179
Loading…
Cancel
Save