Fix kubelet on Windows fails if a pod has SecurityContext with RunAsUser.

Co-authored-by: rphillips <rphillips@redhat.com>
This commit is contained in:
carlory 2024-05-22 10:42:04 +08:00
parent 5732a8bbb4
commit 3812fa1d6d
4 changed files with 127 additions and 1 deletions

View File

@ -444,7 +444,8 @@ func (w *AtomicWriter) writePayloadToDir(payload map[string]FileProjection, dir
if fileProjection.FsUser == nil {
continue
}
if err := os.Chown(fullPath, int(*fileProjection.FsUser), -1); err != nil {
if err := w.chown(fullPath, int(*fileProjection.FsUser), -1); err != nil {
klog.Errorf("%s: unable to change file %s with owner %v: %v", w.logContext, fullPath, int(*fileProjection.FsUser), err)
return err
}

View File

@ -0,0 +1,27 @@
//go:build linux
// +build linux
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import "os"
// chown changes the numeric uid and gid of the named file.
func (w *AtomicWriter) chown(name string, uid, gid int) error {
return os.Chown(name, uid, gid)
}

View File

@ -0,0 +1,33 @@
//go:build !linux
// +build !linux
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"runtime"
"k8s.io/klog/v2"
)
// chown changes the numeric uid and gid of the named file.
// This is a no-op on unsupported platforms.
func (w *AtomicWriter) chown(name string, uid, _ /* gid */ int) error {
klog.Warningf("%s: skipping change of Linux owner %v for file %s; unsupported on %s", w.logContext, uid, name, runtime.GOOS)
return nil
}

View File

@ -28,15 +28,19 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/test/e2e/feature"
"k8s.io/kubernetes/test/e2e/framework"
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
testutils "k8s.io/kubernetes/test/utils"
"k8s.io/kubernetes/test/utils/format"
imageutils "k8s.io/kubernetes/test/utils/image"
admissionapi "k8s.io/pod-security-admission/api"
"k8s.io/utils/ptr"
)
const runAsUserNameContainerName = "run-as-username-container"
@ -193,6 +197,67 @@ var _ = sigDescribe(feature.Windows, "SecurityContext", skipUnlessWindows(func()
})
}))
var _ = sigDescribe(feature.Windows, "SecurityContext", skipUnlessWindows(func() {
f := framework.NewDefaultFramework("windows-with-unsupported-fields")
f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
ginkgo.It("should be able to create pod and run containers", func(ctx context.Context) {
ginkgo.By("Creating 1 pods: run with unsupported fields")
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "run-ignore-unsupported-fields",
Namespace: f.Namespace.Name,
},
Spec: v1.PodSpec{
NodeSelector: map[string]string{"kubernetes.io/os": "windows"},
Containers: []v1.Container{
{
Name: "test-container",
Image: imageutils.GetE2EImage(imageutils.Pause),
},
},
SecurityContext: &v1.PodSecurityContext{
RunAsUser: ptr.To[int64](999), // windows does not support
RunAsGroup: ptr.To[int64](999), // windows does not support
RunAsNonRoot: ptr.To(true),
},
RestartPolicy: v1.RestartPolicyNever,
},
}
pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, pod, metav1.CreateOptions{})
framework.ExpectNoError(err, "Error creating pod")
podErr := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
// Get the logs and events before calling ExpectNoError, so we can debug any errors.
var logs string
var events *v1.EventList
if err := wait.PollUntilContextTimeout(ctx, 30*time.Second, 2*time.Minute, true, func(ctx context.Context) (done bool, err error) {
framework.Logf("polling logs")
logs, err = e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name)
if err != nil {
framework.Logf("Error pulling logs: %v", err)
return false, nil
}
events, err = f.ClientSet.CoreV1().Events(pod.Namespace).Search(scheme.Scheme, pod)
if err != nil {
return false, fmt.Errorf("error in listing events: %w", err)
}
return true, nil
}); err != nil {
framework.Failf("Unexpected error getting pod logs/events: %v", err)
} else {
framework.Logf("Pod logs: \n%v", logs)
framework.Logf("Pod events: \n%v", format.Object(events, 1))
}
framework.ExpectNoError(podErr)
})
}))
func runAsUserNamePod(username *string) *v1.Pod {
podName := "run-as-username-" + string(uuid.NewUUID())
return &v1.Pod{