/* Copyright 2020 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 debug import ( "fmt" "testing" "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" ) var testNode = &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "node-XXX", }, } func TestLegacyProfile(t *testing.T) { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", }, }, }}, } tests := map[string]struct { pod *corev1.Pod containerName string target runtime.Object expectPod *corev1.Pod expectErr bool }{ "bad inputs results in error": { pod: nil, containerName: "dbg", target: runtime.Object(nil), expectErr: true, }, "debug by ephemeral container": { pod: pod, containerName: "dbg", target: pod, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{Name: "dbg", Image: "dbgimage"}, }, }}, }, }, "debug by pod copy": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN"}, }, }, }, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN"}, }, }, }, }, }, }, }, "debug by node": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN"}, }, }, }, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ HostNetwork: true, HostPID: true, HostIPC: true, Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN"}, }, }, VolumeMounts: []corev1.VolumeMount{ { MountPath: "/host", Name: "host-root", }, }, }, }, Volumes: []corev1.Volume{ { Name: "host-root", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{Path: "/"}, }, }, }, }, }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { applier := &legacyProfile{KeepFlags{InitContainers: true}} err := applier.Apply(test.pod, test.containerName, test.target) if (err != nil) != test.expectErr { t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) } if err != nil { return } if diff := cmp.Diff(test.expectPod, test.pod); diff != "" { t.Error("unexpected diff in generated object: (-want +got):\n", diff) } }) } } func TestGeneralProfile(t *testing.T) { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", }, }, }}, } tests := map[string]struct { pod *corev1.Pod containerName string target runtime.Object expectPod *corev1.Pod expectErr bool }{ "bad inputs results in error": { pod: nil, containerName: "dbg", target: runtime.Object(nil), expectErr: true, }, "debug by ephemeral container": { pod: pod, containerName: "dbg", target: pod, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, }}, }, }, "debug by pod copy": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN"}, }, }, }, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ {Name: "app", Image: "appimage"}, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN", "SYS_PTRACE"}, }, }, }, }, ShareProcessNamespace: ptr.To(true), }, }, }, "debug by node": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ HostNetwork: true, HostPID: true, HostIPC: true, Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", VolumeMounts: []corev1.VolumeMount{ { MountPath: "/host", Name: "host-root", }, }, }, }, Volumes: []corev1.Volume{ { Name: "host-root", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{Path: "/"}, }, }, }, }, }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { applier := &generalProfile{KeepFlags{InitContainers: true}} err := applier.Apply(test.pod, test.containerName, test.target) if (err != nil) != test.expectErr { t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) } if err != nil { return } if diff := cmp.Diff(test.expectPod, test.pod); diff != "" { t.Error("unexpected diff in generated object: (-want +got):\n", diff) } }) } } func TestBaselineProfile(t *testing.T) { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, }}, } tests := map[string]struct { pod *corev1.Pod containerName string target runtime.Object expectPod *corev1.Pod expectErr bool }{ "bad inputs results in error": { pod: nil, containerName: "dbg", target: runtime.Object(nil), expectErr: true, }, "debug by ephemeral container": { pod: pod, containerName: "dbg", target: pod, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", }, }, }}, }, }, "debug by pod copy": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ ShareProcessNamespace: ptr.To(true), InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ {Name: "app", Image: "appimage"}, { Name: "dbg", Image: "dbgimage", }, }, }, }, }, "debug by node": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", }, }, }, }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { applier := &baselineProfile{KeepFlags{InitContainers: true}} err := applier.Apply(test.pod, test.containerName, test.target) if (err != nil) != test.expectErr { t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) } if err != nil { return } if diff := cmp.Diff(test.expectPod, test.pod); diff != "" { t.Error("unexpected diff in generated object: (-want +got):\n", diff) } }) } } func TestRestrictedProfile(t *testing.T) { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, }}, } tests := map[string]struct { pod *corev1.Pod containerName string target runtime.Object expectPod *corev1.Pod expectErr bool }{ "bad inputs results in error": { pod: nil, containerName: "dbg", target: runtime.Object(nil), expectErr: true, }, "debug by ephemeral container": { pod: pod, containerName: "dbg", target: pod, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, }, AllowPrivilegeEscalation: ptr.To(false), SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"}, }, }, }, }}, }, }, "debug by pod copy": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ ShareProcessNamespace: ptr.To(true), InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ {Name: "app", Image: "appimage"}, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, }, AllowPrivilegeEscalation: ptr.To(false), SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"}, }, }, }, }, }, }, "debug by node": { pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"ALL"}, }, AllowPrivilegeEscalation: ptr.To(false), SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"}, }, }, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, }, AllowPrivilegeEscalation: ptr.To(false), SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"}, }, }, }, }, }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { applier := &restrictedProfile{KeepFlags{InitContainers: true}} err := applier.Apply(test.pod, test.containerName, test.target) if (err != nil) != test.expectErr { t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) } if err != nil { return } if diff := cmp.Diff(test.expectPod, test.pod); diff != "" { t.Error("unexpected diff in generated object: (-want +got):\n", diff) } }) } } func TestNetAdminProfile(t *testing.T) { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", }, }, }}, } tests := []struct { name string pod *corev1.Pod containerName string target runtime.Object expectPod *corev1.Pod expectErr error }{ { name: "nil target", pod: pod, containerName: "dbg", target: nil, expectErr: fmt.Errorf("netadmin profile: objects of type are not supported"), }, { name: "debug by ephemeral container", pod: pod, containerName: "dbg", target: pod, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"}, }, }, }, }, }}, }, }, { name: "debug by pod copy", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ ShareProcessNamespace: ptr.To(true), InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ {Name: "app", Image: "appimage"}, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"}, }, }, }, }, }, }, }, { name: "debug by pod copy preserve existing capability", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ ShareProcessNamespace: ptr.To(true), Containers: []corev1.Container{ {Name: "app", Image: "appimage"}, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE", "NET_ADMIN", "NET_RAW"}, }, }, }, }, }, }, }, { name: "debug by node", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ HostNetwork: true, HostPID: true, HostIPC: true, Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"}, }, }, }, }, }, }, }, { name: "debug by node preserve existing capability", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ HostNetwork: true, HostPID: true, HostIPC: true, Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE", "NET_ADMIN", "NET_RAW"}, }, }, }, }, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { applier := &netadminProfile{KeepFlags{InitContainers: true}} err := applier.Apply(test.pod, test.containerName, test.target) if (err == nil) != (test.expectErr == nil) || (err != nil && test.expectErr != nil && err.Error() != test.expectErr.Error()) { t.Fatalf("expect error: %v, got error: %v", test.expectErr, err) } if err != nil { return } if diff := cmp.Diff(test.expectPod, test.pod); diff != "" { t.Error("unexpected diff in generated object: (-want +got):\n", diff) } }) } } func TestSysAdminProfile(t *testing.T) { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", }, }, }}, } tests := []struct { name string pod *corev1.Pod containerName string target runtime.Object expectPod *corev1.Pod expectErr error }{ { name: "nil target", pod: pod, containerName: "dbg", target: nil, expectErr: fmt.Errorf("sysadmin profile: objects of type are not supported"), }, { name: "debug by ephemeral container", pod: pod, containerName: "dbg", target: pod, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ { EphemeralContainerCommon: corev1.EphemeralContainerCommon{ Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Privileged: ptr.To(true), }, }, }, }}, }, }, { name: "debug by pod copy", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "podcopy", Labels: map[string]string{ "app": "podcopy", }, Annotations: map[string]string{ "test": "test", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{{Name: "init-container"}}, Containers: []corev1.Container{ {Name: "app", Image: "appimage"}, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Privileged: ptr.To(true), }, }, }, ShareProcessNamespace: ptr.To(true), }, }, }, { name: "debug by pod copy preserve existing capability", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, }, }, containerName: "dbg", target: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "app", Image: "appimage", LivenessProbe: &corev1.Probe{}, ReadinessProbe: &corev1.Probe{}, StartupProbe: &corev1.Probe{}, }, }, }, }, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ {Name: "app", Image: "appimage"}, { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Privileged: ptr.To(true), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, ShareProcessNamespace: ptr.To(true), }, }, }, { name: "debug by node", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ {Name: "dbg", Image: "dbgimage"}, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ HostNetwork: true, HostPID: true, HostIPC: true, Volumes: []corev1.Volume{ { Name: "host-root", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/"}}, }, }, Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Privileged: ptr.To(true), }, VolumeMounts: []corev1.VolumeMount{{Name: "host-root", MountPath: "/host"}}, }, }, }, }, }, { name: "debug by node preserve existing capability", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, }, }, }, }, containerName: "dbg", target: testNode, expectPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "pod"}, Spec: corev1.PodSpec{ HostNetwork: true, HostPID: true, HostIPC: true, Volumes: []corev1.Volume{ { Name: "host-root", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/"}}, }, }, Containers: []corev1.Container{ { Name: "dbg", Image: "dbgimage", SecurityContext: &corev1.SecurityContext{ Privileged: ptr.To(true), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"SYS_PTRACE"}, }, }, VolumeMounts: []corev1.VolumeMount{{Name: "host-root", MountPath: "/host"}}, }, }, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { applier := &sysadminProfile{KeepFlags{InitContainers: true}} err := applier.Apply(test.pod, test.containerName, test.target) if (err == nil) != (test.expectErr == nil) || (err != nil && test.expectErr != nil && err.Error() != test.expectErr.Error()) { t.Fatalf("expect error: %v, got error: %v", test.expectErr, err) } if err != nil { return } if diff := cmp.Diff(test.expectPod, test.pod); diff != "" { t.Error("unexpected diff in generated object: (-want +got):\n", diff) } }) } }