Merge pull request #58720 from joelsmith/ro-vol

Automatic merge from submit-queue. 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>.

Ensure that the runtime mounts RO volumes read-only

**What this PR does / why we need it**:

This change makes it so that containers cannot write to secret, configMap, downwardAPI and projected volumes since the runtime will now mount them read-only. This change makes things less confusing for a user since any attempt to update a secret volume will result in an error rather than a successful change followed by a revert by the kubelet when the volume next syncs.

It also adds a feature gate `ReadOnlyAPIDataVolumes` to a provide a way to disable the new behavior in 1.10, but for 1.11, the new behavior will become non-optional.

Also, E2E tests for downwardAPI and projected volumes are updated to mount the volumes somewhere other than /etc.

**Which issue(s) this PR fixes**
Fixes #58719 

**Release note**:
```release-note
Containers now mount secret, configMap, downwardAPI and projected volumes read-only. Previously,
container modifications to files in these types of volumes were temporary and reverted by the kubelet
during volume sync. Until version 1.11, setting the feature gate ReadOnlyAPIDataVolumes=false will
preserve the old behavior.
```
pull/6/head
Kubernetes Submit Queue 2018-02-02 06:42:12 -08:00 committed by GitHub
commit 8c6be65f4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 42 deletions

View File

@ -56,8 +56,7 @@ spec:
timeoutSeconds: 30
volumes:
- name: kubernetes-dashboard-certs
secret:
secretName: kubernetes-dashboard-certs
emptyDir: {}
- name: tmp-volume
emptyDir: {}
serviceAccountName: kubernetes-dashboard

View File

@ -231,6 +231,13 @@ const (
//
// Enable Hyper-V containers on Windows
HyperVContainer utilfeature.Feature = "HyperVContainer"
// owner: @joelsmith
// deprecated: v1.10
//
// Mount secret, configMap, downwardAPI and projected volumes ReadOnly. Note: this feature
// gate is present only for backward compatability, it will be removed in the 1.11 release.
ReadOnlyAPIDataVolumes utilfeature.Feature = "ReadOnlyAPIDataVolumes"
)
func init() {
@ -287,4 +294,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
// features that enable backwards compatability but are scheduled to be removed
ServiceProxyAllowExternalIPs: {Default: false, PreRelease: utilfeature.Deprecated},
ReadOnlyAPIDataVolumes: {Default: true, PreRelease: utilfeature.Deprecated},
}

View File

@ -247,11 +247,13 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
}
glog.V(5).Infof("Pod %q container %q mount %q has propagation %q", format.Pod(pod), container.Name, mount.Name, propagation)
mustMountRO := vol.Mounter.GetAttributes().ReadOnly && utilfeature.DefaultFeatureGate.Enabled(features.ReadOnlyAPIDataVolumes)
mounts = append(mounts, kubecontainer.Mount{
Name: mount.Name,
ContainerPath: containerPath,
HostPath: hostPath,
ReadOnly: mount.ReadOnly,
ReadOnly: mount.ReadOnly || mustMountRO,
SELinuxRelabel: relabelVolume,
Propagation: propagation,
})

View File

