mirror of
https://github.com/opencontainers/runc.git
synced 2025-09-26 19:41:35 +08:00
Prohibit /proc and /sys to be symlinks
Commit3291d66b98
introduced a check for /proc and /sys, making sure the destination (dest) is a directory (and not e.g. a symlink). Later, a hunk from commit0ca91f44f
switched from using filepath.Join to SecureJoin for dest. As SecureJoin follows and resolves symlinks, the check whether dest is a symlink no longer works. To fix, do the check without/before using SecureJoin. Add integration tests to make sure we won't regress. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
@@ -375,6 +375,35 @@ func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) {
|
||||
|
||||
func mountToRootfs(m *configs.Mount, c *mountConfig) error {
|
||||
rootfs := c.root
|
||||
|
||||
// procfs and sysfs are special because we need to ensure they are actually
|
||||
// mounted on a specific path in a container without any funny business.
|
||||
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.
|
||||
dest := filepath.Clean(m.Destination)
|
||||
if !strings.HasPrefix(dest, rootfs) {
|
||||
// Do not use securejoin as it resolves symlinks.
|
||||
dest = filepath.Join(rootfs, dest)
|
||||
}
|
||||
if fi, err := os.Lstat(dest); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("filesystem %q must be mounted on ordinary directory", m.Device)
|
||||
}
|
||||
if err := os.MkdirAll(dest, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
// Selinux kernels do not support labeling of /proc or /sys.
|
||||
return mountPropagate(m, rootfs, "", nil)
|
||||
}
|
||||
|
||||
mountLabel := c.label
|
||||
mountFd := c.fd
|
||||
dest, err := securejoin.SecureJoin(rootfs, m.Destination)
|
||||
@@ -383,24 +412,6 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) 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 err := os.MkdirAll(dest, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
// Selinux kernels do not support labeling of /proc or /sys
|
||||
return mountPropagate(m, rootfs, "", nil)
|
||||
case "mqueue":
|
||||
if err := os.MkdirAll(dest, 0o755); err != nil {
|
||||
return err
|
||||
|
@@ -56,3 +56,22 @@ function teardown() {
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${output}" == *"Operation not permitted"* ]]
|
||||
}
|
||||
|
||||
@test "mask paths [prohibit symlink /proc]" {
|
||||
ln -s /symlink rootfs/proc
|
||||
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${output}" == *"must be mounted on ordinary directory"* ]]
|
||||
}
|
||||
|
||||
@test "mask paths [prohibit symlink /sys]" {
|
||||
# In rootless containers, /sys is a bind mount not a real sysfs.
|
||||
requires root
|
||||
|
||||
ln -s /symlink rootfs/sys
|
||||
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
||||
[ "$status" -eq 1 ]
|
||||
# On cgroup v1, this may fail before checking if /sys is a symlink,
|
||||
# so we merely check that it fails, and do not check the exact error
|
||||
# message like for /proc above.
|
||||
}
|
||||
|
Reference in New Issue
Block a user