From 598ca5accc410c3cb0fc307c4d6f961457768567 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Thu, 17 May 2018 13:36:37 +0200 Subject: [PATCH] Add GetSELinuxSupport to mounter. --- .../cm/container_manager_linux_test.go | 4 + pkg/util/mount/exec_mount.go | 4 + pkg/util/mount/exec_mount_test.go | 4 + pkg/util/mount/exec_mount_unsupported.go | 4 + pkg/util/mount/fake.go | 4 + pkg/util/mount/mount.go | 3 + pkg/util/mount/mount_linux.go | 78 ++++++++++++++----- pkg/util/mount/mount_linux_test.go | 53 ++++++++++++- pkg/util/mount/mount_unsupported.go | 4 + pkg/util/mount/mount_windows.go | 5 ++ pkg/util/mount/nsenter_mount.go | 4 + pkg/util/mount/nsenter_mount_unsupported.go | 4 + pkg/util/removeall/removeall_test.go | 4 + pkg/volume/host_path/host_path_test.go | 4 + pkg/volume/volume.go | 1 + 15 files changed, 159 insertions(+), 21 deletions(-) diff --git a/pkg/kubelet/cm/container_manager_linux_test.go b/pkg/kubelet/cm/container_manager_linux_test.go index 2e9bb61b645..cd4b0460c6f 100644 --- a/pkg/kubelet/cm/container_manager_linux_test.go +++ b/pkg/kubelet/cm/container_manager_linux_test.go @@ -116,6 +116,10 @@ func (mi *fakeMountInterface) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("not implemented") } +func (mi *fakeMountInterface) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("not implemented") +} + func fakeContainerMgrMountInt() mount.Interface { return &fakeMountInterface{ []mount.MountPoint{ diff --git a/pkg/util/mount/exec_mount.go b/pkg/util/mount/exec_mount.go index 3145d71dd5b..fcb948aa34e 100644 --- a/pkg/util/mount/exec_mount.go +++ b/pkg/util/mount/exec_mount.go @@ -159,3 +159,7 @@ func (m *execMounter) GetMountRefs(pathname string) ([]string, error) { func (m *execMounter) GetFSGroup(pathname string) (int64, error) { return m.wrappedMounter.GetFSGroup(pathname) } + +func (m *execMounter) GetSELinuxSupport(pathname string) (bool, error) { + return m.wrappedMounter.GetSELinuxSupport(pathname) +} diff --git a/pkg/util/mount/exec_mount_test.go b/pkg/util/mount/exec_mount_test.go index c15a9c16d94..b3af0a46fbb 100644 --- a/pkg/util/mount/exec_mount_test.go +++ b/pkg/util/mount/exec_mount_test.go @@ -172,3 +172,7 @@ func (fm *fakeMounter) GetMountRefs(pathname string) ([]string, error) { func (fm *fakeMounter) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("not implemented") } + +func (fm *fakeMounter) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("not implemented") +} diff --git a/pkg/util/mount/exec_mount_unsupported.go b/pkg/util/mount/exec_mount_unsupported.go index 5274a3938a7..cbb5bbc1591 100644 --- a/pkg/util/mount/exec_mount_unsupported.go +++ b/pkg/util/mount/exec_mount_unsupported.go @@ -106,3 +106,7 @@ func (mounter *execMounter) GetMountRefs(pathname string) ([]string, error) { func (mounter *execMounter) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("not implemented") } + +func (mounter *execMounter) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("not implemented") +} diff --git a/pkg/util/mount/fake.go b/pkg/util/mount/fake.go index ef79e731954..f82f669b2eb 100644 --- a/pkg/util/mount/fake.go +++ b/pkg/util/mount/fake.go @@ -228,3 +228,7 @@ func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) { func (f *FakeMounter) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("GetFSGroup not implemented") } + +func (f *FakeMounter) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("GetSELinuxSupport not implemented") +} diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index a7a08fa82f4..0c59ca9ebb7 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -114,6 +114,9 @@ type Interface interface { GetMountRefs(pathname string) ([]string, error) // GetFSGroup returns FSGroup of the path. GetFSGroup(pathname string) (int64, error) + // GetSELinuxSupport returns true if given path is on a mount that supports + // SELinux. + GetSELinuxSupport(pathname string) (bool, error) } type Subpath struct { diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index 05bf54dae8e..dbb864cd479 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -591,25 +591,12 @@ func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) { // isShared returns true, if given path is on a mount point that has shared // mount propagation. -func isShared(path string, filename string) (bool, error) { - infos, err := parseMountInfo(filename) +func isShared(mount string, mountInfoPath string) (bool, error) { + info, err := findMountInfo(mount, mountInfoPath) if err != nil { return false, err } - // process /proc/xxx/mountinfo in backward order and find the first mount - // point that is prefix of 'path' - that's the mount where path resides - var info *mountInfo - for i := len(infos) - 1; i >= 0; i-- { - if strings.HasPrefix(path, infos[i].mountPoint) { - info = &infos[i] - break - } - } - if info == nil { - return false, fmt.Errorf("cannot find mount point for %q", path) - } - // parse optional parameters for _, opt := range info.optional { if strings.HasPrefix(opt, "shared:") { @@ -624,6 +611,10 @@ type mountInfo struct { mountPoint string // list of "optional parameters", mount propagation is one of them optional []string + // mount options + mountOptions []string + // super options: per-superblock options. + superOptions []string } // parseMountInfo parses /proc/xxx/mountinfo. @@ -642,22 +633,46 @@ func parseMountInfo(filename string) ([]mountInfo, error) { } // See `man proc` for authoritative description of format of the file. fields := strings.Fields(line) - if len(fields) < 7 { - return nil, fmt.Errorf("wrong number of fields in (expected %d, got %d): %s", 8, len(fields), line) + if len(fields) < 10 { + return nil, fmt.Errorf("wrong number of fields in (expected %d, got %d): %s", 10, len(fields), line) } info := mountInfo{ - mountPoint: fields[4], - optional: []string{}, + mountPoint: fields[4], + mountOptions: strings.Split(fields[5], ","), + optional: []string{}, } // All fields until "-" are "optional fields". for i := 6; i < len(fields) && fields[i] != "-"; i++ { info.optional = append(info.optional, fields[i]) } + superOpts := fields[len(fields)-1] + info.superOptions = strings.Split(superOpts, ",") infos = append(infos, info) } return infos, nil } +func findMountInfo(path, mountInfoPath string) (mountInfo, error) { + infos, err := parseMountInfo(mountInfoPath) + if err != nil { + return mountInfo{}, err + } + + // process /proc/xxx/mountinfo in backward order and find the first mount + // point that is prefix of 'path' - that's the mount where path resides + var info *mountInfo + for i := len(infos) - 1; i >= 0; i-- { + if pathWithinBase(path, infos[i].mountPoint) { + info = &infos[i] + break + } + } + if info == nil { + return mountInfo{}, fmt.Errorf("cannot find mount point for %q", path) + } + return *info, nil +} + // doMakeRShared is common implementation of MakeRShared on Linux. It checks if // path is shared and bind-mounts it as rshared if needed. mountCmd and // mountArgs are expected to contain mount-like command, doMakeRShared will add @@ -686,6 +701,27 @@ func doMakeRShared(path string, mountInfoFilename string) error { return nil } +// getSELinuxSupport is common implementation of GetSELinuxSupport on Linux. +func getSELinuxSupport(path string, mountInfoFilename string) (bool, error) { + info, err := findMountInfo(path, mountInfoFilename) + if err != nil { + return false, err + } + + // "seclabel" can be both in mount options and super options. + for _, opt := range info.superOptions { + if opt == "seclabel" { + return true, nil + } + } + for _, opt := range info.mountOptions { + if opt == "seclabel" { + return true, nil + } + } + return false, nil +} + func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) { newHostPath, err = doBindSubPath(mounter, subPath, os.Getpid()) // There is no action when the container starts. Bind-mount will be cleaned @@ -934,6 +970,10 @@ func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) { return getMountRefsByDev(mounter, realpath) } +func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) { + return getSELinuxSupport(pathname, procMountInfoPath) +} + func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) { realpath, err := filepath.EvalSymlinks(pathname) if err != nil { diff --git a/pkg/util/mount/mount_linux_test.go b/pkg/util/mount/mount_linux_test.go index 7bec4e447ac..6028d608e97 100644 --- a/pkg/util/mount/mount_linux_test.go +++ b/pkg/util/mount/mount_linux_test.go @@ -1399,8 +1399,10 @@ func TestParseMountInfo(t *testing.T) { "simple bind mount", "/var/lib/kubelet", mountInfo{ - mountPoint: "/var/lib/kubelet", - optional: []string{"shared:30"}, + mountPoint: "/var/lib/kubelet", + optional: []string{"shared:30"}, + mountOptions: []string{"rw", "relatime"}, + superOptions: []string{"rw", "commit=30", "data=ordered"}, }, }, } @@ -1427,6 +1429,53 @@ func TestParseMountInfo(t *testing.T) { } } +func TestGetSELinuxSupport(t *testing.T) { + info := + `62 0 253:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered +78 62 0:41 / /tmp rw,nosuid,nodev shared:30 - tmpfs tmpfs rw,seclabel +83 63 0:44 / /var/lib/bar rw,relatime - tmpfs tmpfs rw +227 62 253:0 /var/lib/docker/devicemapper /var/lib/docker/devicemapper rw,relatime - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered +150 23 1:58 / /media/nfs_vol rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223 +` + tempDir, filename, err := writeFile(info) + if err != nil { + t.Fatalf("cannot create temporary file: %v", err) + } + defer os.RemoveAll(tempDir) + + tests := []struct { + name string + mountPoint string + expectedResult bool + }{ + { + "ext4 on /", + "/", + true, + }, + { + "tmpfs on /var/lib/bar", + "/var/lib/bar", + false, + }, + { + "nfsv4", + "/media/nfs_vol", + false, + }, + } + + for _, test := range tests { + out, err := getSELinuxSupport(test.mountPoint, filename) + if err != nil { + t.Errorf("Test %s failed with error: %s", test.name, err) + } + if test.expectedResult != out { + t.Errorf("Test %s failed: expected %v, got %v", test.name, test.expectedResult, out) + } + } +} + func TestSafeOpen(t *testing.T) { defaultPerm := os.FileMode(0750) diff --git a/pkg/util/mount/mount_unsupported.go b/pkg/util/mount/mount_unsupported.go index 87b8e334825..6e268e0f43d 100644 --- a/pkg/util/mount/mount_unsupported.go +++ b/pkg/util/mount/mount_unsupported.go @@ -134,3 +134,7 @@ func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) { func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("not implemented") } + +func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("not implemented") +} diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index 0c10733e9de..f31f99bd66d 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -456,6 +456,11 @@ func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) { return 0, nil } +func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) { + // Windows does not support SELinux. + return false, nil +} + // SafeMakeDir makes sure that the created directory does not escape given base directory mis-using symlinks. func (mounter *Mounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error { return doSafeMakeDir(pathname, base, perm) diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 17d730bf758..4c48d673254 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -343,3 +343,7 @@ func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) { } return getFSGroup(kubeletpath) } + +func (mounter *NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) { + return getSELinuxSupport(pathname, procMountInfoPath) +} diff --git a/pkg/util/mount/nsenter_mount_unsupported.go b/pkg/util/mount/nsenter_mount_unsupported.go index 787ba6b7197..8cf79de472c 100644 --- a/pkg/util/mount/nsenter_mount_unsupported.go +++ b/pkg/util/mount/nsenter_mount_unsupported.go @@ -106,3 +106,7 @@ func (*NsenterMounter) GetMountRefs(pathname string) ([]string, error) { func (*NsenterMounter) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("not implemented") } + +func (*NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("not implemented") +} diff --git a/pkg/util/removeall/removeall_test.go b/pkg/util/removeall/removeall_test.go index ad0efac8d1d..134326369ff 100644 --- a/pkg/util/removeall/removeall_test.go +++ b/pkg/util/removeall/removeall_test.go @@ -99,6 +99,10 @@ func (mounter *fakeMounter) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("not implemented") } +func (mounter *fakeMounter) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("not implemented") +} + func (mounter *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) { name := path.Base(file) if strings.HasPrefix(name, "mount") { diff --git a/pkg/volume/host_path/host_path_test.go b/pkg/volume/host_path/host_path_test.go index b48c0264449..199880c247e 100644 --- a/pkg/volume/host_path/host_path_test.go +++ b/pkg/volume/host_path/host_path_test.go @@ -397,6 +397,10 @@ func (fftc *fakeFileTypeChecker) GetFSGroup(pathname string) (int64, error) { return -1, errors.New("not implemented") } +func (fftc *fakeFileTypeChecker) GetSELinuxSupport(pathname string) (bool, error) { + return false, errors.New("not implemented") +} + func setUp() error { err := os.MkdirAll("/tmp/ExistingFolder", os.FileMode(0755)) if err != nil { diff --git a/pkg/volume/volume.go b/pkg/volume/volume.go index 471963556a8..161d84faf49 100644 --- a/pkg/volume/volume.go +++ b/pkg/volume/volume.go @@ -131,6 +131,7 @@ type Mounter interface { // idempotent. SetUpAt(dir string, fsGroup *int64) error // GetAttributes returns the attributes of the mounter. + // This function is called after SetUp()/SetUpAt(). GetAttributes() Attributes }