@ -46,7 +46,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
*/
framework.ConformanceIt("should provide podname only ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podname")
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podinfo/podname")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("%s\n", podName),
@ -61,10 +61,10 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
framework.ConformanceIt("should set DefaultMode on files ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
defaultMode := int32(0400)
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podname", nil, &defaultMode)
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", nil, &defaultMode)
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--------",
"mode of file \"/etc/podinfo/podname\": -r--------",
})
})
@ -76,10 +76,10 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
framework.ConformanceIt("should set mode on item file ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
mode := int32(0400)
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podname", &mode, nil)
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", &mode, nil)
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--------",
"mode of file \"/etc/podinfo/podname\": -r--------",
})
})
@ -87,7 +87,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
podName := "metadata-volume-" + string(uuid.NewUUID())
uid := int64(1001)
gid := int64(1234)
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podname")
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podinfo/podname")
pod.Spec.SecurityContext = &v1.PodSecurityContext{
RunAsUser: &uid,
FSGroup: &gid,
@ -102,13 +102,13 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
uid := int64(1001)
gid := int64(1234)
mode := int32(0440) /* setting fsGroup sets mode to at least 440 */
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podname", &mode, nil)
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", &mode, nil)
pod.Spec.SecurityContext = &v1.PodSecurityContext{
RunAsUser: &uid,
FSGroup: &gid,
}
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--r-----",
"mode of file \"/etc/podinfo/podname\": -r--r-----",
})
})
@ -123,7 +123,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
labels["key2"] = "value2"
podName := "labelsupdate" + string(uuid.NewUUID())
pod := downwardAPIVolumePodForUpdateTest(podName, labels, map[string]string{}, "/etc/labels")
pod := downwardAPIVolumePodForUpdateTest(podName, labels, map[string]string{}, "/etc/podinfo/labels")
containerName := "client-container"
By("Creating the pod")
podClient.CreateSync(pod)
@ -153,7 +153,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
annotations := map[string]string{}
annotations["builder"] = "bar"
podName := "annotationupdate" + string(uuid.NewUUID())
pod := downwardAPIVolumePodForUpdateTest(podName, map[string]string{}, annotations, "/etc/annotations")
pod := downwardAPIVolumePodForUpdateTest(podName, map[string]string{}, annotations, "/etc/podinfo/annotations")
containerName := "client-container"
By("Creating the pod")
@ -185,7 +185,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
*/
framework.ConformanceIt("should provide container's cpu limit ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/cpu_limit")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/cpu_limit")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("2\n"),
@ -199,7 +199,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
*/
framework.ConformanceIt("should provide container's memory limit ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/memory_limit")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/memory_limit")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("67108864\n"),
@ -213,7 +213,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
*/
framework.ConformanceIt("should provide container's cpu request ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/cpu_request")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/cpu_request")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("1\n"),
@ -227,7 +227,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
*/
framework.ConformanceIt("should provide container's memory request ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/memory_request")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/memory_request")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("33554432\n"),
@ -242,7 +242,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
*/
framework.ConformanceIt("should provide node allocatable (cpu) as default cpu limit if the limit is not set ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/cpu_limit")
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/podinfo/cpu_limit")
f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
})
@ -255,7 +255,7 @@ var _ = Describe("[sig-storage] Downward API volume", func() {
*/
framework.ConformanceIt("should provide node allocatable (memory) as default memory limit if the limit is not set ", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/memory_limit")
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/podinfo/memory_limit")
f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
})
@ -273,7 +273,7 @@ func downwardAPIVolumePodForModeTest(name, filePath string, itemMode, defaultMod
VolumeMounts: []v1.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
MountPath: "/etc/podinfo",
},
},
},
@ -299,7 +299,7 @@ func downwardAPIVolumePodForSimpleTest(name string, filePath string) *v1.Pod {
VolumeMounts: []v1.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
MountPath: "/etc/podinfo",
ReadOnly: false,
},
},
@ -340,7 +340,7 @@ func downwardAPIVolumeBaseContainers(name, filePath string) []v1.Container {
VolumeMounts: []v1.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
MountPath: "/etc/podinfo",
ReadOnly: false,
},
},
@ -358,7 +358,7 @@ func downwardAPIVolumeDefaultBaseContainer(name, filePath string) []v1.Container
VolumeMounts: []v1.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
MountPath: "/etc/podinfo",
},
},
},
@ -377,7 +377,7 @@ func downwardAPIVolumePodForUpdateTest(name string, labels, annotations map[stri
VolumeMounts: []v1.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
MountPath: "/etc/podinfo",
ReadOnly: false,
},
},

View File

