diff --git a/hack/testdata/pod-run-as-non-root.yaml b/hack/testdata/pod-run-as-non-root.yaml new file mode 100644 index 00000000000..330088260b3 --- /dev/null +++ b/hack/testdata/pod-run-as-non-root.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + labels: + run: target + name: target +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + runAsNonRoot: true + containers: + - image: busybox + name: target + command: ["/bin/sh", "-c", "sleep 100"] diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go b/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go index 1cb325be988..8df40aefa92 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go @@ -75,6 +75,9 @@ var ( debugging utilities without restarting the pod. * Node: Create a new pod that runs in the node's host namespaces and can access the node's filesystem. + + Note: When a non-root user is configured for the entire target Pod, some capabilities granted + by debug profile may not work. `)) debugExample = templates.Examples(i18n.T(` @@ -495,6 +498,8 @@ func (o *DebugOptions) debugByEphemeralContainer(ctx context.Context, pod *corev } klog.V(2).Infof("new ephemeral container: %#v", debugContainer) + o.displayWarning((*corev1.Container)(&debugContainer.EphemeralContainerCommon), pod) + debugJS, err := json.Marshal(debugPod) if err != nil { return nil, "", fmt.Errorf("error creating JSON for debug container: %v", err) @@ -611,6 +616,16 @@ func (o *DebugOptions) debugByCopy(ctx context.Context, pod *corev1.Pod) (*corev if err != nil { return nil, "", err } + + var debugContainer *corev1.Container + for i := range copied.Spec.Containers { + if copied.Spec.Containers[i].Name == dc { + debugContainer = &copied.Spec.Containers[i] + break + } + } + o.displayWarning(debugContainer, copied) + created, err := o.podClient.Pods(copied.Namespace).Create(ctx, copied, metav1.CreateOptions{}) if err != nil { return nil, "", err @@ -624,6 +639,32 @@ func (o *DebugOptions) debugByCopy(ctx context.Context, pod *corev1.Pod) (*corev return created, dc, nil } +// Display warning message if some capabilities are set by profile and non-root user is specified in .Spec.SecurityContext.RunAsUser.(#1650) +func (o *DebugOptions) displayWarning(container *corev1.Container, pod *corev1.Pod) { + if container == nil { + return + } + + if pod.Spec.SecurityContext.RunAsUser == nil || *pod.Spec.SecurityContext.RunAsUser == 0 { + return + } + + if container.SecurityContext == nil { + return + } + + if container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 { + return + } + + if (container.SecurityContext.Privileged == nil || !*container.SecurityContext.Privileged) && + (container.SecurityContext.Capabilities == nil || len(container.SecurityContext.Capabilities.Add) == 0) { + return + } + + _, _ = fmt.Fprintln(o.ErrOut, `Warning: Non-root user is configured for the entire target Pod, and some capabilities granted by debug profile may not work. Please consider using "--custom" with a custom profile that specifies "securityContext.runAsUser: 0".`) +} + // generateDebugContainer returns a debugging pod and an EphemeralContainer suitable for use as a debug container // in the given pod. func (o *DebugOptions) generateDebugContainer(pod *corev1.Pod) (*corev1.Pod, *corev1.EphemeralContainer, error) { diff --git a/test/cmd/debug.sh b/test/cmd/debug.sh index bb49fa2d1fc..f8b4e53e211 100755 --- a/test/cmd/debug.sh +++ b/test/cmd/debug.sh @@ -659,3 +659,34 @@ EOF set +o nounset set +o errexit } + +run_kubectl_debug_warning_tests() { + set -o nounset + set -o errexit + + create_and_use_new_namespace + kube::log::status "Testing kubectl debug warning" + + ### Non-root Pod Troubleshooting by ephemeral containers + # Pre-Condition: Non-root Pod "busybox" is created + kubectl create -f hack/testdata/pod-run-as-non-root.yaml "${kube_flags[@]:?}" + kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' + # Command: add a new debug container with netadmin profile + output_message=$(kubectl debug target -it --image=busybox --container=debug-container --attach=false --profile=netadmin "${kube_flags[@]:?}" 2>&1) + kube::test::if_has_string "${output_message}" 'Warning: Non-root user is configured for the entire target Pod, and some capabilities granted by debug profile may not work. Please consider using "--custom" with a custom profile that specifies "securityContext.runAsUser: 0".' + # Clean up + kubectl delete pod target "${kube_flags[@]:?}" + + ### Non-root Pod Troubleshooting by pod copy + # Pre-Condition: Non-root Pod "busybox" is created + kubectl create -f hack/testdata/pod-run-as-non-root.yaml "${kube_flags[@]:?}" + kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' + # Command: create a copy of target with a new debug container + output_message=$(kubectl debug target -it --copy-to=target-copy --image=busybox --container=debug-container --attach=false --profile=netadmin "${kube_flags[@]:?}" 2>&1) + kube::test::if_has_string "${output_message}" 'Warning: Non-root user is configured for the entire target Pod, and some capabilities granted by debug profile may not work. Please consider using "--custom" with a custom profile that specifies "securityContext.runAsUser: 0".' + # Clean up + kubectl delete pod target "${kube_flags[@]:?}" + + set +o nounset + set +o errexit +} diff --git a/test/cmd/legacy-script.sh b/test/cmd/legacy-script.sh index 4f9177a5612..ebcefb676f1 100755 --- a/test/cmd/legacy-script.sh +++ b/test/cmd/legacy-script.sh @@ -1048,6 +1048,7 @@ runTests() { record_command run_kubectl_debug_restricted_tests record_command run_kubectl_debug_netadmin_tests record_command run_kubectl_debug_custom_profile_tests + record_command run_kubectl_debug_warning_tests fi if kube::test::if_supports_resource "${nodes}" ; then record_command run_kubectl_debug_node_tests