Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
48b3fb84
Commit
48b3fb84
authored
Jun 10, 2017
by
Josh Horwitz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
do not allow backsteps in host volume plugin
Fixes #47107
parent
0a1b7d94
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
249 additions
and
62 deletions
+249
-62
validation.go
pkg/api/validation/validation.go
+16
-3
validation_test.go
pkg/api/validation/validation_test.go
+27
-0
BUILD
pkg/kubelet/BUILD
+1
-0
kubelet_pods.go
pkg/kubelet/kubelet_pods.go
+10
-0
kubelet_pods_test.go
pkg/kubelet/kubelet_pods_test.go
+120
-59
BUILD
pkg/volume/host_path/BUILD
+1
-0
host_path.go
pkg/volume/host_path/host_path.go
+6
-0
host_path_test.go
pkg/volume/host_path/host_path_test.go
+25
-0
pv_validation.go
pkg/volume/validation/pv_validation.go
+16
-0
pv_validation_test.go
pkg/volume/validation/pv_validation_test.go
+27
-0
No files found.
pkg/api/validation/validation.go
View file @
48b3fb84
...
...
@@ -20,8 +20,8 @@ import (
"encoding/json"
"fmt"
"net"
"os"
"path"
"path/filepath"
"reflect"
"regexp"
"strconv"
...
...
@@ -627,7 +627,10 @@ func validateHostPathVolumeSource(hostPath *api.HostPathVolumeSource, fldPath *f
allErrs
:=
field
.
ErrorList
{}
if
len
(
hostPath
.
Path
)
==
0
{
allErrs
=
append
(
allErrs
,
field
.
Required
(
fldPath
.
Child
(
"path"
),
""
))
return
allErrs
}
allErrs
=
append
(
allErrs
,
validatePathNoBacksteps
(
hostPath
.
Path
,
fldPath
.
Child
(
"path"
))
...
)
return
allErrs
}
...
...
@@ -958,8 +961,18 @@ func validateLocalDescendingPath(targetPath string, fldPath *field.Path) field.E
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
,
targetPath
,
"must be a relative path"
))
}
// TODO: this assumes the OS of apiserver & nodes are the same
parts
:=
strings
.
Split
(
targetPath
,
string
(
os
.
PathSeparator
))
allErrs
=
append
(
allErrs
,
validatePathNoBacksteps
(
targetPath
,
fldPath
)
...
)
return
allErrs
}
// validatePathNoBacksteps makes sure the targetPath does not have any `..` path elements when split
//
// This assumes the OS of the apiserver and the nodes are the same. The same check should be done
// on the node to ensure there are no backsteps.
func
validatePathNoBacksteps
(
targetPath
string
,
fldPath
*
field
.
Path
)
field
.
ErrorList
{
allErrs
:=
field
.
ErrorList
{}
parts
:=
strings
.
Split
(
filepath
.
ToSlash
(
targetPath
),
"/"
)
for
_
,
item
:=
range
parts
{
if
item
==
".."
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
,
targetPath
,
"must not contain '..'"
))
...
...
pkg/api/validation/validation_test.go
View file @
48b3fb84
...
...
@@ -268,6 +268,19 @@ func TestValidatePersistentVolumes(t *testing.T) {
StorageClassName
:
"test-storage-class"
,
}),
},
"bad-hostpath-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
{
HostPath
:
&
api
.
HostPathVolumeSource
{
Path
:
"/foo/.."
},
},
StorageClassName
:
"backstep-hostpath"
,
}),
},
}
for
name
,
scenario
:=
range
scenarios
{
...
...
@@ -1102,6 +1115,20 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
{
name
:
"invalid HostPath backsteps"
,
vol
:
api
.
Volume
{
Name
:
"hostpath"
,
VolumeSource
:
api
.
VolumeSource
{
HostPath
:
&
api
.
HostPathVolumeSource
{
Path
:
"/mnt/path/.."
,
},
},
},
errtype
:
field
.
ErrorTypeInvalid
,
errfield
:
"path"
,
errdetail
:
"must not contain '..'"
,
},
// GcePersistentDisk
{
name
:
"valid GcePersistentDisk"
,
...
...
pkg/kubelet/BUILD
View file @
48b3fb84
...
...
@@ -111,6 +111,7 @@ go_library(
"//pkg/volume/util:go_default_library",
"//pkg/volume/util/types:go_default_library",
"//pkg/volume/util/volumehelper:go_default_library",
"//pkg/volume/validation:go_default_library",
"//plugin/pkg/scheduler/algorithm:go_default_library",
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
"//third_party/forked/golang/expansion:go_default_library",
...
...
pkg/kubelet/kubelet_pods.go
View file @
48b3fb84
...
...
@@ -61,6 +61,7 @@ import (
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
volumevalidation
"k8s.io/kubernetes/pkg/volume/validation"
"k8s.io/kubernetes/third_party/forked/golang/expansion"
)
...
...
@@ -138,6 +139,15 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
return
nil
,
err
}
if
mount
.
SubPath
!=
""
{
if
filepath
.
IsAbs
(
mount
.
SubPath
)
{
return
nil
,
fmt
.
Errorf
(
"error SubPath `%s` must not be an absolute path"
,
mount
.
SubPath
)
}
err
=
volumevalidation
.
ValidatePathNoBacksteps
(
mount
.
SubPath
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"unable to provision SubPath `%s`: %v"
,
mount
.
SubPath
,
err
)
}
fileinfo
,
err
:=
os
.
Lstat
(
hostPath
)
if
err
!=
nil
{
return
nil
,
err
...
...
pkg/kubelet/kubelet_pods_test.go
View file @
48b3fb84
...
...
@@ -42,76 +42,137 @@ import (
)
func
TestMakeMounts
(
t
*
testing
.
T
)
{
container
:=
v1
.
Container
{
VolumeMounts
:
[]
v1
.
VolumeMount
{
{
MountPath
:
"/etc/hosts"
,
Name
:
"disk"
,
ReadOnly
:
false
,
testCases
:=
map
[
string
]
struct
{
container
v1
.
Container
podVolumes
kubecontainer
.
VolumeMap
expectErr
bool
expectedErrMsg
string
expectedMounts
[]
kubecontainer
.
Mount
}{
"valid mounts"
:
{
podVolumes
:
kubecontainer
.
VolumeMap
{
"disk"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/mnt/disk"
}},
"disk4"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/mnt/host"
}},
"disk5"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/var/lib/kubelet/podID/volumes/empty/disk5"
}},
},
container
:
v1
.
Container
{
VolumeMounts
:
[]
v1
.
VolumeMount
{
{
MountPath
:
"/etc/hosts"
,
Name
:
"disk"
,
ReadOnly
:
false
,
},
{
MountPath
:
"/mnt/path3"
,
Name
:
"disk"
,
ReadOnly
:
true
,
},
{
MountPath
:
"/mnt/path4"
,
Name
:
"disk4"
,
ReadOnly
:
false
,
},
{
MountPath
:
"/mnt/path5"
,
Name
:
"disk5"
,
ReadOnly
:
false
,
},
},
},
{
MountPath
:
"/mnt/path3"
,
Name
:
"disk"
,
ReadOnly
:
true
,
expectedMounts
:
[]
kubecontainer
.
Mount
{
{
Name
:
"disk"
,
ContainerPath
:
"/etc/hosts"
,
HostPath
:
"/mnt/disk"
,
ReadOnly
:
false
,
SELinuxRelabel
:
false
,
},
{
Name
:
"disk"
,
ContainerPath
:
"/mnt/path3"
,
HostPath
:
"/mnt/disk"
,
ReadOnly
:
true
,
SELinuxRelabel
:
false
,
},
{
Name
:
"disk4"
,
ContainerPath
:
"/mnt/path4"
,
HostPath
:
"/mnt/host"
,
ReadOnly
:
false
,
SELinuxRelabel
:
false
,
},
{
Name
:
"disk5"
,
ContainerPath
:
"/mnt/path5"
,
HostPath
:
"/var/lib/kubelet/podID/volumes/empty/disk5"
,
ReadOnly
:
false
,
SELinuxRelabel
:
false
,
},
},
{
MountPath
:
"/mnt/path4"
,
Name
:
"disk4"
,
ReadOnly
:
false
,
expectErr
:
false
,
},
"invalid absolute SubPath"
:
{
podVolumes
:
kubecontainer
.
VolumeMap
{
"disk"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/mnt/disk"
}},
},
{
MountPath
:
"/mnt/path5"
,
Name
:
"disk5"
,
ReadOnly
:
false
,
container
:
v1
.
Container
{
VolumeMounts
:
[]
v1
.
VolumeMount
{
{
MountPath
:
"/mnt/path3"
,
SubPath
:
"/must/not/be/absolute"
,
Name
:
"disk"
,
ReadOnly
:
true
,
},
},
},
expectErr
:
true
,
expectedErrMsg
:
"error SubPath `/must/not/be/absolute` must not be an absolute path"
,
},
"invalid SubPath with backsteps"
:
{
podVolumes
:
kubecontainer
.
VolumeMap
{
"disk"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/mnt/disk"
}},
},
container
:
v1
.
Container
{
VolumeMounts
:
[]
v1
.
VolumeMount
{
{
MountPath
:
"/mnt/path3"
,
SubPath
:
"no/backsteps/../allowed"
,
Name
:
"disk"
,
ReadOnly
:
true
,
},
},
},
expectErr
:
true
,
expectedErrMsg
:
"unable to provision SubPath `no/backsteps/../allowed`: must not contain '..'"
,
},
}
podVolumes
:=
kubecontainer
.
VolumeMap
{
"disk"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/mnt/disk"
}},
"disk4"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/mnt/host"
}},
"disk5"
:
kubecontainer
.
VolumeInfo
{
Mounter
:
&
stubVolume
{
path
:
"/var/lib/kubelet/podID/volumes/empty/disk5"
}},
}
for
name
,
tc
:=
range
testCases
{
t
.
Run
(
name
,
func
(
t
*
testing
.
T
)
{
pod
:=
v1
.
Pod
{
Spec
:
v1
.
PodSpec
{
HostNetwork
:
true
,
},
}
pod
:=
v1
.
Pod
{
Spec
:
v1
.
PodSpec
{
HostNetwork
:
true
,
},
}
mounts
,
err
:=
makeMounts
(
&
pod
,
"/pod"
,
&
tc
.
container
,
"fakepodname"
,
""
,
""
,
tc
.
podVolumes
)
mounts
,
_
:=
makeMounts
(
&
pod
,
"/pod"
,
&
container
,
"fakepodname"
,
""
,
""
,
podVolumes
)
// validate only the error if we expect an error
if
tc
.
expectErr
{
if
err
==
nil
||
err
.
Error
()
!=
tc
.
expectedErrMsg
{
t
.
Fatalf
(
"expected error message `%s` but got `%v`"
,
tc
.
expectedErrMsg
,
err
)
}
return
}
expectedMounts
:=
[]
kubecontainer
.
Mount
{
{
Name
:
"disk"
,
ContainerPath
:
"/etc/hosts"
,
HostPath
:
"/mnt/disk"
,
ReadOnly
:
false
,
SELinuxRelabel
:
false
,
},
{
Name
:
"disk"
,
ContainerPath
:
"/mnt/path3"
,
HostPath
:
"/mnt/disk"
,
ReadOnly
:
true
,
SELinuxRelabel
:
false
,
},
{
Name
:
"disk4"
,
ContainerPath
:
"/mnt/path4"
,
HostPath
:
"/mnt/host"
,
ReadOnly
:
false
,
SELinuxRelabel
:
false
,
},
{
Name
:
"disk5"
,
ContainerPath
:
"/mnt/path5"
,
HostPath
:
"/var/lib/kubelet/podID/volumes/empty/disk5"
,
ReadOnly
:
false
,
SELinuxRelabel
:
false
,
},
// otherwise validate the mounts
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
assert
.
Equal
(
t
,
tc
.
expectedMounts
,
mounts
,
"mounts of container %+v"
,
tc
.
container
)
})
}
assert
.
Equal
(
t
,
expectedMounts
,
mounts
,
"mounts of container %+v"
,
container
)
}
func
TestHostsFileContent
(
t
*
testing
.
T
)
{
...
...
pkg/volume/host_path/BUILD
View file @
48b3fb84
...
...
@@ -19,6 +19,7 @@ go_library(
"//pkg/api/v1:go_default_library",
"//pkg/volume:go_default_library",
"//pkg/volume/util/volumehelper:go_default_library",
"//pkg/volume/validation: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/util/uuid:go_default_library",
...
...
pkg/volume/host_path/host_path.go
View file @
48b3fb84
...
...
@@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
"k8s.io/kubernetes/pkg/volume/validation"
)
// This is the primary entrypoint for volume plugins.
...
...
@@ -103,6 +104,7 @@ func (plugin *hostPathPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volum
if
err
!=
nil
{
return
nil
,
err
}
return
&
hostPathMounter
{
hostPath
:
&
hostPath
{
path
:
hostPathVolumeSource
.
Path
},
readOnly
:
readOnly
,
...
...
@@ -205,6 +207,10 @@ func (b *hostPathMounter) CanMount() error {
// SetUp does nothing.
func
(
b
*
hostPathMounter
)
SetUp
(
fsGroup
*
types
.
UnixGroupID
)
error
{
err
:=
validation
.
ValidatePathNoBacksteps
(
b
.
GetPath
())
if
err
!=
nil
{
return
fmt
.
Errorf
(
"invalid HostPath `%s`: %v"
,
b
.
GetPath
(),
err
)
}
return
nil
}
...
...
pkg/volume/host_path/host_path_test.go
View file @
48b3fb84
...
...
@@ -182,6 +182,31 @@ func TestProvisioner(t *testing.T) {
os
.
RemoveAll
(
pv
.
Spec
.
HostPath
.
Path
)
}
func
TestInvalidHostPath
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
VolumePluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(
volume
.
VolumeConfig
{}),
volumetest
.
NewFakeVolumeHost
(
"fake"
,
nil
,
nil
))
plug
,
err
:=
plugMgr
.
FindPluginByName
(
hostPathPluginName
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unable to find plugin %s by name: %v"
,
hostPathPluginName
,
err
)
}
spec
:=
&
v1
.
Volume
{
Name
:
"vol1"
,
VolumeSource
:
v1
.
VolumeSource
{
HostPath
:
&
v1
.
HostPathVolumeSource
{
Path
:
"/no/backsteps/allowed/.."
}},
}
pod
:=
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
UID
:
types
.
UID
(
"poduid"
)}}
mounter
,
err
:=
plug
.
NewMounter
(
volume
.
NewSpecFromVolume
(
spec
),
pod
,
volume
.
VolumeOptions
{})
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
err
=
mounter
.
SetUp
(
nil
)
expectedMsg
:=
"invalid HostPath `/no/backsteps/allowed/..`: must not contain '..'"
if
err
.
Error
()
!=
expectedMsg
{
t
.
Fatalf
(
"expected error `%s` but got `%s`"
,
expectedMsg
,
err
)
}
}
func
TestPlugin
(
t
*
testing
.
T
)
{
plugMgr
:=
volume
.
VolumePluginMgr
{}
plugMgr
.
InitPlugins
(
ProbeVolumePlugins
(
volume
.
VolumeConfig
{}),
volumetest
.
NewFakeVolumeHost
(
"fake"
,
nil
,
nil
))
...
...
pkg/volume/validation/pv_validation.go
View file @
48b3fb84
...
...
@@ -17,6 +17,10 @@ limitations under the License.
package
validation
import
(
"errors"
"path/filepath"
"strings"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/api"
)
...
...
@@ -53,3 +57,15 @@ func checkMountOption(pv *api.PersistentVolume) field.ErrorList {
}
return
allErrs
}
// ValidatePathNoBacksteps will make sure the targetPath does not have any element which is ".."
func
ValidatePathNoBacksteps
(
targetPath
string
)
error
{
parts
:=
strings
.
Split
(
filepath
.
ToSlash
(
targetPath
),
"/"
)
for
_
,
item
:=
range
parts
{
if
item
==
".."
{
return
errors
.
New
(
"must not contain '..'"
)
}
}
return
nil
}
pkg/volume/validation/pv_validation_test.go
View file @
48b3fb84
...
...
@@ -84,3 +84,30 @@ func testVolumeWithMountOption(name string, namespace string, mountOptions strin
Spec
:
spec
,
}
}
func
TestValidatePathNoBacksteps
(
t
*
testing
.
T
)
{
testCases
:=
map
[
string
]
struct
{
path
string
expectedErr
bool
}{
"valid path"
:
{
path
:
"/foo/bar"
,
},
"invalid path"
:
{
path
:
"/foo/bar/.."
,
expectedErr
:
true
,
},
}
for
name
,
tc
:=
range
testCases
{
err
:=
ValidatePathNoBacksteps
(
tc
.
path
)
if
err
==
nil
&&
tc
.
expectedErr
{
t
.
Fatalf
(
"expected test `%s` to return an error but it didnt"
,
name
)
}
if
err
!=
nil
&&
!
tc
.
expectedErr
{
t
.
Fatalf
(
"expected test `%s` to return no error but got `%v`"
,
name
,
err
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment