Merge pull request #47236 from dixudx/not_allow_backsteps_in_local_volume

Automatic merge from submit-queue (batch tested with PRs 34515, 47236, 46694, 47819, 47792)

not allow backsteps in local volume plugin

**Which issue this PR fixes** : fixes #47207

**Special notes for your reviewer**:
cc @msau42 @ddysher
Just follow @liggitt [commented](https://github.com/kubernetes/kubernetes/issues/47107#issuecomment-306831175).

**Release note**:
```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2017-06-21 13:30:09 -07:00 committed by GitHub
commit 1184ce869a
5 changed files with 50 additions and 8 deletions

View File

@ -1133,7 +1133,10 @@ func validateLocalVolumeSource(ls *api.LocalVolumeSource, fldPath *field.Path) f
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if ls.Path == "" { if ls.Path == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("path"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
return allErrs
} }
allErrs = append(allErrs, validatePathNoBacksteps(ls.Path, fldPath.Child("path"))...)
return allErrs return allErrs
} }

View File

@ -281,6 +281,21 @@ func TestValidatePersistentVolumes(t *testing.T) {
StorageClassName: "backstep-hostpath", StorageClassName: "backstep-hostpath",
}), }),
}, },
"bad-local-volume-backsteps": {
isExpectedFailure: true,
volume: testVolume("foo", "", api.PersistentVolumeSpec{
Capacity: api.ResourceList{
api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
},
AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
PersistentVolumeSource: api.PersistentVolumeSource{
Local: &api.LocalVolumeSource{
Path: "/foo/..",
},
},
StorageClassName: "backstep-local",
}),
},
} }
for name, scenario := range scenarios { for name, scenario := range scenarios {

View File

@ -21,6 +21,7 @@ go_library(
"//pkg/util/strings:go_default_library", "//pkg/util/strings:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library", "//pkg/volume/util:go_default_library",
"//pkg/volume/validation:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",

View File

@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util"
"k8s.io/kubernetes/pkg/volume/validation"
) )
// This is the primary entrypoint for volume plugins. // This is the primary entrypoint for volume plugins.
@ -192,6 +193,11 @@ func (m *localVolumeMounter) SetUpAt(dir string, fsGroup *types.UnixGroupID) err
return err return err
} }
err := validation.ValidatePathNoBacksteps(m.globalPath)
if err != nil {
return fmt.Errorf("invalid path: %s %v", m.globalPath, err)
}
notMnt, err := m.mounter.IsLikelyNotMountPoint(dir) notMnt, err := m.mounter.IsLikelyNotMountPoint(dir)
glog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly) glog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {

View File

@ -75,7 +75,7 @@ func getPersistentPlugin(t *testing.T) (string, volume.PersistentVolumePlugin) {
return tmpDir, plug return tmpDir, plug
} }
func getTestVolume(readOnly bool) *volume.Spec { func getTestVolume(readOnly bool, path string) *volume.Spec {
pv := &v1.PersistentVolume{ pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: testPVName, Name: testPVName,
@ -83,7 +83,7 @@ func getTestVolume(readOnly bool) *volume.Spec {
Spec: v1.PersistentVolumeSpec{ Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{ PersistentVolumeSource: v1.PersistentVolumeSource{
Local: &v1.LocalVolumeSource{ Local: &v1.LocalVolumeSource{
Path: "/test-vol", Path: path,
}, },
}, },
}, },
@ -104,7 +104,7 @@ func TestCanSupport(t *testing.T) {
tmpDir, plug := getPlugin(t) tmpDir, plug := getPlugin(t)
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
if !plug.CanSupport(getTestVolume(false)) { if !plug.CanSupport(getTestVolume(false, "/test-vol")) {
t.Errorf("Expected true") t.Errorf("Expected true")
} }
} }
@ -130,7 +130,7 @@ func TestGetVolumeName(t *testing.T) {
tmpDir, plug := getPersistentPlugin(t) tmpDir, plug := getPersistentPlugin(t)
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
volName, err := plug.GetVolumeName(getTestVolume(false)) volName, err := plug.GetVolumeName(getTestVolume(false, "/test-vol"))
if err != nil { if err != nil {
t.Errorf("Failed to get volume name: %v", err) t.Errorf("Failed to get volume name: %v", err)
} }
@ -139,12 +139,29 @@ func TestGetVolumeName(t *testing.T) {
} }
} }
func TestInvalidLocalPath(t *testing.T) {
tmpDir, plug := getPlugin(t)
defer os.RemoveAll(tmpDir)
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
mounter, err := plug.NewMounter(getTestVolume(false, "/no/backsteps/allowed/.."), pod, volume.VolumeOptions{})
if err != nil {
t.Fatal(err)
}
err = mounter.SetUp(nil)
expectedMsg := "invalid path: /no/backsteps/allowed/.. must not contain '..'"
if err.Error() != expectedMsg {
t.Fatalf("expected error `%s` but got `%s`", expectedMsg, err)
}
}
func TestMountUnmount(t *testing.T) { func TestMountUnmount(t *testing.T) {
tmpDir, plug := getPlugin(t) tmpDir, plug := getPlugin(t)
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
mounter, err := plug.NewMounter(getTestVolume(false), pod, volume.VolumeOptions{}) mounter, err := plug.NewMounter(getTestVolume(false, "/test-vol"), pod, volume.VolumeOptions{})
if err != nil { if err != nil {
t.Errorf("Failed to make a new Mounter: %v", err) t.Errorf("Failed to make a new Mounter: %v", err)
} }
@ -226,7 +243,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
// Read only == true // Read only == true
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}} pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
mounter, err := plug.NewMounter(getTestVolume(true), pod, volume.VolumeOptions{}) mounter, err := plug.NewMounter(getTestVolume(true, "/test-vol"), pod, volume.VolumeOptions{})
if err != nil { if err != nil {
t.Errorf("Failed to make a new Mounter: %v", err) t.Errorf("Failed to make a new Mounter: %v", err)
} }
@ -238,7 +255,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
} }
// Read only == false // Read only == false
mounter, err = plug.NewMounter(getTestVolume(false), pod, volume.VolumeOptions{}) mounter, err = plug.NewMounter(getTestVolume(false, "/test-vol"), pod, volume.VolumeOptions{})
if err != nil { if err != nil {
t.Errorf("Failed to make a new Mounter: %v", err) t.Errorf("Failed to make a new Mounter: %v", err)
} }
@ -259,7 +276,7 @@ func TestUnsupportedPlugins(t *testing.T) {
plugMgr := volume.VolumePluginMgr{} plugMgr := volume.VolumePluginMgr{}
plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil))
spec := getTestVolume(false) spec := getTestVolume(false, "/test-vol")
recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec) recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec)
if err == nil && recyclePlug != nil { if err == nil && recyclePlug != nil {