@ -866,7 +866,7 @@ var _ = Describe("[sig-storage] Projected", func() {
*/
framework.ConformanceIt("should provide podname only", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podname")
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podinfo/podname")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("%s\n", podName),
@ -882,10 +882,10 @@ var _ = Describe("[sig-storage] Projected", func() {
framework.ConformanceIt("should set DefaultMode on files", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
defaultMode := int32(0400)
pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podname", nil, &defaultMode)
pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", nil, &defaultMode)
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--------",
"mode of file \"/etc/podinfo/podname\": -r--------",
})
})
@ -897,10 +897,10 @@ var _ = Describe("[sig-storage] Projected", func() {
framework.ConformanceIt("should set mode on item file", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
mode := int32(0400)
pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podname", &mode, nil)
pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", &mode, nil)
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--------",
"mode of file \"/etc/podinfo/podname\": -r--------",
})
})
@ -908,7 +908,7 @@ var _ = Describe("[sig-storage] Projected", func() {
podName := "metadata-volume-" + string(uuid.NewUUID())
uid := int64(1001)
gid := int64(1234)
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podname")
pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podinfo/podname")
pod.Spec.SecurityContext = &v1.PodSecurityContext{
RunAsUser: &uid,
FSGroup: &gid,
@ -923,13 +923,13 @@ var _ = Describe("[sig-storage] Projected", func() {
uid := int64(1001)
gid := int64(1234)
mode := int32(0440) /* setting fsGroup sets mode to at least 440 */
pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podname", &mode, nil)
pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", &mode, nil)
pod.Spec.SecurityContext = &v1.PodSecurityContext{
RunAsUser: &uid,
FSGroup: &gid,
}
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--r-----",
"mode of file \"/etc/podinfo/podname\": -r--r-----",
})
})
@ -945,7 +945,7 @@ var _ = Describe("[sig-storage] Projected", func() {
labels["key2"] = "value2"
podName := "labelsupdate" + string(uuid.NewUUID())
pod := projectedDownwardAPIVolumePodForUpdateTest(podName, labels, map[string]string{}, "/etc/labels")
pod := projectedDownwardAPIVolumePodForUpdateTest(podName, labels, map[string]string{}, "/etc/podinfo/labels")
containerName := "client-container"
By("Creating the pod")
podClient.CreateSync(pod)
@ -976,7 +976,7 @@ var _ = Describe("[sig-storage] Projected", func() {
annotations := map[string]string{}
annotations["builder"] = "bar"
podName := "annotationupdate" + string(uuid.NewUUID())
pod := projectedDownwardAPIVolumePodForUpdateTest(podName, map[string]string{}, annotations, "/etc/annotations")
pod := projectedDownwardAPIVolumePodForUpdateTest(podName, map[string]string{}, annotations, "/etc/podinfo/annotations")
containerName := "client-container"
By("Creating the pod")
@ -1008,7 +1008,7 @@ var _ = Describe("[sig-storage] Projected", func() {
*/
framework.ConformanceIt("should provide container's cpu limit", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/cpu_limit")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/cpu_limit")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("2\n"),
@ -1022,7 +1022,7 @@ var _ = Describe("[sig-storage] Projected", func() {
*/
framework.ConformanceIt("should provide container's memory limit", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/memory_limit")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/memory_limit")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("67108864\n"),
@ -1036,7 +1036,7 @@ var _ = Describe("[sig-storage] Projected", func() {
*/
framework.ConformanceIt("should provide container's cpu request", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/cpu_request")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/cpu_request")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("1\n"),
@ -1050,7 +1050,7 @@ var _ = Describe("[sig-storage] Projected", func() {
*/
framework.ConformanceIt("should provide container's memory request", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForContainerResources(podName, "/etc/memory_request")
pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/memory_request")
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
fmt.Sprintf("33554432\n"),
@ -1065,7 +1065,7 @@ var _ = Describe("[sig-storage] Projected", func() {
*/
framework.ConformanceIt("should provide node allocatable (cpu) as default cpu limit if the limit is not set", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/cpu_limit")
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/podinfo/cpu_limit")
f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
})
@ -1078,7 +1078,7 @@ var _ = Describe("[sig-storage] Projected", func() {
*/
framework.ConformanceIt("should provide node allocatable (memory) as default memory limit if the limit is not set", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/memory_limit")
pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/podinfo/memory_limit")
f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
})
@ -1495,7 +1495,7 @@ func projectedDownwardAPIVolumePodForModeTest(name, filePath string, itemMode, d
VolumeMounts: []v1.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
MountPath: "/etc/podinfo",
},
},
},
@ -1521,7 +1521,7 @@ func projectedDownwardAPIVolumePodForUpdateTest(name string, labels, annotations
VolumeMounts: []v1.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
MountPath: "/etc/podinfo",
ReadOnly: false,
},
},