Merge pull request #119200 from eiffel-fl/francis/sysadmin-debug-profile
kubectl debug: add sysadmin profile Kubernetes-commit: 4163ce5017268b0ae25df327f0a210032ef1cc80
This commit is contained in:
		
						commit
						b73518af09
					
				
							
								
								
									
										10
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										10
									
								
								go.mod
								
								
								
								
							|  | @ -30,10 +30,10 @@ require ( | ||||||
| 	github.com/stretchr/testify v1.8.4 | 	github.com/stretchr/testify v1.8.4 | ||||||
| 	golang.org/x/sys v0.15.0 | 	golang.org/x/sys v0.15.0 | ||||||
| 	gopkg.in/yaml.v2 v2.4.0 | 	gopkg.in/yaml.v2 v2.4.0 | ||||||
| 	k8s.io/api v0.0.0-20240118211853-d5724e467262 | 	k8s.io/api v0.0.0-20240124211858-f3648a53522e | ||||||
| 	k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523 | 	k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523 | ||||||
| 	k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2 | 	k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2 | ||||||
| 	k8s.io/client-go v0.0.0-20240122172058-657d7be98b25 | 	k8s.io/client-go v0.0.0-20240124011219-8092c71d3605 | ||||||
| 	k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48 | 	k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48 | ||||||
| 	k8s.io/component-helpers v0.0.0-20240118212950-9a5801419916 | 	k8s.io/component-helpers v0.0.0-20240118212950-9a5801419916 | ||||||
| 	k8s.io/klog/v2 v2.120.1 | 	k8s.io/klog/v2 v2.120.1 | ||||||
|  | @ -96,11 +96,11 @@ require ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| replace ( | replace ( | ||||||
| 	k8s.io/api => k8s.io/api v0.0.0-20240118211853-d5724e467262 | 	k8s.io/api => k8s.io/api v0.0.0-20240124211858-f3648a53522e | ||||||
| 	k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523 | 	k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523 | ||||||
| 	k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2 | 	k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2 | ||||||
| 	k8s.io/client-go => k8s.io/client-go v0.0.0-20240122172058-657d7be98b25 | 	k8s.io/client-go => k8s.io/client-go v0.0.0-20240124011219-8092c71d3605 | ||||||
| 	k8s.io/code-generator => k8s.io/code-generator v0.0.0-20240118211431-5ad9f43b6468 | 	k8s.io/code-generator => k8s.io/code-generator v0.0.0-20240123225209-c781f8765cf8 | ||||||
| 	k8s.io/component-base => k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48 | 	k8s.io/component-base => k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48 | ||||||
| 	k8s.io/component-helpers => k8s.io/component-helpers v0.0.0-20240118212950-9a5801419916 | 	k8s.io/component-helpers => k8s.io/component-helpers v0.0.0-20240118212950-9a5801419916 | ||||||
| 	k8s.io/metrics => k8s.io/metrics v0.0.0-20240118214633-5b4611d6f391 | 	k8s.io/metrics => k8s.io/metrics v0.0.0-20240118214633-5b4611d6f391 | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										8
									
								
								go.sum
								
								
								
								
							|  | @ -280,14 +280,14 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
| honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
| k8s.io/api v0.0.0-20240118211853-d5724e467262 h1:iP5kk4e+c89xvFLRWdUb7BAYzBV9A//0vBHc9Ob/NUM= | k8s.io/api v0.0.0-20240124211858-f3648a53522e h1:Lv52wennNKzlcDrBtANztawHC8xaTllHm51WUKIP0Ew= | ||||||
| k8s.io/api v0.0.0-20240118211853-d5724e467262/go.mod h1:BZUGwl6J5EvsODp+6ZUA+9p7V4iWxVLcr70rnzIshpA= | k8s.io/api v0.0.0-20240124211858-f3648a53522e/go.mod h1:BZUGwl6J5EvsODp+6ZUA+9p7V4iWxVLcr70rnzIshpA= | ||||||
| k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523 h1:1iJCbQAZv58v4zxd0ECIIMnyYlFsPWa2hmjqGEsv/5g= | k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523 h1:1iJCbQAZv58v4zxd0ECIIMnyYlFsPWa2hmjqGEsv/5g= | ||||||
| k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523/go.mod h1:Oh3ZrffM1/I8O/43oAA+aoOYgSregIXHxcWJB9ZRfQ8= | k8s.io/apimachinery v0.0.0-20240118211638-f14778da5523/go.mod h1:Oh3ZrffM1/I8O/43oAA+aoOYgSregIXHxcWJB9ZRfQ8= | ||||||
| k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2 h1:KFiGqjF1kq6YDXgsWmsms2bu10g1M17P9HRO2lGYry4= | k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2 h1:KFiGqjF1kq6YDXgsWmsms2bu10g1M17P9HRO2lGYry4= | ||||||
| k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2/go.mod h1:zmPMirb3vXLcTGRaL9Pw5SifCP8EY2asTp+PTgnwSYI= | k8s.io/cli-runtime v0.0.0-20240118214801-ad54ff319bf2/go.mod h1:zmPMirb3vXLcTGRaL9Pw5SifCP8EY2asTp+PTgnwSYI= | ||||||
| k8s.io/client-go v0.0.0-20240122172058-657d7be98b25 h1:iBiouUazhDUHxrqDywgcbmARvao6UwVu+nSbIrIRh4k= | k8s.io/client-go v0.0.0-20240124011219-8092c71d3605 h1:Dw3Ctw+SS3YmJTpaYP2nhIs4XagL4ctjKY0pHxN4RT8= | ||||||
| k8s.io/client-go v0.0.0-20240122172058-657d7be98b25/go.mod h1:WuuT9L6+pj4rHmL2pb22xnOdtSvjiEcpB18g9Fuk0js= | k8s.io/client-go v0.0.0-20240124011219-8092c71d3605/go.mod h1:WuuT9L6+pj4rHmL2pb22xnOdtSvjiEcpB18g9Fuk0js= | ||||||
| k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48 h1:3HvTUZ0ry5c0P15P+glBxBj+eh8Uv2ijNvjEORH+oOQ= | k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48 h1:3HvTUZ0ry5c0P15P+glBxBj+eh8Uv2ijNvjEORH+oOQ= | ||||||
| k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48/go.mod h1:ANnr9YwsqK1XgjzXj9fGHEMDOp0QddDkKgQLLBPZ7Kg= | k8s.io/component-base v0.0.0-20240123212339-5f9f8131aa48/go.mod h1:ANnr9YwsqK1XgjzXj9fGHEMDOp0QddDkKgQLLBPZ7Kg= | ||||||
| k8s.io/component-helpers v0.0.0-20240118212950-9a5801419916 h1:Ptl0rZGRIrjdZuSqSO8Dwok1SWA9bqAi/Vcc3HYA/Ks= | k8s.io/component-helpers v0.0.0-20240118212950-9a5801419916 h1:Ptl0rZGRIrjdZuSqSO8Dwok1SWA9bqAi/Vcc3HYA/Ks= | ||||||
|  |  | ||||||
|  | @ -192,7 +192,7 @@ func (o *DebugOptions) AddFlags(cmd *cobra.Command) { | ||||||
| 	cmd.Flags().BoolVar(&o.ShareProcesses, "share-processes", o.ShareProcesses, i18n.T("When used with '--copy-to', enable process namespace sharing in the copy.")) | 	cmd.Flags().BoolVar(&o.ShareProcesses, "share-processes", o.ShareProcesses, i18n.T("When used with '--copy-to', enable process namespace sharing in the copy.")) | ||||||
| 	cmd.Flags().StringVar(&o.TargetContainer, "target", "", i18n.T("When using an ephemeral container, target processes in this container name.")) | 	cmd.Flags().StringVar(&o.TargetContainer, "target", "", i18n.T("When using an ephemeral container, target processes in this container name.")) | ||||||
| 	cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, i18n.T("Allocate a TTY for the debugging container.")) | 	cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, i18n.T("Allocate a TTY for the debugging container.")) | ||||||
| 	cmd.Flags().StringVar(&o.Profile, "profile", ProfileLegacy, i18n.T(`Debugging profile. Options are "legacy", "general", "baseline", "netadmin", or "restricted".`)) | 	cmd.Flags().StringVar(&o.Profile, "profile", ProfileLegacy, i18n.T(`Options are "legacy", "general", "baseline", "netadmin", "restricted" or "sysadmin".`)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Complete finishes run-time initialization of debug.DebugOptions.
 | // Complete finishes run-time initialization of debug.DebugOptions.
 | ||||||
|  |  | ||||||
|  | @ -316,6 +316,25 @@ func TestGenerateDebugContainer(t *testing.T) { | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "sysadmin profile", | ||||||
|  | 			opts: &DebugOptions{ | ||||||
|  | 				Image:      "busybox", | ||||||
|  | 				PullPolicy: corev1.PullIfNotPresent, | ||||||
|  | 				Profile:    ProfileSysadmin, | ||||||
|  | 			}, | ||||||
|  | 			expected: &corev1.EphemeralContainer{ | ||||||
|  | 				EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||||||
|  | 					Name:                     "debugger-1", | ||||||
|  | 					Image:                    "busybox", | ||||||
|  | 					ImagePullPolicy:          corev1.PullIfNotPresent, | ||||||
|  | 					TerminationMessagePolicy: corev1.TerminationMessageReadFile, | ||||||
|  | 					SecurityContext: &corev1.SecurityContext{ | ||||||
|  | 						Privileged: pointer.Bool(true), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
| 	} { | 	} { | ||||||
| 		t.Run(tc.name, func(t *testing.T) { | 		t.Run(tc.name, func(t *testing.T) { | ||||||
| 			tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard() | 			tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard() | ||||||
|  |  | ||||||
|  | @ -54,6 +54,8 @@ const ( | ||||||
| 	ProfileRestricted = "restricted" | 	ProfileRestricted = "restricted" | ||||||
| 	// ProfileNetadmin offers elevated privileges for network debugging.
 | 	// ProfileNetadmin offers elevated privileges for network debugging.
 | ||||||
| 	ProfileNetadmin = "netadmin" | 	ProfileNetadmin = "netadmin" | ||||||
|  | 	// ProfileSysadmin offers elevated privileges for debugging.
 | ||||||
|  | 	ProfileSysadmin = "sysadmin" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type ProfileApplier interface { | type ProfileApplier interface { | ||||||
|  | @ -74,6 +76,8 @@ func NewProfileApplier(profile string) (ProfileApplier, error) { | ||||||
| 		return &restrictedProfile{}, nil | 		return &restrictedProfile{}, nil | ||||||
| 	case ProfileNetadmin: | 	case ProfileNetadmin: | ||||||
| 		return &netadminProfile{}, nil | 		return &netadminProfile{}, nil | ||||||
|  | 	case ProfileSysadmin: | ||||||
|  | 		return &sysadminProfile{}, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil, fmt.Errorf("unknown profile: %s", profile) | 	return nil, fmt.Errorf("unknown profile: %s", profile) | ||||||
|  | @ -94,6 +98,9 @@ type restrictedProfile struct { | ||||||
| type netadminProfile struct { | type netadminProfile struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type sysadminProfile struct { | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (p *legacyProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | func (p *legacyProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||||
| 	switch target.(type) { | 	switch target.(type) { | ||||||
| 	case *corev1.Pod: | 	case *corev1.Pod: | ||||||
|  | @ -212,6 +219,29 @@ func (p *netadminProfile) Apply(pod *corev1.Pod, containerName string, target ru | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (p *sysadminProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||||
|  | 	style, err := getDebugStyle(pod, target) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("sysadmin profile: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	setPrivileged(pod, containerName) | ||||||
|  | 
 | ||||||
|  | 	switch style { | ||||||
|  | 	case node: | ||||||
|  | 		useHostNamespaces(pod) | ||||||
|  | 		mountRootPartition(pod, containerName) | ||||||
|  | 
 | ||||||
|  | 	case podCopy: | ||||||
|  | 		// to mimic general, default and baseline
 | ||||||
|  | 		shareProcessNamespace(pod) | ||||||
|  | 	case ephemeral: | ||||||
|  | 		// no additional modifications needed
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // removeLabelsAndProbes removes labels from the pod and remove probes
 | // removeLabelsAndProbes removes labels from the pod and remove probes
 | ||||||
| // from all containers of the pod.
 | // from all containers of the pod.
 | ||||||
| func removeLabelsAndProbes(p *corev1.Pod) { | func removeLabelsAndProbes(p *corev1.Pod) { | ||||||
|  | @ -271,6 +301,20 @@ func clearSecurityContext(p *corev1.Pod, containerName string) { | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // setPrivileged configures the containers as privileged.
 | ||||||
|  | func setPrivileged(p *corev1.Pod, containerName string) { | ||||||
|  | 	podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool { | ||||||
|  | 		if c.Name != containerName { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 		if c.SecurityContext == nil { | ||||||
|  | 			c.SecurityContext = &corev1.SecurityContext{} | ||||||
|  | 		} | ||||||
|  | 		c.SecurityContext.Privileged = pointer.Bool(true) | ||||||
|  | 		return false | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // disallowRoot configures the container to run as a non-root user.
 | // disallowRoot configures the container to run as a non-root user.
 | ||||||
| func disallowRoot(p *corev1.Pod, containerName string) { | func disallowRoot(p *corev1.Pod, containerName string) { | ||||||
| 	podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool { | 	podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool { | ||||||
|  |  | ||||||
|  | @ -678,3 +678,237 @@ func TestNetAdminProfile(t *testing.T) { | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 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 <nil> 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: pointer.Bool(true), | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "debug by pod copy", | ||||||
|  | 			pod: &corev1.Pod{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||||
|  | 				Spec: corev1.PodSpec{ | ||||||
|  | 					Containers: []corev1.Container{ | ||||||
|  | 						{Name: "app", Image: "appimage"}, | ||||||
|  | 						{Name: "dbg", Image: "dbgimage"}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			containerName: "dbg", | ||||||
|  | 			target: &corev1.Pod{ | ||||||
|  | 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||||
|  | 				Spec: corev1.PodSpec{ | ||||||
|  | 					Containers: []corev1.Container{ | ||||||
|  | 						{Name: "app", Image: "appimage"}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			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: pointer.Bool(true), | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 					ShareProcessNamespace: pointer.Bool(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"}, | ||||||
|  | 						{ | ||||||
|  | 							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"}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			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: pointer.Bool(true), | ||||||
|  | 								Capabilities: &corev1.Capabilities{ | ||||||
|  | 									Add: []corev1.Capability{"SYS_PTRACE"}, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 					ShareProcessNamespace: pointer.Bool(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: pointer.Bool(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: pointer.Bool(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) { | ||||||
|  | 			err := (&sysadminProfile{}).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) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue