diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 6ce16595a354..c94ebf2972df 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -1133,7 +1133,10 @@ func validateLocalVolumeSource(ls *api.LocalVolumeSource, fldPath *field.Path) f allErrs := field.ErrorList{} if ls.Path == "" { allErrs = append(allErrs, field.Required(fldPath.Child("path"), "")) + return allErrs } + + allErrs = append(allErrs, validatePathNoBacksteps(ls.Path, fldPath.Child("path"))...) return allErrs } diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index c687acda7f9e..781896834586 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -281,6 +281,21 @@ func TestValidatePersistentVolumes(t *testing.T) { 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 { diff --git a/pkg/volume/local/BUILD b/pkg/volume/local/BUILD index e6172e5ce318..39b25a7c822d 100644 --- a/pkg/volume/local/BUILD +++ b/pkg/volume/local/BUILD @@ -21,6 +21,7 @@ go_library( "//pkg/util/strings:go_default_library", "//pkg/volume:go_default_library", "//pkg/volume/util:go_default_library", + "//pkg/volume/validation: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/types:go_default_library", diff --git a/pkg/volume/local/local.go b/pkg/volume/local/local.go index 9e485c52b347..d98d088ecd11 100644 --- a/pkg/volume/local/local.go +++ b/pkg/volume/local/local.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/util" + "k8s.io/kubernetes/pkg/volume/validation" ) // This is the primary entrypoint for volume plugins. @@ -192,6 +193,11 @@ func (m *localVolumeMounter) SetUpAt(dir string, fsGroup *types.UnixGroupID) 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) 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) { diff --git a/pkg/volume/local/local_test.go b/pkg/volume/local/local_test.go index b417eff3ea3b..9730eff76615 100644 --- a/pkg/volume/local/local_test.go +++ b/pkg/volume/local/local_test.go @@ -75,7 +75,7 @@ func getPersistentPlugin(t *testing.T) (string, volume.PersistentVolumePlugin) { return tmpDir, plug } -func getTestVolume(readOnly bool) *volume.Spec { +func getTestVolume(readOnly bool, path string) *volume.Spec { pv := &v1.PersistentVolume{ ObjectMeta: metav1.ObjectMeta{ Name: testPVName, @@ -83,7 +83,7 @@ func getTestVolume(readOnly bool) *volume.Spec { Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ Local: &v1.LocalVolumeSource{ - Path: "/test-vol", + Path: path, }, }, }, @@ -104,7 +104,7 @@ func TestCanSupport(t *testing.T) { tmpDir, plug := getPlugin(t) defer os.RemoveAll(tmpDir) - if !plug.CanSupport(getTestVolume(false)) { + if !plug.CanSupport(getTestVolume(false, "/test-vol")) { t.Errorf("Expected true") } } @@ -130,7 +130,7 @@ func TestGetVolumeName(t *testing.T) { tmpDir, plug := getPersistentPlugin(t) defer os.RemoveAll(tmpDir) - volName, err := plug.GetVolumeName(getTestVolume(false)) + volName, err := plug.GetVolumeName(getTestVolume(false, "/test-vol")) if err != nil { 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) { tmpDir, plug := getPlugin(t) defer os.RemoveAll(tmpDir) 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 { t.Errorf("Failed to make a new Mounter: %v", err) } @@ -226,7 +243,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { // Read only == true 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 { t.Errorf("Failed to make a new Mounter: %v", err) } @@ -238,7 +255,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { } // 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 { t.Errorf("Failed to make a new Mounter: %v", err) } @@ -259,7 +276,7 @@ func TestUnsupportedPlugins(t *testing.T) { plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) - spec := getTestVolume(false) + spec := getTestVolume(false, "/test-vol") recyclePlug, err := plugMgr.FindRecyclablePluginBySpec(spec) if err == nil && recyclePlug != nil {