Commit d7b069fb authored by Saad Ali's avatar Saad Ali

Merge pull request #23793 from pmorie/configmap-nonroot

Make ConfigMap volume readable as non-root
parents c9b6e89a e838ff28
......@@ -341,6 +341,15 @@ func (w *AtomicWriter) newTimestampDir() (string, error) {
return "", err
}
// 0755 permissions are needed to allow 'group' and 'other' to recurse the
// directory tree. do a chmod here to ensure that permissions are set correctly
// regardless of the process' umask.
err = os.Chmod(tsDir, 0755)
if err != nil {
glog.Errorf("%s: unable to set mode on new temp directory: %v", w.logContext, err)
return "", err
}
return tsDir, nil
}
......@@ -408,7 +417,6 @@ func (w *AtomicWriter) createUserVisibleFiles(payload map[string][]byte) error {
}
}
}
return nil
}
......
......@@ -32,9 +32,41 @@ var _ = KubeDescribe("ConfigMap", func() {
f := NewDefaultFramework("configmap")
It("should be consumable from pods in volume [Conformance]", func() {
name := "configmap-test-volume-" + string(util.NewUUID())
doConfigMapE2EWithoutMappings(f, 0, 0)
})
It("should be consumable from pods in volume as non-root [Conformance]", func() {
doConfigMapE2EWithoutMappings(f, 1000, 0)
})
It("should be consumable from pods in volume as non-root with FSGroup [Feature:FSGroup]", func() {
doConfigMapE2EWithoutMappings(f, 1000, 1001)
})
It("should be consumable from pods in volume with mappings [Conformance]", func() {
doConfigMapE2EWithMappings(f, 0, 0)
})
It("should be consumable from pods in volume with mappings as non-root [Conformance]", func() {
doConfigMapE2EWithMappings(f, 1000, 0)
})
It("should be consumable from pods in volume with mappings as non-root with FSGroup [Feature:FSGroup]", func() {
doConfigMapE2EWithMappings(f, 1000, 1001)
})
It("updates should be reflected in volume [Conformance]", func() {
// We may have to wait or a full sync period to elapse before the
// Kubelet projects the update into the volume and the container picks
// it up. This timeout is based on the default Kubelet sync period (1
// minute) plus additional time for fudge factor.
const podLogTimeout = 300 * time.Second
name := "configmap-test-upd-" + string(util.NewUUID())
volumeName := "configmap-volume"
volumeMountPath := "/etc/configmap-volume"
containerName := "configmap-volume-test"
configMap := &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
......@@ -43,8 +75,6 @@ var _ = KubeDescribe("ConfigMap", func() {
},
Data: map[string]string{
"data-1": "value-1",
"data-2": "value-2",
"data-3": "value-3",
},
}
......@@ -79,9 +109,9 @@ var _ = KubeDescribe("ConfigMap", func() {
},
Containers: []api.Container{
{
Name: "configmap-volume-test",
Name: containerName,
Image: "gcr.io/google_containers/mounttest:0.6",
Args: []string{"--file_content=/etc/configmap-volume/data-1"},
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volume/data-1"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
......@@ -95,29 +125,36 @@ var _ = KubeDescribe("ConfigMap", func() {
},
}
testContainerOutput("consume configMaps", f.Client, pod, 0, []string{
"content of file \"/etc/configmap-volume/data-1\": value-1",
}, f.Namespace.Name)
})
defer func() {
By("Deleting the pod")
f.Client.Pods(f.Namespace.Name).Delete(pod.Name, api.NewDeleteOptions(0))
}()
By("Creating the pod")
_, err = f.Client.Pods(f.Namespace.Name).Create(pod)
Expect(err).NotTo(HaveOccurred())
It("should be consumable from pods in volume with mappings [Conformance]", func() {
name := "configmap-test-volume-map-" + string(util.NewUUID())
volumeName := "configmap-volume"
volumeMountPath := "/etc/configmap-volume"
expectNoError(waitForPodRunningInNamespace(f.Client, pod.Name, f.Namespace.Name))
configMap := &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
Namespace: f.Namespace.Name,
Name: name,
},
Data: map[string]string{
"data-1": "value-1",
"data-2": "value-2",
"data-3": "value-3",
},
pollLogs := func() (string, error) {
return getPodLogs(f.Client, f.Namespace.Name, pod.Name, containerName)
}
By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
Eventually(pollLogs, podLogTimeout, poll).Should(ContainSubstring("value-1"))
By(fmt.Sprintf("Updating configmap %v", configMap.Name))
configMap.ResourceVersion = "" // to force update
configMap.Data["data-1"] = "value-2"
_, err = f.Client.ConfigMaps(f.Namespace.Name).Update(configMap)
Expect(err).NotTo(HaveOccurred())
By("waiting to observe update in volume")
Eventually(pollLogs, podLogTimeout, poll).Should(ContainSubstring("value-2"))
})
It("should be consumable via environment variable [Conformance]", func() {
name := "configmap-test-" + string(util.NewUUID())
configMap := newConfigMap(f, name)
By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
defer func() {
By("Cleaning up the configMap")
if err := f.Client.ConfigMaps(f.Namespace.Name).Delete(configMap.Name); err != nil {
......@@ -134,34 +171,22 @@ var _ = KubeDescribe("ConfigMap", func() {
Name: "pod-configmaps-" + string(util.NewUUID()),
},
Spec: api.PodSpec{
Volumes: []api.Volume{
Containers: []api.Container{
{
Name: volumeName,
VolumeSource: api.VolumeSource{
ConfigMap: &api.ConfigMapVolumeSource{
Name: "env-test",
Image: "gcr.io/google_containers/busybox:1.24",
Command: []string{"sh", "-c", "env"},
Env: []api.EnvVar{
{
Name: "CONFIG_DATA_1",
ValueFrom: &api.EnvVarSource{
ConfigMapKeyRef: &api.ConfigMapKeySelector{
LocalObjectReference: api.LocalObjectReference{
Name: name,
},
Items: []api.KeyToPath{
{
Key: "data-2",
Path: "path/to/data-2",
},
},
},
},
Key: "data-1",
},
},
Containers: []api.Container{
{
Name: "configmap-volume-test",
Image: "gcr.io/google_containers/mounttest:0.6",
Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
MountPath: volumeMountPath,
ReadOnly: true,
},
},
},
......@@ -171,32 +196,32 @@ var _ = KubeDescribe("ConfigMap", func() {
}
testContainerOutput("consume configMaps", f.Client, pod, 0, []string{
"content of file \"/etc/configmap-volume/path/to/data-2\": value-2",
"CONFIG_DATA_1=value-1",
}, f.Namespace.Name)
})
})
It("updates should be reflected in volume [Conformance]", func() {
// We may have to wait or a full sync period to elapse before the
// Kubelet projects the update into the volume and the container picks
// it up. This timeout is based on the default Kubelet sync period (1
// minute) plus additional time for fudge factor.
const podLogTimeout = 300 * time.Second
name := "configmap-test-upd-" + string(util.NewUUID())
volumeName := "configmap-volume"
volumeMountPath := "/etc/configmap-volume"
containerName := "configmap-volume-test"
configMap := &api.ConfigMap{
func newConfigMap(f *Framework, name string) *api.ConfigMap {
return &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
Namespace: f.Namespace.Name,
Name: name,
},
Data: map[string]string{
"data-1": "value-1",
"data-2": "value-2",
"data-3": "value-3",
},
}
}
func doConfigMapE2EWithoutMappings(f *Framework, uid, fsGroup int64) {
var (
name = "configmap-test-volume-" + string(util.NewUUID())
volumeName = "configmap-volume"
volumeMountPath = "/etc/configmap-volume"
configMap = newConfigMap(f, name)
)
By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
defer func() {
......@@ -215,6 +240,7 @@ var _ = KubeDescribe("ConfigMap", func() {
Name: "pod-configmaps-" + string(util.NewUUID()),
},
Spec: api.PodSpec{
SecurityContext: &api.PodSecurityContext{},
Volumes: []api.Volume{
{
Name: volumeName,
......@@ -229,9 +255,9 @@ var _ = KubeDescribe("ConfigMap", func() {
},
Containers: []api.Container{
{
Name: containerName,
Name: "configmap-volume-test",
Image: "gcr.io/google_containers/mounttest:0.6",
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volume/data-1"},
Args: []string{"--file_content=/etc/configmap-volume/data-1"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
......@@ -245,47 +271,29 @@ var _ = KubeDescribe("ConfigMap", func() {
},
}
defer func() {
By("Deleting the pod")
f.Client.Pods(f.Namespace.Name).Delete(pod.Name, api.NewDeleteOptions(0))
}()
By("Creating the pod")
_, err = f.Client.Pods(f.Namespace.Name).Create(pod)
Expect(err).NotTo(HaveOccurred())
expectNoError(waitForPodRunningInNamespace(f.Client, pod.Name, f.Namespace.Name))
pollLogs := func() (string, error) {
return getPodLogs(f.Client, f.Namespace.Name, pod.Name, containerName)
if uid != 0 {
pod.Spec.SecurityContext.RunAsUser = &uid
}
Eventually(pollLogs, podLogTimeout, poll).Should(ContainSubstring("value-1"))
if fsGroup != 0 {
pod.Spec.SecurityContext.FSGroup = &fsGroup
}
By(fmt.Sprintf("Updating configmap %v", configMap.Name))
configMap.ResourceVersion = "" // to force update
configMap.Data["data-1"] = "value-2"
_, err = f.Client.ConfigMaps(f.Namespace.Name).Update(configMap)
Expect(err).NotTo(HaveOccurred())
testContainerOutput("consume configMaps", f.Client, pod, 0, []string{
"content of file \"/etc/configmap-volume/data-1\": value-1",
}, f.Namespace.Name)
By("waiting to observe update in volume")
Eventually(pollLogs, podLogTimeout, poll).Should(ContainSubstring("value-2"))
})
}
It("should be consumable via environment variable [Conformance]", func() {
name := "configmap-test-" + string(util.NewUUID())
configMap := &api.ConfigMap{
ObjectMeta: api.ObjectMeta{
Namespace: f.Namespace.Name,
Name: name,
},
Data: map[string]string{
"data-1": "value-1",
"data-2": "value-2",
"data-3": "value-3",
},
}
func doConfigMapE2EWithMappings(f *Framework, uid, fsGroup int64) {
var (
name = "configmap-test-volume-map-" + string(util.NewUUID())
volumeName = "configmap-volume"
volumeMountPath = "/etc/configmap-volume"
configMap = newConfigMap(f, name)
)
By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
By(fmt.Sprintf("Creating configMap with name %s", configMap.Name))
defer func() {
By("Cleaning up the configMap")
if err := f.Client.ConfigMaps(f.Namespace.Name).Delete(configMap.Name); err != nil {
......@@ -302,23 +310,36 @@ var _ = KubeDescribe("ConfigMap", func() {
Name: "pod-configmaps-" + string(util.NewUUID()),
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "env-test",
Image: "gcr.io/google_containers/busybox:1.24",
Command: []string{"sh", "-c", "env"},
Env: []api.EnvVar{
SecurityContext: &api.PodSecurityContext{},
Volumes: []api.Volume{
{
Name: "CONFIG_DATA_1",
ValueFrom: &api.EnvVarSource{
ConfigMapKeyRef: &api.ConfigMapKeySelector{
Name: volumeName,
VolumeSource: api.VolumeSource{
ConfigMap: &api.ConfigMapVolumeSource{
LocalObjectReference: api.LocalObjectReference{
Name: name,
},
Key: "data-1",
Items: []api.KeyToPath{
{
Key: "data-2",
Path: "path/to/data-2",
},
},
},
},
},
},
Containers: []api.Container{
{
Name: "configmap-volume-test",
Image: "gcr.io/google_containers/mounttest:0.6",
Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
MountPath: volumeMountPath,
ReadOnly: true,
},
},
},
},
......@@ -326,8 +347,15 @@ var _ = KubeDescribe("ConfigMap", func() {
},
}
if uid != 0 {
pod.Spec.SecurityContext.RunAsUser = &uid
}
if fsGroup != 0 {
pod.Spec.SecurityContext.FSGroup = &fsGroup
}
testContainerOutput("consume configMaps", f.Client, pod, 0, []string{
"CONFIG_DATA_1=value-1",
"content of file \"/etc/configmap-volume/path/to/data-2\": value-2",
}, f.Namespace.Name)
})
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment