Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable SELinux relabeling in CSI volumes #64026

Merged
merged 2 commits into from
May 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/kubelet/cm/container_manager_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
4 changes: 4 additions & 0 deletions pkg/util/mount/exec_mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
4 changes: 4 additions & 0 deletions pkg/util/mount/exec_mount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
4 changes: 4 additions & 0 deletions pkg/util/mount/exec_mount_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
4 changes: 4 additions & 0 deletions pkg/util/mount/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
3 changes: 3 additions & 0 deletions pkg/util/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
78 changes: 59 additions & 19 deletions pkg/util/mount/mount_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:") {
Expand All @@ -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.
Expand All @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this a constant?

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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
53 changes: 51 additions & 2 deletions pkg/util/mount/mount_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
},
},
}
Expand All @@ -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)

Expand Down
4 changes: 4 additions & 0 deletions pkg/util/mount/mount_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
5 changes: 5 additions & 0 deletions pkg/util/mount/mount_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions pkg/util/mount/nsenter_mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
4 changes: 4 additions & 0 deletions pkg/util/mount/nsenter_mount_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
4 changes: 4 additions & 0 deletions pkg/util/removeall/removeall_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
10 changes: 9 additions & 1 deletion pkg/volume/csi/csi_mounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,18 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
}

func (c *csiMountMgr) GetAttributes() volume.Attributes {
mounter := c.plugin.host.GetMounter(c.plugin.GetPluginName())
path := c.GetPath()
supportSelinux, err := mounter.GetSELinuxSupport(path)
if err != nil {
glog.V(2).Info(log("error checking for SELinux support: %s", err))
// Best guess
supportSelinux = false
}
return volume.Attributes{
ReadOnly: c.readOnly,
Managed: !c.readOnly,
SupportsSELinux: false,
SupportsSELinux: supportSelinux,
}
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/host_path/host_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down