diff --git a/pkg/kubelet/BUILD b/pkg/kubelet/BUILD index 979ef5e2d22..64b94915787 100644 --- a/pkg/kubelet/BUILD +++ b/pkg/kubelet/BUILD @@ -43,6 +43,7 @@ go_library( "//pkg/fieldpath:go_default_library", "//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis/cri:go_default_library", + "//pkg/kubelet/apis/cri/v1alpha1/runtime:go_default_library", "//pkg/kubelet/apis/kubeletconfig:go_default_library", "//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library", "//pkg/kubelet/cadvisor:go_default_library", @@ -170,6 +171,7 @@ go_test( "//pkg/api/install:go_default_library", "//pkg/capabilities:go_default_library", "//pkg/kubelet/apis:go_default_library", + "//pkg/kubelet/apis/cri/v1alpha1/runtime:go_default_library", "//pkg/kubelet/apis/kubeletconfig:go_default_library", "//pkg/kubelet/cadvisor/testing:go_default_library", "//pkg/kubelet/cm:go_default_library", @@ -225,6 +227,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library", "//vendor/k8s.io/client-go/tools/record:go_default_library", diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index 19d13dc4f99..3e9644c24f4 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -389,6 +389,8 @@ type Mount struct { ReadOnly bool // Whether the mount needs SELinux relabeling SELinuxRelabel bool + // Requested propagation mode + Propagation runtimeapi.MountPropagation } type PortMapping struct { diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 8720902a1fa..be5cb3f59e0 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -41,6 +41,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" utilvalidation "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/remotecommand" "k8s.io/kubernetes/pkg/api" v1helper "k8s.io/kubernetes/pkg/api/v1/helper" @@ -48,7 +49,9 @@ import ( podutil "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/api/v1/resource" "k8s.io/kubernetes/pkg/api/v1/validation" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/fieldpath" + runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" "k8s.io/kubernetes/pkg/kubelet/cm" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/envvars" @@ -188,12 +191,19 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h } } + propagation, err := translateMountPropagation(mount.MountPropagation) + if err != nil { + return nil, err + } + glog.V(5).Infof("Pod %q container %q mount %q has propagation %q", format.Pod(pod), container.Name, mount.Name, propagation) + mounts = append(mounts, kubecontainer.Mount{ Name: mount.Name, ContainerPath: containerPath, HostPath: hostPath, ReadOnly: mount.ReadOnly, SELinuxRelabel: relabelVolume, + Propagation: propagation, }) } if mountEtcHostsFile { @@ -207,6 +217,26 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h return mounts, nil } +// translateMountPropagation transforms v1.MountPropagationMode to +// runtimeapi.MountPropagation. +func translateMountPropagation(mountMode *v1.MountPropagationMode) (runtimeapi.MountPropagation, error) { + if !utilfeature.DefaultFeatureGate.Enabled(features.MountPropagation) { + // mount propagation is disabled, use private as in the old versions + return runtimeapi.MountPropagation_PROPAGATION_PRIVATE, nil + } + switch { + case mountMode == nil: + // HostToContainer is the default + return runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, nil + case *mountMode == v1.MountPropagationHostToContainer: + return runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, nil + case *mountMode == v1.MountPropagationBidirectional: + return runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL, nil + default: + return 0, fmt.Errorf("invalid MountPropagation mode: %q", mountMode) + } +} + // makeHostsMount makes the mountpoint for the hosts file that the containers // in a pod are injected with. func makeHostsMount(podDir, podIP, hostName, hostDomainName string, hostAliases []v1.HostAlias, useHostNetwork bool) (*kubecontainer.Mount, error) { diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 413f266b84c..497e6666ac4 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -35,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + utilfeature "k8s.io/apiserver/pkg/util/feature" core "k8s.io/client-go/testing" "k8s.io/client-go/tools/record" "k8s.io/kubernetes/pkg/api" @@ -42,6 +43,7 @@ import ( // api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed // to "v1"? _ "k8s.io/kubernetes/pkg/api/install" + runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" "k8s.io/kubernetes/pkg/kubelet/server/portforward" @@ -49,6 +51,10 @@ import ( ) func TestMakeMounts(t *testing.T) { + bTrue := true + propagationHostToContainer := v1.MountPropagationHostToContainer + propagationBidirectional := v1.MountPropagationBidirectional + testCases := map[string]struct { container v1.Container podVolumes kubecontainer.VolumeMap @@ -56,18 +62,20 @@ func TestMakeMounts(t *testing.T) { expectedErrMsg string expectedMounts []kubecontainer.Mount }{ - "valid mounts": { + "valid mounts in unprivileged container": { 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{ + Name: "container1", VolumeMounts: []v1.VolumeMount{ { - MountPath: "/etc/hosts", - Name: "disk", - ReadOnly: false, + MountPath: "/etc/hosts", + Name: "disk", + ReadOnly: false, + MountPropagation: &propagationHostToContainer, }, { MountPath: "/mnt/path3", @@ -93,6 +101,7 @@ func TestMakeMounts(t *testing.T) { HostPath: "/mnt/disk", ReadOnly: false, SELinuxRelabel: false, + Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, }, { Name: "disk", @@ -100,6 +109,7 @@ func TestMakeMounts(t *testing.T) { HostPath: "/mnt/disk", ReadOnly: true, SELinuxRelabel: false, + Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, }, { Name: "disk4", @@ -107,6 +117,7 @@ func TestMakeMounts(t *testing.T) { HostPath: "/mnt/host", ReadOnly: false, SELinuxRelabel: false, + Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, }, { Name: "disk5", @@ -114,6 +125,66 @@ func TestMakeMounts(t *testing.T) { HostPath: "/var/lib/kubelet/podID/volumes/empty/disk5", ReadOnly: false, SELinuxRelabel: false, + Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, + }, + }, + expectErr: false, + }, + "valid mounts in privileged container": { + 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{ + Name: "container1", + VolumeMounts: []v1.VolumeMount{ + { + MountPath: "/etc/hosts", + Name: "disk", + ReadOnly: false, + MountPropagation: &propagationBidirectional, + }, + { + MountPath: "/mnt/path3", + Name: "disk", + ReadOnly: true, + MountPropagation: &propagationHostToContainer, + }, + { + MountPath: "/mnt/path4", + Name: "disk4", + ReadOnly: false, + }, + }, + SecurityContext: &v1.SecurityContext{ + Privileged: &bTrue, + }, + }, + expectedMounts: []kubecontainer.Mount{ + { + Name: "disk", + ContainerPath: "/etc/hosts", + HostPath: "/mnt/disk", + ReadOnly: false, + SELinuxRelabel: false, + Propagation: runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL, + }, + { + Name: "disk", + ContainerPath: "/mnt/path3", + HostPath: "/mnt/disk", + ReadOnly: true, + SELinuxRelabel: false, + Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, + }, + { + Name: "disk4", + ContainerPath: "/mnt/path4", + HostPath: "/mnt/host", + ReadOnly: false, + SELinuxRelabel: false, + Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, }, }, expectErr: false, @@ -161,6 +232,12 @@ func TestMakeMounts(t *testing.T) { HostNetwork: true, }, } + // test makeMounts with enabled mount propagation + err := utilfeature.DefaultFeatureGate.Set("MountPropagation=true") + if err != nil { + t.Errorf("Failed to enable feature gate for MountPropagation: %v", err) + return + } mounts, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes) @@ -178,6 +255,24 @@ func TestMakeMounts(t *testing.T) { } assert.Equal(t, tc.expectedMounts, mounts, "mounts of container %+v", tc.container) + + // test makeMounts with disabled mount propagation + err = utilfeature.DefaultFeatureGate.Set("MountPropagation=false") + if err != nil { + t.Errorf("Failed to enable feature gate for MountPropagation: %v", err) + return + } + mounts, err = makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes) + if !tc.expectErr { + expectedPrivateMounts := []kubecontainer.Mount{} + for _, mount := range tc.expectedMounts { + // all mounts are expected to be private when mount + // propagation is disabled + mount.Propagation = runtimeapi.MountPropagation_PROPAGATION_PRIVATE + expectedPrivateMounts = append(expectedPrivateMounts, mount) + } + assert.Equal(t, expectedPrivateMounts, mounts, "mounts of container %+v", tc.container) + } }) } } diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container.go b/pkg/kubelet/kuberuntime/kuberuntime_container.go index d57614aeb69..74b0e1b354b 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container.go @@ -294,6 +294,7 @@ func (m *kubeGenericRuntimeManager) makeMounts(opts *kubecontainer.RunContainerO ContainerPath: v.ContainerPath, Readonly: v.ReadOnly, SelinuxRelabel: selinuxRelabel, + Propagation: v.Propagation, } volumeMounts = append(volumeMounts, mount)