k8s.io/kubernetes@v1.29.3/test/e2e/common/node/sysctl.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package node
    18  
    19  import (
    20  	"context"
    21  
    22  	v1 "k8s.io/api/core/v1"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/util/uuid"
    25  	"k8s.io/kubernetes/test/e2e/environment"
    26  	"k8s.io/kubernetes/test/e2e/framework"
    27  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    28  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    29  	imageutils "k8s.io/kubernetes/test/utils/image"
    30  	admissionapi "k8s.io/pod-security-admission/api"
    31  
    32  	"github.com/onsi/ginkgo/v2"
    33  	"github.com/onsi/gomega"
    34  )
    35  
    36  var _ = SIGDescribe("Sysctls [LinuxOnly]", framework.WithNodeConformance(), func() {
    37  
    38  	ginkgo.BeforeEach(func() {
    39  		// sysctl is not supported on Windows.
    40  		e2eskipper.SkipIfNodeOSDistroIs("windows")
    41  	})
    42  
    43  	f := framework.NewDefaultFramework("sysctl")
    44  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
    45  	var podClient *e2epod.PodClient
    46  
    47  	testPod := func() *v1.Pod {
    48  		podName := "sysctl-" + string(uuid.NewUUID())
    49  		pod := v1.Pod{
    50  			ObjectMeta: metav1.ObjectMeta{
    51  				Name:        podName,
    52  				Annotations: map[string]string{},
    53  			},
    54  			Spec: v1.PodSpec{
    55  				Containers: []v1.Container{
    56  					{
    57  						Name:  "test-container",
    58  						Image: imageutils.GetE2EImage(imageutils.BusyBox),
    59  					},
    60  				},
    61  				RestartPolicy: v1.RestartPolicyNever,
    62  			},
    63  		}
    64  
    65  		return &pod
    66  	}
    67  
    68  	ginkgo.BeforeEach(func() {
    69  		podClient = e2epod.NewPodClient(f)
    70  	})
    71  
    72  	/*
    73  	  Release: v1.21
    74  	  Testname: Sysctl, test sysctls
    75  	  Description: Pod is created with kernel.shm_rmid_forced sysctl. Kernel.shm_rmid_forced must be set to 1
    76  	  [LinuxOnly]: This test is marked as LinuxOnly since Windows does not support sysctls
    77  	  [Environment:NotInUserNS]: The test fails in UserNS (as expected): `open /proc/sys/kernel/shm_rmid_forced: permission denied`
    78  	*/
    79  	framework.ConformanceIt("should support sysctls [MinimumKubeletVersion:1.21]", environment.NotInUserNS, func(ctx context.Context) {
    80  		pod := testPod()
    81  		pod.Spec.SecurityContext = &v1.PodSecurityContext{
    82  			Sysctls: []v1.Sysctl{
    83  				{
    84  					Name:  "kernel.shm_rmid_forced",
    85  					Value: "1",
    86  				},
    87  			},
    88  		}
    89  		pod.Spec.Containers[0].Command = []string{"/bin/sysctl", "kernel.shm_rmid_forced"}
    90  
    91  		ginkgo.By("Creating a pod with the kernel.shm_rmid_forced sysctl")
    92  		pod = podClient.Create(ctx, pod)
    93  
    94  		ginkgo.By("Watching for error events or started pod")
    95  		// watch for events instead of termination of pod because the kubelet deletes
    96  		// failed pods without running containers. This would create a race as the pod
    97  		// might have already been deleted here.
    98  		ev, err := e2epod.NewPodClient(f).WaitForErrorEventOrSuccess(ctx, pod)
    99  		framework.ExpectNoError(err)
   100  		gomega.Expect(ev).To(gomega.BeNil())
   101  
   102  		ginkgo.By("Waiting for pod completion")
   103  		err = e2epod.WaitForPodNoLongerRunningInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name)
   104  		framework.ExpectNoError(err)
   105  		pod, err = podClient.Get(ctx, pod.Name, metav1.GetOptions{})
   106  		framework.ExpectNoError(err)
   107  
   108  		ginkgo.By("Checking that the pod succeeded")
   109  		gomega.Expect(pod.Status.Phase).To(gomega.Equal(v1.PodSucceeded))
   110  
   111  		ginkgo.By("Getting logs from the pod")
   112  		log, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name)
   113  		framework.ExpectNoError(err)
   114  
   115  		ginkgo.By("Checking that the sysctl is actually updated")
   116  		gomega.Expect(log).To(gomega.ContainSubstring("kernel.shm_rmid_forced = 1"))
   117  	})
   118  
   119  	/*
   120  	  Release: v1.21
   121  	  Testname: Sysctls, reject invalid sysctls
   122  	  Description: Pod is created with one valid and two invalid sysctls. Pod should not apply invalid sysctls.
   123  	  [LinuxOnly]: This test is marked as LinuxOnly since Windows does not support sysctls
   124  	*/
   125  	framework.ConformanceIt("should reject invalid sysctls [MinimumKubeletVersion:1.21]", func(ctx context.Context) {
   126  		pod := testPod()
   127  		pod.Spec.SecurityContext = &v1.PodSecurityContext{
   128  			Sysctls: []v1.Sysctl{
   129  				// Safe parameters
   130  				{
   131  					Name:  "foo-",
   132  					Value: "bar",
   133  				},
   134  				{
   135  					Name:  "kernel.shmmax",
   136  					Value: "100000000",
   137  				},
   138  				{
   139  					Name:  "safe-and-unsafe",
   140  					Value: "100000000",
   141  				},
   142  				{
   143  					Name:  "bar..",
   144  					Value: "42",
   145  				},
   146  			},
   147  		}
   148  
   149  		ginkgo.By("Creating a pod with one valid and two invalid sysctls")
   150  		client := f.ClientSet.CoreV1().Pods(f.Namespace.Name)
   151  		_, err := client.Create(ctx, pod, metav1.CreateOptions{})
   152  
   153  		gomega.Expect(err).To(gomega.MatchError(gomega.SatisfyAll(
   154  			gomega.ContainSubstring(`Invalid value: "foo-"`),
   155  			gomega.ContainSubstring(`Invalid value: "bar.."`),
   156  			gomega.Not(gomega.ContainSubstring(`safe-and-unsafe`)),
   157  			gomega.Not(gomega.ContainSubstring("kernel.shmmax")),
   158  		)))
   159  	})
   160  
   161  	// Pod is created with kernel.msgmax, an unsafe sysctl.
   162  	ginkgo.It("should not launch unsafe, but not explicitly enabled sysctls on the node [MinimumKubeletVersion:1.21]", func(ctx context.Context) {
   163  		pod := testPod()
   164  		pod.Spec.SecurityContext = &v1.PodSecurityContext{
   165  			Sysctls: []v1.Sysctl{
   166  				{
   167  					Name:  "kernel.msgmax",
   168  					Value: "10000000000",
   169  				},
   170  			},
   171  		}
   172  
   173  		ginkgo.By("Creating a pod with an ignorelisted, but not allowlisted sysctl on the node")
   174  		pod = podClient.Create(ctx, pod)
   175  
   176  		ginkgo.By("Wait for pod failed reason")
   177  		// watch for pod failed reason instead of termination of pod
   178  		err := e2epod.WaitForPodFailedReason(ctx, f.ClientSet, pod, "SysctlForbidden", f.Timeouts.PodStart)
   179  		framework.ExpectNoError(err)
   180  	})
   181  
   182  	/*
   183  	  Release: v1.23
   184  	  Testname: Sysctl, test sysctls supports slashes
   185  	  Description: Pod is created with kernel/shm_rmid_forced sysctl. Support slashes as sysctl separator. The '/' separator is also accepted in place of a '.'
   186  	  [LinuxOnly]: This test is marked as LinuxOnly since Windows does not support sysctls
   187  	  [Environment:NotInUserNS]: The test fails in UserNS (as expected): `open /proc/sys/kernel/shm_rmid_forced: permission denied`
   188  	*/
   189  	f.It("should support sysctls with slashes as separator [MinimumKubeletVersion:1.23]", environment.NotInUserNS, func(ctx context.Context) {
   190  		pod := testPod()
   191  		pod.Spec.SecurityContext = &v1.PodSecurityContext{
   192  			Sysctls: []v1.Sysctl{
   193  				{
   194  					Name:  "kernel/shm_rmid_forced",
   195  					Value: "1",
   196  				},
   197  			},
   198  		}
   199  		pod.Spec.Containers[0].Command = []string{"/bin/sysctl", "kernel/shm_rmid_forced"}
   200  
   201  		ginkgo.By("Creating a pod with the kernel/shm_rmid_forced sysctl")
   202  		pod = podClient.Create(ctx, pod)
   203  
   204  		ginkgo.By("Watching for error events or started pod")
   205  		// watch for events instead of termination of pod because the kubelet deletes
   206  		// failed pods without running containers. This would create a race as the pod
   207  		// might have already been deleted here.
   208  		ev, err := e2epod.NewPodClient(f).WaitForErrorEventOrSuccess(ctx, pod)
   209  		framework.ExpectNoError(err)
   210  		gomega.Expect(ev).To(gomega.BeNil())
   211  
   212  		ginkgo.By("Waiting for pod completion")
   213  		err = e2epod.WaitForPodNoLongerRunningInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name)
   214  		framework.ExpectNoError(err)
   215  		pod, err = podClient.Get(ctx, pod.Name, metav1.GetOptions{})
   216  		framework.ExpectNoError(err)
   217  
   218  		ginkgo.By("Checking that the pod succeeded")
   219  		gomega.Expect(pod.Status.Phase).To(gomega.Equal(v1.PodSucceeded))
   220  
   221  		ginkgo.By("Getting logs from the pod")
   222  		log, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name)
   223  		framework.ExpectNoError(err)
   224  
   225  		ginkgo.By("Checking that the sysctl is actually updated")
   226  		// Note that either "/" or "."  may be used as separators within sysctl variable names.
   227  		// "kernel.shm_rmid_forced=1" and "kernel/shm_rmid_forced=1" are equivalent.
   228  		// Run "/bin/sysctl kernel/shm_rmid_forced" command on Linux system
   229  		// The displayed result is "kernel.shm_rmid_forced=1"
   230  		// Therefore, the substring that needs to be checked for the obtained pod log is
   231  		// "kernel.shm_rmid_forced=1" instead of "kernel/shm_rmid_forced=1".
   232  		gomega.Expect(log).To(gomega.ContainSubstring("kernel.shm_rmid_forced = 1"))
   233  	})
   234  })