k8s.io/kubernetes@v1.29.3/test/e2e_node/security_context_test.go (about) 1 /* 2 Copyright 2017 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 e2enode 18 19 import ( 20 "context" 21 "fmt" 22 "net" 23 "os/exec" 24 "strings" 25 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/util/sets" 29 "k8s.io/apimachinery/pkg/util/uuid" 30 "k8s.io/kubernetes/test/e2e/framework" 31 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 32 "k8s.io/kubernetes/test/e2e/nodefeature" 33 imageutils "k8s.io/kubernetes/test/utils/image" 34 admissionapi "k8s.io/pod-security-admission/api" 35 36 "github.com/onsi/ginkgo/v2" 37 ) 38 39 var _ = SIGDescribe("Security Context", func() { 40 f := framework.NewDefaultFramework("security-context-test") 41 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 42 var podClient *e2epod.PodClient 43 ginkgo.BeforeEach(func() { 44 podClient = e2epod.NewPodClient(f) 45 }) 46 47 f.Context(framework.WithNodeConformance(), "[LinuxOnly] Container PID namespace sharing", func() { 48 ginkgo.It("containers in pods using isolated PID namespaces should all receive PID 1", func(ctx context.Context) { 49 ginkgo.By("Create a pod with isolated PID namespaces.") 50 e2epod.NewPodClient(f).CreateSync(ctx, &v1.Pod{ 51 ObjectMeta: metav1.ObjectMeta{Name: "isolated-pid-ns-test-pod"}, 52 Spec: v1.PodSpec{ 53 Containers: []v1.Container{ 54 { 55 Name: "test-container-1", 56 Image: imageutils.GetE2EImage(imageutils.BusyBox), 57 Command: []string{"/bin/top"}, 58 }, 59 { 60 Name: "test-container-2", 61 Image: imageutils.GetE2EImage(imageutils.BusyBox), 62 Command: []string{"/bin/sleep"}, 63 Args: []string{"10000"}, 64 }, 65 }, 66 }, 67 }) 68 69 ginkgo.By("Check if both containers receive PID 1.") 70 pid1 := e2epod.ExecCommandInContainer(f, "isolated-pid-ns-test-pod", "test-container-1", "/bin/pidof", "top") 71 pid2 := e2epod.ExecCommandInContainer(f, "isolated-pid-ns-test-pod", "test-container-2", "/bin/pidof", "sleep") 72 if pid1 != "1" || pid2 != "1" { 73 framework.Failf("PIDs of different containers are not all 1: test-container-1=%v, test-container-2=%v", pid1, pid2) 74 } 75 }) 76 77 ginkgo.It("processes in containers sharing a pod namespace should be able to see each other", func(ctx context.Context) { 78 ginkgo.By("Create a pod with shared PID namespace.") 79 e2epod.NewPodClient(f).CreateSync(ctx, &v1.Pod{ 80 ObjectMeta: metav1.ObjectMeta{Name: "shared-pid-ns-test-pod"}, 81 Spec: v1.PodSpec{ 82 ShareProcessNamespace: &[]bool{true}[0], 83 Containers: []v1.Container{ 84 { 85 Name: "test-container-1", 86 Image: imageutils.GetE2EImage(imageutils.BusyBox), 87 Command: []string{"/bin/top"}, 88 }, 89 { 90 Name: "test-container-2", 91 Image: imageutils.GetE2EImage(imageutils.BusyBox), 92 Command: []string{"/bin/sleep"}, 93 Args: []string{"10000"}, 94 }, 95 }, 96 }, 97 }) 98 99 ginkgo.By("Check if the process in one container is visible to the process in the other.") 100 pid1 := e2epod.ExecCommandInContainer(f, "shared-pid-ns-test-pod", "test-container-1", "/bin/pidof", "top") 101 pid2 := e2epod.ExecCommandInContainer(f, "shared-pid-ns-test-pod", "test-container-2", "/bin/pidof", "top") 102 if pid1 != pid2 { 103 framework.Failf("PIDs are not the same in different containers: test-container-1=%v, test-container-2=%v", pid1, pid2) 104 } 105 }) 106 }) 107 108 ginkgo.Context("when creating a pod in the host PID namespace", func() { 109 makeHostPidPod := func(podName, image string, command []string, hostPID bool) *v1.Pod { 110 return &v1.Pod{ 111 ObjectMeta: metav1.ObjectMeta{ 112 Name: podName, 113 }, 114 Spec: v1.PodSpec{ 115 RestartPolicy: v1.RestartPolicyNever, 116 HostPID: hostPID, 117 Containers: []v1.Container{ 118 { 119 Image: image, 120 Name: podName, 121 Command: command, 122 }, 123 }, 124 }, 125 } 126 } 127 createAndWaitHostPidPod := func(ctx context.Context, podName string, hostPID bool) { 128 podClient.Create(ctx, makeHostPidPod(podName, 129 busyboxImage, 130 []string{"sh", "-c", "pidof nginx || true"}, 131 hostPID, 132 )) 133 134 podClient.WaitForSuccess(ctx, podName, framework.PodStartTimeout) 135 } 136 137 nginxPid := "" 138 ginkgo.BeforeEach(func(ctx context.Context) { 139 nginxPodName := "nginx-hostpid-" + string(uuid.NewUUID()) 140 podClient.CreateSync(ctx, makeHostPidPod(nginxPodName, 141 imageutils.GetE2EImage(imageutils.Nginx), 142 nil, 143 true, 144 )) 145 146 output := e2epod.ExecShellInContainer(f, nginxPodName, nginxPodName, 147 "cat /var/run/nginx.pid") 148 nginxPid = strings.TrimSpace(output) 149 }) 150 151 f.It("should show its pid in the host PID namespace", nodefeature.HostAccess, func(ctx context.Context) { 152 busyboxPodName := "busybox-hostpid-" + string(uuid.NewUUID()) 153 createAndWaitHostPidPod(ctx, busyboxPodName, true) 154 logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, busyboxPodName, busyboxPodName) 155 if err != nil { 156 framework.Failf("GetPodLogs for pod %q failed: %v", busyboxPodName, err) 157 } 158 159 pids := strings.TrimSpace(logs) 160 framework.Logf("Got nginx's pid %q from pod %q", pids, busyboxPodName) 161 if pids == "" { 162 framework.Failf("nginx's pid should be seen by hostpid containers") 163 } 164 165 pidSets := sets.NewString(strings.Split(pids, " ")...) 166 if !pidSets.Has(nginxPid) { 167 framework.Failf("nginx's pid should be seen by hostpid containers") 168 } 169 }) 170 171 f.It("should not show its pid in the non-hostpid containers", nodefeature.HostAccess, func(ctx context.Context) { 172 busyboxPodName := "busybox-non-hostpid-" + string(uuid.NewUUID()) 173 createAndWaitHostPidPod(ctx, busyboxPodName, false) 174 logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, busyboxPodName, busyboxPodName) 175 if err != nil { 176 framework.Failf("GetPodLogs for pod %q failed: %v", busyboxPodName, err) 177 } 178 179 pids := strings.TrimSpace(logs) 180 framework.Logf("Got nginx's pid %q from pod %q", pids, busyboxPodName) 181 pidSets := sets.NewString(strings.Split(pids, " ")...) 182 if pidSets.Has(nginxPid) { 183 framework.Failf("nginx's pid should not be seen by non-hostpid containers") 184 } 185 }) 186 }) 187 188 ginkgo.Context("when creating a pod in the host IPC namespace", func() { 189 makeHostIPCPod := func(podName, image string, command []string, hostIPC bool) *v1.Pod { 190 return &v1.Pod{ 191 ObjectMeta: metav1.ObjectMeta{ 192 Name: podName, 193 }, 194 Spec: v1.PodSpec{ 195 RestartPolicy: v1.RestartPolicyNever, 196 HostIPC: hostIPC, 197 Containers: []v1.Container{ 198 { 199 Image: image, 200 Name: podName, 201 Command: command, 202 }, 203 }, 204 }, 205 } 206 } 207 createAndWaitHostIPCPod := func(ctx context.Context, podName string, hostNetwork bool) { 208 podClient.Create(ctx, makeHostIPCPod(podName, 209 imageutils.GetE2EImage(imageutils.IpcUtils), 210 []string{"sh", "-c", "ipcs -m | awk '{print $2}'"}, 211 hostNetwork, 212 )) 213 214 podClient.WaitForSuccess(ctx, podName, framework.PodStartTimeout) 215 } 216 217 hostSharedMemoryID := "" 218 ginkgo.BeforeEach(func() { 219 output, err := exec.Command("sh", "-c", "ipcmk -M 1048576 | awk '{print $NF}'").Output() 220 if err != nil { 221 framework.Failf("Failed to create the shared memory on the host: %v", err) 222 } 223 hostSharedMemoryID = strings.TrimSpace(string(output)) 224 framework.Logf("Got host shared memory ID %q", hostSharedMemoryID) 225 }) 226 227 f.It("should show the shared memory ID in the host IPC containers", nodefeature.HostAccess, func(ctx context.Context) { 228 ipcutilsPodName := "ipcutils-hostipc-" + string(uuid.NewUUID()) 229 createAndWaitHostIPCPod(ctx, ipcutilsPodName, true) 230 logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, ipcutilsPodName, ipcutilsPodName) 231 if err != nil { 232 framework.Failf("GetPodLogs for pod %q failed: %v", ipcutilsPodName, err) 233 } 234 235 podSharedMemoryIDs := strings.TrimSpace(logs) 236 framework.Logf("Got shared memory IDs %q from pod %q", podSharedMemoryIDs, ipcutilsPodName) 237 if !strings.Contains(podSharedMemoryIDs, hostSharedMemoryID) { 238 framework.Failf("hostIPC container should show shared memory IDs on host") 239 } 240 }) 241 242 f.It("should not show the shared memory ID in the non-hostIPC containers", nodefeature.HostAccess, func(ctx context.Context) { 243 ipcutilsPodName := "ipcutils-non-hostipc-" + string(uuid.NewUUID()) 244 createAndWaitHostIPCPod(ctx, ipcutilsPodName, false) 245 logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, ipcutilsPodName, ipcutilsPodName) 246 if err != nil { 247 framework.Failf("GetPodLogs for pod %q failed: %v", ipcutilsPodName, err) 248 } 249 250 podSharedMemoryIDs := strings.TrimSpace(logs) 251 framework.Logf("Got shared memory IDs %q from pod %q", podSharedMemoryIDs, ipcutilsPodName) 252 if strings.Contains(podSharedMemoryIDs, hostSharedMemoryID) { 253 framework.Failf("non-hostIPC container should not show shared memory IDs on host") 254 } 255 }) 256 257 ginkgo.AfterEach(func() { 258 if hostSharedMemoryID != "" { 259 _, err := exec.Command("sh", "-c", fmt.Sprintf("ipcrm -m %q", hostSharedMemoryID)).Output() 260 if err != nil { 261 framework.Failf("Failed to remove shared memory %q on the host: %v", hostSharedMemoryID, err) 262 } 263 } 264 }) 265 }) 266 267 ginkgo.Context("when creating a pod in the host network namespace", func() { 268 makeHostNetworkPod := func(podName, image string, command []string, hostNetwork bool) *v1.Pod { 269 return &v1.Pod{ 270 ObjectMeta: metav1.ObjectMeta{ 271 Name: podName, 272 }, 273 Spec: v1.PodSpec{ 274 RestartPolicy: v1.RestartPolicyNever, 275 HostNetwork: hostNetwork, 276 Containers: []v1.Container{ 277 { 278 Image: image, 279 Name: podName, 280 Command: command, 281 }, 282 }, 283 }, 284 } 285 } 286 listListeningPortsCommand := []string{"sh", "-c", "netstat -ln"} 287 createAndWaitHostNetworkPod := func(ctx context.Context, podName string, hostNetwork bool) { 288 podClient.Create(ctx, makeHostNetworkPod(podName, 289 busyboxImage, 290 listListeningPortsCommand, 291 hostNetwork, 292 )) 293 294 podClient.WaitForSuccess(ctx, podName, framework.PodStartTimeout) 295 } 296 297 listeningPort := "" 298 var l net.Listener 299 var err error 300 ginkgo.BeforeEach(func() { 301 l, err = net.Listen("tcp", ":0") 302 if err != nil { 303 framework.Failf("Failed to open a new tcp port: %v", err) 304 } 305 addr := strings.Split(l.Addr().String(), ":") 306 listeningPort = addr[len(addr)-1] 307 framework.Logf("Opened a new tcp port %q", listeningPort) 308 }) 309 310 f.It("should listen on same port in the host network containers", nodefeature.HostAccess, func(ctx context.Context) { 311 busyboxPodName := "busybox-hostnetwork-" + string(uuid.NewUUID()) 312 createAndWaitHostNetworkPod(ctx, busyboxPodName, true) 313 logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, busyboxPodName, busyboxPodName) 314 if err != nil { 315 framework.Failf("GetPodLogs for pod %q failed: %v", busyboxPodName, err) 316 } 317 318 framework.Logf("Got logs for pod %q: %q", busyboxPodName, logs) 319 if !strings.Contains(logs, listeningPort) { 320 framework.Failf("host-networked container should listening on same port as host") 321 } 322 }) 323 324 f.It("shouldn't show the same port in the non-hostnetwork containers", nodefeature.HostAccess, func(ctx context.Context) { 325 busyboxPodName := "busybox-non-hostnetwork-" + string(uuid.NewUUID()) 326 createAndWaitHostNetworkPod(ctx, busyboxPodName, false) 327 logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, f.Namespace.Name, busyboxPodName, busyboxPodName) 328 if err != nil { 329 framework.Failf("GetPodLogs for pod %q failed: %v", busyboxPodName, err) 330 } 331 332 framework.Logf("Got logs for pod %q: %q", busyboxPodName, logs) 333 if strings.Contains(logs, listeningPort) { 334 framework.Failf("non-hostnetworked container shouldn't show the same port as host") 335 } 336 }) 337 338 ginkgo.AfterEach(func() { 339 if l != nil { 340 l.Close() 341 } 342 }) 343 }) 344 })