Skip to content

Commit c24b8f6

Browse files
authored
Merge commit from fork
kube play: don't follow volume symlinks onto the host
2 parents c3bf705 + 43fbde4 commit c24b8f6

File tree

4 files changed

+71
-4
lines changed

4 files changed

+71
-4
lines changed

pkg/domain/infra/abi/play.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -810,8 +810,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
810810
defaultMode := v.DefaultMode
811811
// Create files and add data to the volume mountpoint based on the Items in the volume
812812
for k, v := range v.Items {
813-
dataPath := filepath.Join(mountPoint, k)
814-
f, err := os.Create(dataPath)
813+
f, err := openPathSafely(mountPoint, k)
815814
if err != nil {
816815
return nil, nil, fmt.Errorf("cannot create file %q at volume mountpoint %q: %w", k, mountPoint, err)
817816
}
@@ -821,7 +820,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
821820
return nil, nil, err
822821
}
823822
// Set file permissions
824-
if err := os.Chmod(f.Name(), os.FileMode(defaultMode)); err != nil {
823+
if err := f.Chmod(os.FileMode(defaultMode)); err != nil {
825824
return nil, nil, err
826825
}
827826
}

pkg/domain/infra/abi/play_linux.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//go:build !remote
2+
3+
package abi
4+
5+
import (
6+
"os"
7+
8+
securejoin "github.com/cyphar/filepath-securejoin"
9+
)
10+
11+
// openSymlinkPath opens the path under root using securejoin.OpenatInRoot().
12+
func openSymlinkPath(root *os.File, unsafePath string, flags int) (*os.File, error) {
13+
file, err := securejoin.OpenatInRoot(root, unsafePath)
14+
if err != nil {
15+
return nil, err
16+
}
17+
return securejoin.Reopen(file, flags)
18+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build !linux && !remote
2+
3+
package abi
4+
5+
import (
6+
"errors"
7+
"os"
8+
)
9+
10+
// openSymlinkPath is not supported on this platform.
11+
func openSymlinkPath(root *os.File, unsafePath string, flags int) (*os.File, error) {
12+
return nil, errors.New("cannot safely open symlink on this platform")
13+
}

pkg/domain/infra/abi/play_utils.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22

33
package abi
44

5-
import "github.com/containers/podman/v5/libpod/define"
5+
import (
6+
"fmt"
7+
"os"
8+
"strings"
9+
10+
"github.com/containers/podman/v5/libpod/define"
11+
"golang.org/x/sys/unix"
12+
)
613

714
// getSdNotifyMode returns the `sdNotifyAnnotation/$name` for the specified
815
// 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)
1623
}
1724
return mode, define.ValidateSdNotifyMode(mode)
1825
}
26+
27+
// openPathSafely opens the given name under the trusted root path, the unsafeName
28+
// must be a single path component and not contain "/".
29+
// The resulting path will be opened or created if it does not exists.
30+
// Following of symlink is done within staying under root, escapes outsides
31+
// of root are not allowed and prevent.
32+
//
33+
// This custom function is needed because securejoin.SecureJoin() is not race safe
34+
// and the volume might be mounted in another container that could swap in a symlink
35+
// after the function ahs run. securejoin.OpenInRoot() doesn't work either because
36+
// it cannot create files and doesn't work on freebsd.
37+
func openPathSafely(root, unsafeName string) (*os.File, error) {
38+
if strings.Contains(unsafeName, "/") {
39+
return nil, fmt.Errorf("name %q must not contain path separator", unsafeName)
40+
}
41+
fdDir, err := os.OpenFile(root, unix.O_RDONLY, 0)
42+
if err != nil {
43+
return nil, err
44+
}
45+
defer fdDir.Close()
46+
flags := unix.O_CREAT | unix.O_WRONLY | unix.O_TRUNC | unix.O_CLOEXEC
47+
fd, err := unix.Openat(int(fdDir.Fd()), unsafeName, flags|unix.O_NOFOLLOW, 0o644)
48+
if err == nil {
49+
return os.NewFile(uintptr(fd), unsafeName), nil
50+
}
51+
if err == unix.ELOOP {
52+
return openSymlinkPath(fdDir, unsafeName, flags)
53+
}
54+
return nil, &os.PathError{Op: "openat", Path: unsafeName, Err: err}
55+
}

0 commit comments

Comments
 (0)