mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 05:57:25 +00:00
Merge pull request #64026 from jsafrane/csi-selinux
Automatic merge from submit-queue (batch tested with PRs 63914, 63887, 64116, 64026, 62933). 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>. Enable SELinux relabeling in CSI volumes **What this PR does / why we need it**: CSI volume plugin should provide correct information in `GetAttributes` call so kubelet can ask container runtime to relabel the volume. Therefore CSI volume plugin needs to check if a random volume mounted by a CSI driver supports SELinux or not by checking for "seclabel" mount or superblock option. **Which issue(s) this PR fixes** Fixes #63965 **Release note**: ```release-note NONE ``` @saad-ali @vladimirvivien @davidz627 @cofyc, FYI, I'm changing `struct mountInfo`.
This commit is contained in:
commit
36b1f67617
@ -116,6 +116,10 @@ func (mi *fakeMountInterface) GetFSGroup(pathname string) (int64, error) {
|
|||||||
return -1, errors.New("not implemented")
|
return -1, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mi *fakeMountInterface) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func fakeContainerMgrMountInt() mount.Interface {
|
func fakeContainerMgrMountInt() mount.Interface {
|
||||||
return &fakeMountInterface{
|
return &fakeMountInterface{
|
||||||
[]mount.MountPoint{
|
[]mount.MountPoint{
|
||||||
|
@ -159,3 +159,7 @@ func (m *execMounter) GetMountRefs(pathname string) ([]string, error) {
|
|||||||
func (m *execMounter) GetFSGroup(pathname string) (int64, error) {
|
func (m *execMounter) GetFSGroup(pathname string) (int64, error) {
|
||||||
return m.wrappedMounter.GetFSGroup(pathname)
|
return m.wrappedMounter.GetFSGroup(pathname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *execMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return m.wrappedMounter.GetSELinuxSupport(pathname)
|
||||||
|
}
|
||||||
|
@ -172,3 +172,7 @@ func (fm *fakeMounter) GetMountRefs(pathname string) ([]string, error) {
|
|||||||
func (fm *fakeMounter) GetFSGroup(pathname string) (int64, error) {
|
func (fm *fakeMounter) GetFSGroup(pathname string) (int64, error) {
|
||||||
return -1, errors.New("not implemented")
|
return -1, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fm *fakeMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
@ -106,3 +106,7 @@ func (mounter *execMounter) GetMountRefs(pathname string) ([]string, error) {
|
|||||||
func (mounter *execMounter) GetFSGroup(pathname string) (int64, error) {
|
func (mounter *execMounter) GetFSGroup(pathname string) (int64, error) {
|
||||||
return -1, errors.New("not implemented")
|
return -1, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mounter *execMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
@ -228,3 +228,7 @@ func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
|
|||||||
func (f *FakeMounter) GetFSGroup(pathname string) (int64, error) {
|
func (f *FakeMounter) GetFSGroup(pathname string) (int64, error) {
|
||||||
return -1, errors.New("GetFSGroup not implemented")
|
return -1, errors.New("GetFSGroup not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("GetSELinuxSupport not implemented")
|
||||||
|
}
|
||||||
|
@ -114,6 +114,9 @@ type Interface interface {
|
|||||||
GetMountRefs(pathname string) ([]string, error)
|
GetMountRefs(pathname string) ([]string, error)
|
||||||
// GetFSGroup returns FSGroup of the path.
|
// GetFSGroup returns FSGroup of the path.
|
||||||
GetFSGroup(pathname string) (int64, error)
|
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 {
|
type Subpath struct {
|
||||||
|
@ -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
|
// isShared returns true, if given path is on a mount point that has shared
|
||||||
// mount propagation.
|
// mount propagation.
|
||||||
func isShared(path string, filename string) (bool, error) {
|
func isShared(mount string, mountInfoPath string) (bool, error) {
|
||||||
infos, err := parseMountInfo(filename)
|
info, err := findMountInfo(mount, mountInfoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
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
|
// parse optional parameters
|
||||||
for _, opt := range info.optional {
|
for _, opt := range info.optional {
|
||||||
if strings.HasPrefix(opt, "shared:") {
|
if strings.HasPrefix(opt, "shared:") {
|
||||||
@ -624,6 +611,10 @@ type mountInfo struct {
|
|||||||
mountPoint string
|
mountPoint string
|
||||||
// list of "optional parameters", mount propagation is one of them
|
// list of "optional parameters", mount propagation is one of them
|
||||||
optional []string
|
optional []string
|
||||||
|
// mount options
|
||||||
|
mountOptions []string
|
||||||
|
// super options: per-superblock options.
|
||||||
|
superOptions []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseMountInfo parses /proc/xxx/mountinfo.
|
// 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.
|
// See `man proc` for authoritative description of format of the file.
|
||||||
fields := strings.Fields(line)
|
fields := strings.Fields(line)
|
||||||
if len(fields) < 7 {
|
if len(fields) < 10 {
|
||||||
return nil, fmt.Errorf("wrong number of fields in (expected %d, got %d): %s", 8, len(fields), line)
|
return nil, fmt.Errorf("wrong number of fields in (expected %d, got %d): %s", 10, len(fields), line)
|
||||||
}
|
}
|
||||||
info := mountInfo{
|
info := mountInfo{
|
||||||
mountPoint: fields[4],
|
mountPoint: fields[4],
|
||||||
optional: []string{},
|
mountOptions: strings.Split(fields[5], ","),
|
||||||
|
optional: []string{},
|
||||||
}
|
}
|
||||||
// All fields until "-" are "optional fields".
|
// All fields until "-" are "optional fields".
|
||||||
for i := 6; i < len(fields) && fields[i] != "-"; i++ {
|
for i := 6; i < len(fields) && fields[i] != "-"; i++ {
|
||||||
info.optional = append(info.optional, fields[i])
|
info.optional = append(info.optional, fields[i])
|
||||||
}
|
}
|
||||||
|
superOpts := fields[len(fields)-1]
|
||||||
|
info.superOptions = strings.Split(superOpts, ",")
|
||||||
infos = append(infos, info)
|
infos = append(infos, info)
|
||||||
}
|
}
|
||||||
return infos, nil
|
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
|
// doMakeRShared is common implementation of MakeRShared on Linux. It checks if
|
||||||
// path is shared and bind-mounts it as rshared if needed. mountCmd and
|
// path is shared and bind-mounts it as rshared if needed. mountCmd and
|
||||||
// mountArgs are expected to contain mount-like command, doMakeRShared will add
|
// mountArgs are expected to contain mount-like command, doMakeRShared will add
|
||||||
@ -686,6 +701,27 @@ func doMakeRShared(path string, mountInfoFilename string) error {
|
|||||||
return nil
|
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) {
|
func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||||
newHostPath, err = doBindSubPath(mounter, subPath, os.Getpid())
|
newHostPath, err = doBindSubPath(mounter, subPath, os.Getpid())
|
||||||
// There is no action when the container starts. Bind-mount will be cleaned
|
// 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)
|
return getMountRefsByDev(mounter, realpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return getSELinuxSupport(pathname, procMountInfoPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||||
realpath, err := filepath.EvalSymlinks(pathname)
|
realpath, err := filepath.EvalSymlinks(pathname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1399,8 +1399,10 @@ func TestParseMountInfo(t *testing.T) {
|
|||||||
"simple bind mount",
|
"simple bind mount",
|
||||||
"/var/lib/kubelet",
|
"/var/lib/kubelet",
|
||||||
mountInfo{
|
mountInfo{
|
||||||
mountPoint: "/var/lib/kubelet",
|
mountPoint: "/var/lib/kubelet",
|
||||||
optional: []string{"shared:30"},
|
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) {
|
func TestSafeOpen(t *testing.T) {
|
||||||
defaultPerm := os.FileMode(0750)
|
defaultPerm := os.FileMode(0750)
|
||||||
|
|
||||||
|
@ -134,3 +134,7 @@ func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
|||||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||||
return -1, errors.New("not implemented")
|
return -1, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
@ -456,6 +456,11 @@ func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
|||||||
return 0, nil
|
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.
|
// 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 {
|
func (mounter *Mounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||||
return doSafeMakeDir(pathname, base, perm)
|
return doSafeMakeDir(pathname, base, perm)
|
||||||
|
@ -343,3 +343,7 @@ func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) {
|
|||||||
}
|
}
|
||||||
return getFSGroup(kubeletpath)
|
return getFSGroup(kubeletpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mounter *NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return getSELinuxSupport(pathname, procMountInfoPath)
|
||||||
|
}
|
||||||
|
@ -106,3 +106,7 @@ func (*NsenterMounter) GetMountRefs(pathname string) ([]string, error) {
|
|||||||
func (*NsenterMounter) GetFSGroup(pathname string) (int64, error) {
|
func (*NsenterMounter) GetFSGroup(pathname string) (int64, error) {
|
||||||
return -1, errors.New("not implemented")
|
return -1, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
@ -99,6 +99,10 @@ func (mounter *fakeMounter) GetFSGroup(pathname string) (int64, error) {
|
|||||||
return -1, errors.New("not implemented")
|
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) {
|
func (mounter *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||||
name := path.Base(file)
|
name := path.Base(file)
|
||||||
if strings.HasPrefix(name, "mount") {
|
if strings.HasPrefix(name, "mount") {
|
||||||
|
@ -252,10 +252,18 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *csiMountMgr) GetAttributes() volume.Attributes {
|
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{
|
return volume.Attributes{
|
||||||
ReadOnly: c.readOnly,
|
ReadOnly: c.readOnly,
|
||||||
Managed: !c.readOnly,
|
Managed: !c.readOnly,
|
||||||
SupportsSELinux: false,
|
SupportsSELinux: supportSelinux,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +397,10 @@ func (fftc *fakeFileTypeChecker) GetFSGroup(pathname string) (int64, error) {
|
|||||||
return -1, errors.New("not implemented")
|
return -1, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fftc *fakeFileTypeChecker) GetSELinuxSupport(pathname string) (bool, error) {
|
||||||
|
return false, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func setUp() error {
|
func setUp() error {
|
||||||
err := os.MkdirAll("/tmp/ExistingFolder", os.FileMode(0755))
|
err := os.MkdirAll("/tmp/ExistingFolder", os.FileMode(0755))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -131,6 +131,7 @@ type Mounter interface {
|
|||||||
// idempotent.
|
// idempotent.
|
||||||
SetUpAt(dir string, fsGroup *int64) error
|
SetUpAt(dir string, fsGroup *int64) error
|
||||||
// GetAttributes returns the attributes of the mounter.
|
// GetAttributes returns the attributes of the mounter.
|
||||||
|
// This function is called after SetUp()/SetUpAt().
|
||||||
GetAttributes() Attributes
|
GetAttributes() Attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user