mirror of
https://github.com/containers/podman.git
synced 2025-10-09 06:26:26 +08:00
Merge pull request #27021 from TomSweeneyRedHat/dev/tsweeney/CVE-2025-9566-v5.4-rhel
[v5.4-rhel] Backport CVE-2025-9566 fixes
This commit is contained in:
@ -788,8 +788,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
defaultMode := v.DefaultMode
|
||||
// Create files and add data to the volume mountpoint based on the Items in the volume
|
||||
for k, v := range v.Items {
|
||||
dataPath := filepath.Join(mountPoint, k)
|
||||
f, err := os.Create(dataPath)
|
||||
f, err := openPathSafely(mountPoint, k)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create file %q at volume mountpoint %q: %w", k, mountPoint, err)
|
||||
}
|
||||
@ -799,7 +798,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||
return nil, nil, err
|
||||
}
|
||||
// Set file permissions
|
||||
if err := os.Chmod(f.Name(), os.FileMode(defaultMode)); err != nil {
|
||||
if err := f.Chmod(os.FileMode(defaultMode)); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
18
pkg/domain/infra/abi/play_linux.go
Normal file
18
pkg/domain/infra/abi/play_linux.go
Normal file
@ -0,0 +1,18 @@
|
||||
//go:build !remote
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
)
|
||||
|
||||
// openSymlinkPath opens the path under root using securejoin.OpenatInRoot().
|
||||
func openSymlinkPath(root *os.File, unsafePath string, flags int) (*os.File, error) {
|
||||
file, err := securejoin.OpenatInRoot(root, unsafePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return securejoin.Reopen(file, flags)
|
||||
}
|
13
pkg/domain/infra/abi/play_unsupported.go
Normal file
13
pkg/domain/infra/abi/play_unsupported.go
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build !linux && !remote
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// openSymlinkPath is not supported on this platform.
|
||||
func openSymlinkPath(root *os.File, unsafePath string, flags int) (*os.File, error) {
|
||||
return nil, errors.New("cannot safely open symlink on this platform")
|
||||
}
|
@ -2,7 +2,14 @@
|
||||
|
||||
package abi
|
||||
|
||||
import "github.com/containers/podman/v5/libpod/define"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v5/libpod/define"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// getSdNotifyMode returns the `sdNotifyAnnotation/$name` for the specified
|
||||
// name. If name is empty, it'll only look for `sdNotifyAnnotation`.
|
||||
@ -16,3 +23,33 @@ func getSdNotifyMode(annotations map[string]string, name string) (string, error)
|
||||
}
|
||||
return mode, define.ValidateSdNotifyMode(mode)
|
||||
}
|
||||
|
||||
// openPathSafely opens the given name under the trusted root path, the unsafeName
|
||||
// must be a single path component and not contain "/".
|
||||
// The resulting path will be opened or created if it does not exists.
|
||||
// Following of symlink is done within staying under root, escapes outsides
|
||||
// of root are not allowed and prevent.
|
||||
//
|
||||
// This custom function is needed because securejoin.SecureJoin() is not race safe
|
||||
// and the volume might be mounted in another container that could swap in a symlink
|
||||
// after the function ahs run. securejoin.OpenInRoot() doesn't work either because
|
||||
// it cannot create files and doesn't work on freebsd.
|
||||
func openPathSafely(root, unsafeName string) (*os.File, error) {
|
||||
if strings.Contains(unsafeName, "/") {
|
||||
return nil, fmt.Errorf("name %q must not contain path separator", unsafeName)
|
||||
}
|
||||
fdDir, err := os.OpenFile(root, unix.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fdDir.Close()
|
||||
flags := unix.O_CREAT | unix.O_WRONLY | unix.O_TRUNC | unix.O_CLOEXEC
|
||||
fd, err := unix.Openat(int(fdDir.Fd()), unsafeName, flags|unix.O_NOFOLLOW, 0o644)
|
||||
if err == nil {
|
||||
return os.NewFile(uintptr(fd), unsafeName), nil
|
||||
}
|
||||
if err == unix.ELOOP {
|
||||
return openSymlinkPath(fdDir, unsafeName, flags)
|
||||
}
|
||||
return nil, &os.PathError{Op: "openat", Path: unsafeName, Err: err}
|
||||
}
|
||||
|
@ -2157,7 +2157,7 @@ func getPersistentVolumeClaimVolume(vName string) *Volume {
|
||||
|
||||
// getConfigMap returns a new ConfigMap Volume given the name and items
|
||||
// of the ConfigMap.
|
||||
func getConfigMapVolume(vName string, items []map[string]string, optional bool, defaultMode *int32) *Volume { //nolint:unparam
|
||||
func getConfigMapVolume(vName string, items []map[string]string, optional bool, defaultMode *int32) *Volume {
|
||||
vol := &Volume{
|
||||
VolumeType: "ConfigMap",
|
||||
Name: defaultVolName,
|
||||
@ -6182,4 +6182,32 @@ spec:
|
||||
Expect(execArr[len(execArr)-1]).To(Not(ContainSubstring(arr[len(arr)-1])))
|
||||
})
|
||||
|
||||
It("CVE-2025-9566 regression test - ConfigMap mount", func() {
|
||||
testfile := filepath.Join(podmanTest.TempDir, "testfile")
|
||||
volumeName := "cm-vol"
|
||||
cm := getConfigMap(withConfigMapName(volumeName), withConfigMapData("foo", "content1"))
|
||||
cmYaml, err := getKubeYaml("configmap", cm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ctrName := "ctr1"
|
||||
podName := "pod1"
|
||||
// create a symlink at the volume mount location so we can make sure we don't resolve that to the host location.
|
||||
ctr := getCtr(withName(ctrName), withVolumeMount("/test", "", false), withImage(CITEST_IMAGE), withCmd([]string{"sh", "-c", "ln -sf " + testfile + " /test/foo"}))
|
||||
pod := getPod(withPodName(podName), withVolume(getConfigMapVolume(volumeName, nil, false, nil)), withCtr(ctr))
|
||||
podYaml, err := getKubeYaml("pod", pod)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
yamls := []string{cmYaml, podYaml}
|
||||
err = generateMultiDocKubeYaml(yamls, kubeYaml)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
podmanTest.PodmanExitCleanly("kube", "play", kubeYaml)
|
||||
// wait for the container to finish to ensure the symlink was created
|
||||
podmanTest.PodmanExitCleanly("wait", podName+"-"+ctrName)
|
||||
podmanTest.PodmanExitCleanly("kube", "down", kubeYaml)
|
||||
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
|
||||
kube.WaitWithDefaultTimeout()
|
||||
Expect(kube).To(ExitWithError(125, `cannot create file "foo" at volume mountpoint`))
|
||||
|
||||
Expect(testfile).ToNot(BeAnExistingFile(), "file should never be created on the host")
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user