Merge pull request #61480 from gnufied/fix-unix-socket-subpath-mount

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Fix unix socket subpath mount

Fixes https://github.com/kubernetes/kubernetes/issues/61377

```release-note
Fix mounting of UNIX sockets(and other special files) in subpaths
```
pull/8/head
Kubernetes Submit Queue 2018-03-25 20:40:28 -07:00 committed by GitHub
commit 0aa9a69bf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 3 deletions

View File

@ -54,7 +54,9 @@ const (
// place for subpath mounts // place for subpath mounts
containerSubPathDirectoryName = "volume-subpaths" containerSubPathDirectoryName = "volume-subpaths"
// syscall.Openat flags used to traverse directories not following symlinks // syscall.Openat flags used to traverse directories not following symlinks
nofollowFlags = syscall.O_RDONLY | syscall.O_NOFOLLOW nofollowFlags = unix.O_RDONLY | unix.O_NOFOLLOW
// flags for getting file descriptor without following the symlink
openFDFlags = unix.O_NOFOLLOW | unix.O_PATH
) )
// Mounter provides the default implementation of mount.Interface // Mounter provides the default implementation of mount.Interface
@ -1074,7 +1076,9 @@ func findExistingPrefix(base, pathname string) (string, []string, error) {
} }
}() }()
for i, dir := range dirs { for i, dir := range dirs {
childFD, err := syscall.Openat(fd, dir, syscall.O_RDONLY, 0) // Using O_PATH here will prevent hangs in case user replaces directory with
// fifo
childFD, err := syscall.Openat(fd, dir, unix.O_PATH, 0)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return currentPath, dirs[i:], nil return currentPath, dirs[i:], nil
@ -1136,11 +1140,21 @@ func doSafeOpen(pathname string, base string) (int, error) {
} }
glog.V(5).Infof("Opening path %s", currentPath) glog.V(5).Infof("Opening path %s", currentPath)
childFD, err = syscall.Openat(parentFD, seg, nofollowFlags, 0) childFD, err = syscall.Openat(parentFD, seg, openFDFlags, 0)
if err != nil { if err != nil {
return -1, fmt.Errorf("cannot open %s: %s", currentPath, err) return -1, fmt.Errorf("cannot open %s: %s", currentPath, err)
} }
var deviceStat unix.Stat_t
err := unix.Fstat(childFD, &deviceStat)
if err != nil {
return -1, fmt.Errorf("Error running fstat on %s with %v", currentPath, err)
}
fileFmt := deviceStat.Mode & syscall.S_IFMT
if fileFmt == syscall.S_IFLNK {
return -1, fmt.Errorf("Unexpected symlink found %s", currentPath)
}
// Close parentFD // Close parentFD
if err = syscall.Close(parentFD); err != nil { if err = syscall.Close(parentFD); err != nil {
return -1, fmt.Errorf("closing fd for %q failed: %v", filepath.Dir(currentPath), err) return -1, fmt.Errorf("closing fd for %q failed: %v", filepath.Dir(currentPath), err)

View File

@ -21,6 +21,7 @@ package mount
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@ -1179,6 +1180,44 @@ func TestBindSubPath(t *testing.T) {
}, },
expectError: false, expectError: false,
}, },
{
name: "subpath-mounting-unix-socket",
prepare: func(base string) ([]string, string, string, error) {
volpath, subpathMount := getTestPaths(base)
mounts := []string{subpathMount}
if err := os.MkdirAll(volpath, defaultPerm); err != nil {
return nil, "", "", err
}
if err := os.MkdirAll(subpathMount, defaultPerm); err != nil {
return nil, "", "", err
}
testSocketFile := filepath.Join(volpath, "mount_test.sock")
_, err := net.Listen("unix", testSocketFile)
return mounts, volpath, testSocketFile, err
},
expectError: false,
},
{
name: "subpath-mounting-fifo",
prepare: func(base string) ([]string, string, string, error) {
volpath, subpathMount := getTestPaths(base)
mounts := []string{subpathMount}
if err := os.MkdirAll(volpath, defaultPerm); err != nil {
return nil, "", "", err
}
if err := os.MkdirAll(subpathMount, defaultPerm); err != nil {
return nil, "", "", err
}
testFifo := filepath.Join(volpath, "mount_test.fifo")
err := syscall.Mkfifo(testFifo, 0)
return mounts, volpath, testFifo, err
},
expectError: false,
},
} }
for _, test := range tests { for _, test := range tests {
@ -1308,6 +1347,7 @@ func TestParseMountInfo(t *testing.T) {
func TestSafeOpen(t *testing.T) { func TestSafeOpen(t *testing.T) {
defaultPerm := os.FileMode(0750) defaultPerm := os.FileMode(0750)
tests := []struct { tests := []struct {
name string name string
// Function that prepares directory structure for the test under given // Function that prepares directory structure for the test under given
@ -1435,6 +1475,32 @@ func TestSafeOpen(t *testing.T) {
"test", "test",
true, true,
}, },
{
"mounting-unix-socket",
func(base string) error {
testSocketFile := filepath.Join(base, "mount_test.sock")
_, err := net.Listen("unix", testSocketFile)
if err != nil {
return fmt.Errorf("Error preparing socket file %s with %v", testSocketFile, err)
}
return nil
},
"mount_test.sock",
false,
},
{
"mounting-unix-socket-in-middle",
func(base string) error {
testSocketFile := filepath.Join(base, "mount_test.sock")
_, err := net.Listen("unix", testSocketFile)
if err != nil {
return fmt.Errorf("Error preparing socket file %s with %v", testSocketFile, err)
}
return nil
},
"mount_test.sock/bar",
true,
},
} }
for _, test := range tests { for _, test := range tests {
@ -1551,6 +1617,17 @@ func TestFindExistingPrefix(t *testing.T) {
[]string{"test", "directory"}, []string{"test", "directory"},
false, false,
}, },
{
"with-fifo-in-middle",
func(base string) error {
testFifo := filepath.Join(base, "mount_test.fifo")
return syscall.Mkfifo(testFifo, 0)
},
"mount_test.fifo/directory",
"",
nil,
true,
},
} }
for _, test := range tests { for _, test := range tests {