Commit 0f2c6fc7 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #37009 from sjenning/fix-perms-with-fsgroup

Automatic merge from submit-queue (batch tested with PRs 38294, 37009, 36778, 38130, 37835) fix permissions when using fsGroup Currently, when an fsGroup is specified, the permissions of the defaultMode are not respected and all files created by the atomic writer have mode 777. This is because in `SetVolumeOwnership()` the `filepath.Walk` includes the symlinks created by the atomic writer. The symlinks have mode 777 when read from `info.Mode()`. However, when the are chmod'ed later, the chmod applies to the file the symlink points to, not the symlink itself, resulting in the wrong mode for the underlying file. This PR skips chmod/chown for symlinks in the walk since those operations are carried out on the underlying file which will be included elsewhere in the walk. xref https://bugzilla.redhat.com/show_bug.cgi?id=1384458 @derekwaynecarr @pmorie
parents 440ed2cf 51ae5a34
...@@ -51,6 +51,17 @@ func SetVolumeOwnership(mounter Mounter, fsGroup *int64) error { ...@@ -51,6 +51,17 @@ func SetVolumeOwnership(mounter Mounter, fsGroup *int64) error {
return err return err
} }
// chown and chmod pass through to the underlying file for symlinks.
// Symlinks have a mode of 777 but this really doesn't mean anything.
// The permissions of the underlying file are what matter.
// However, if one reads the mode of a symlink then chmods the symlink
// with that mode, it changes the mode of the underlying file, overridden
// the defaultMode and permissions initialized by the volume plugin, which
// is not what we want; thus, we skip chown/chmod for symlinks.
if info.Mode()&os.ModeSymlink != 0 {
return nil
}
stat, ok := info.Sys().(*syscall.Stat_t) stat, ok := info.Sys().(*syscall.Stat_t)
if !ok { if !ok {
return nil return nil
......
...@@ -41,6 +41,11 @@ var _ = framework.KubeDescribe("ConfigMap", func() { ...@@ -41,6 +41,11 @@ var _ = framework.KubeDescribe("ConfigMap", func() {
doConfigMapE2EWithoutMappings(f, 0, 0, &defaultMode) doConfigMapE2EWithoutMappings(f, 0, 0, &defaultMode)
}) })
It("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [Feature:FSGroup]", func() {
defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */
doConfigMapE2EWithoutMappings(f, 1000, 1001, &defaultMode)
})
It("should be consumable from pods in volume as non-root [Conformance]", func() { It("should be consumable from pods in volume as non-root [Conformance]", func() {
doConfigMapE2EWithoutMappings(f, 1000, 0, nil) doConfigMapE2EWithoutMappings(f, 1000, 0, nil)
}) })
...@@ -343,14 +348,10 @@ func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64, d ...@@ -343,14 +348,10 @@ func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64, d
defaultMode = &mode defaultMode = &mode
} }
// Just check file mode if fsGroup is not set. If fsGroup is set, the modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode))
// final mode is adjusted and we are not testing that case.
output := []string{ output := []string{
"content of file \"/etc/configmap-volume/data-1\": value-1", "content of file \"/etc/configmap-volume/data-1\": value-1",
} "mode of file \"/etc/configmap-volume/data-1\": " + modeString,
if fsGroup == 0 {
modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode))
output = append(output, "mode of file \"/etc/configmap-volume/data-1\": "+modeString)
} }
f.TestContainerOutput("consume configMaps", pod, 0, output) f.TestContainerOutput("consume configMaps", pod, 0, output)
} }
......
...@@ -81,6 +81,21 @@ var _ = framework.KubeDescribe("Downward API volume", func() { ...@@ -81,6 +81,21 @@ var _ = framework.KubeDescribe("Downward API volume", func() {
}) })
}) })
It("should provide podname as non-root with fsgroup and defaultMode [Feature:FSGroup]", func() {
podName := "metadata-volume-" + string(uuid.NewUUID())
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.Spec.SecurityContext = &v1.PodSecurityContext{
RunAsUser: &uid,
FSGroup: &gid,
}
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--r-----",
})
})
It("should update labels on modification [Conformance]", func() { It("should update labels on modification [Conformance]", func() {
labels := map[string]string{} labels := map[string]string{}
labels["key1"] = "value1" labels["key1"] = "value1"
......
...@@ -31,12 +31,19 @@ var _ = framework.KubeDescribe("Secrets", func() { ...@@ -31,12 +31,19 @@ var _ = framework.KubeDescribe("Secrets", func() {
f := framework.NewDefaultFramework("secrets") f := framework.NewDefaultFramework("secrets")
It("should be consumable from pods in volume [Conformance]", func() { It("should be consumable from pods in volume [Conformance]", func() {
doSecretE2EWithoutMapping(f, nil /* default mode */, "secret-test-"+string(uuid.NewUUID())) doSecretE2EWithoutMapping(f, nil /* default mode */, "secret-test-"+string(uuid.NewUUID()), nil, nil)
}) })
It("should be consumable from pods in volume with defaultMode set [Conformance]", func() { It("should be consumable from pods in volume with defaultMode set [Conformance]", func() {
defaultMode := int32(0400) defaultMode := int32(0400)
doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID())) doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), nil, nil)
})
It("should be consumable from pods in volume as non-root with defaultMode and fsGroup set [Conformance]", func() {
defaultMode := int32(0440) /* setting fsGroup sets mode to at least 440 */
fsGroup := int64(1001)
uid := int64(1000)
doSecretE2EWithoutMapping(f, &defaultMode, "secret-test-"+string(uuid.NewUUID()), &fsGroup, &uid)
}) })
It("should be consumable from pods in volume with mappings [Conformance]", func() { It("should be consumable from pods in volume with mappings [Conformance]", func() {
...@@ -66,7 +73,7 @@ var _ = framework.KubeDescribe("Secrets", func() { ...@@ -66,7 +73,7 @@ var _ = framework.KubeDescribe("Secrets", func() {
if secret2, err = f.ClientSet.Core().Secrets(namespace2.Name).Create(secret2); err != nil { if secret2, err = f.ClientSet.Core().Secrets(namespace2.Name).Create(secret2); err != nil {
framework.Failf("unable to create test secret %s: %v", secret2.Name, err) framework.Failf("unable to create test secret %s: %v", secret2.Name, err)
} }
doSecretE2EWithoutMapping(f, nil /* default mode */, secret2.Name) doSecretE2EWithoutMapping(f, nil /* default mode */, secret2.Name, nil, nil)
}) })
It("should be consumable in multiple volumes in a pod [Conformance]", func() { It("should be consumable in multiple volumes in a pod [Conformance]", func() {
...@@ -201,7 +208,7 @@ func secretForTest(namespace, name string) *v1.Secret { ...@@ -201,7 +208,7 @@ func secretForTest(namespace, name string) *v1.Secret {
} }
} }
func doSecretE2EWithoutMapping(f *framework.Framework, defaultMode *int32, secretName string) { func doSecretE2EWithoutMapping(f *framework.Framework, defaultMode *int32, secretName string, fsGroup *int64, uid *int64) {
var ( var (
volumeName = "secret-volume" volumeName = "secret-volume"
volumeMountPath = "/etc/secret-volume" volumeMountPath = "/etc/secret-volume"
...@@ -256,6 +263,13 @@ func doSecretE2EWithoutMapping(f *framework.Framework, defaultMode *int32, secre ...@@ -256,6 +263,13 @@ func doSecretE2EWithoutMapping(f *framework.Framework, defaultMode *int32, secre
defaultMode = &mode defaultMode = &mode
} }
if fsGroup != nil || uid != nil {
pod.Spec.SecurityContext = &v1.PodSecurityContext{
FSGroup: fsGroup,
RunAsUser: uid,
}
}
modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode)) modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode))
expectedOutput := []string{ expectedOutput := []string{
"content of file \"/etc/secret-volume/data-1\": value-1", "content of file \"/etc/secret-volume/data-1\": value-1",
......
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