mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-30 17:58:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			220 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2015 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 exec
 | |
| 
 | |
| import (
 | |
| 	"testing"
 | |
| 
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/apimachinery/pkg/runtime"
 | |
| 	"k8s.io/apiserver/pkg/admission"
 | |
| 	"k8s.io/kubernetes/pkg/api"
 | |
| 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
 | |
| 	"k8s.io/kubernetes/pkg/client/testing/core"
 | |
| 	"k8s.io/kubernetes/pkg/genericapiserver/api/rest"
 | |
| )
 | |
| 
 | |
| func TestAdmission(t *testing.T) {
 | |
| 	privPod := validPod("privileged")
 | |
| 	priv := true
 | |
| 	privPod.Spec.Containers[0].SecurityContext = &api.SecurityContext{
 | |
| 		Privileged: &priv,
 | |
| 	}
 | |
| 
 | |
| 	hostPIDPod := validPod("hostPID")
 | |
| 	hostPIDPod.Spec.SecurityContext = &api.PodSecurityContext{}
 | |
| 	hostPIDPod.Spec.SecurityContext.HostPID = true
 | |
| 
 | |
| 	hostIPCPod := validPod("hostIPC")
 | |
| 	hostIPCPod.Spec.SecurityContext = &api.PodSecurityContext{}
 | |
| 	hostIPCPod.Spec.SecurityContext.HostIPC = true
 | |
| 
 | |
| 	testCases := map[string]struct {
 | |
| 		pod          *api.Pod
 | |
| 		shouldAccept bool
 | |
| 	}{
 | |
| 		"priv": {
 | |
| 			shouldAccept: false,
 | |
| 			pod:          privPod,
 | |
| 		},
 | |
| 		"hostPID": {
 | |
| 			shouldAccept: false,
 | |
| 			pod:          hostPIDPod,
 | |
| 		},
 | |
| 		"hostIPC": {
 | |
| 			shouldAccept: false,
 | |
| 			pod:          hostIPCPod,
 | |
| 		},
 | |
| 		"non privileged": {
 | |
| 			shouldAccept: true,
 | |
| 			pod:          validPod("nonPrivileged"),
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// use the same code as NewDenyEscalatingExec, using the direct object though to allow testAdmission to
 | |
| 	// inject the client
 | |
| 	handler := &denyExec{
 | |
| 		Handler:    admission.NewHandler(admission.Connect),
 | |
| 		hostIPC:    true,
 | |
| 		hostPID:    true,
 | |
| 		privileged: true,
 | |
| 	}
 | |
| 
 | |
| 	for _, tc := range testCases {
 | |
| 		testAdmission(t, tc.pod, handler, tc.shouldAccept)
 | |
| 	}
 | |
| 
 | |
| 	// run with a permissive config and all cases should pass
 | |
| 	handler.privileged = false
 | |
| 	handler.hostPID = false
 | |
| 	handler.hostIPC = false
 | |
| 
 | |
| 	for _, tc := range testCases {
 | |
| 		testAdmission(t, tc.pod, handler, true)
 | |
| 	}
 | |
| 
 | |
| 	// run against an init container
 | |
| 	handler = &denyExec{
 | |
| 		Handler:    admission.NewHandler(admission.Connect),
 | |
| 		hostIPC:    true,
 | |
| 		hostPID:    true,
 | |
| 		privileged: true,
 | |
| 	}
 | |
| 
 | |
| 	for _, tc := range testCases {
 | |
| 		tc.pod.Spec.InitContainers = tc.pod.Spec.Containers
 | |
| 		tc.pod.Spec.Containers = nil
 | |
| 		testAdmission(t, tc.pod, handler, tc.shouldAccept)
 | |
| 	}
 | |
| 
 | |
| 	// run with a permissive config and all cases should pass
 | |
| 	handler.privileged = false
 | |
| 	handler.hostPID = false
 | |
| 	handler.hostIPC = false
 | |
| 
 | |
| 	for _, tc := range testCases {
 | |
| 		testAdmission(t, tc.pod, handler, true)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func testAdmission(t *testing.T, pod *api.Pod, handler *denyExec, shouldAccept bool) {
 | |
| 	mockClient := &fake.Clientset{}
 | |
| 	mockClient.AddReactor("get", "pods", func(action core.Action) (bool, runtime.Object, error) {
 | |
| 		if action.(core.GetAction).GetName() == pod.Name {
 | |
| 			return true, pod, nil
 | |
| 		}
 | |
| 		t.Errorf("Unexpected API call: %#v", action)
 | |
| 		return true, nil, nil
 | |
| 	})
 | |
| 
 | |
| 	handler.client = mockClient
 | |
| 
 | |
| 	// pods/exec
 | |
| 	{
 | |
| 		req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/exec"}
 | |
| 		err := handler.Admit(admission.NewAttributesRecord(req, nil, api.Kind("Pod").WithVersion("version"), "test", "name", api.Resource("pods").WithVersion("version"), "exec", admission.Connect, nil))
 | |
| 		if shouldAccept && err != nil {
 | |
| 			t.Errorf("Unexpected error returned from admission handler: %v", err)
 | |
| 		}
 | |
| 		if !shouldAccept && err == nil {
 | |
| 			t.Errorf("An error was expected from the admission handler. Received nil")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// pods/attach
 | |
| 	{
 | |
| 		req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/attach"}
 | |
| 		err := handler.Admit(admission.NewAttributesRecord(req, nil, api.Kind("Pod").WithVersion("version"), "test", "name", api.Resource("pods").WithVersion("version"), "attach", admission.Connect, nil))
 | |
| 		if shouldAccept && err != nil {
 | |
| 			t.Errorf("Unexpected error returned from admission handler: %v", err)
 | |
| 		}
 | |
| 		if !shouldAccept && err == nil {
 | |
| 			t.Errorf("An error was expected from the admission handler. Received nil")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test to ensure legacy admission controller works as expected.
 | |
| func TestDenyExecOnPrivileged(t *testing.T) {
 | |
| 	privPod := validPod("privileged")
 | |
| 	priv := true
 | |
| 	privPod.Spec.Containers[0].SecurityContext = &api.SecurityContext{
 | |
| 		Privileged: &priv,
 | |
| 	}
 | |
| 
 | |
| 	hostPIDPod := validPod("hostPID")
 | |
| 	hostPIDPod.Spec.SecurityContext = &api.PodSecurityContext{}
 | |
| 	hostPIDPod.Spec.SecurityContext.HostPID = true
 | |
| 
 | |
| 	hostIPCPod := validPod("hostIPC")
 | |
| 	hostIPCPod.Spec.SecurityContext = &api.PodSecurityContext{}
 | |
| 	hostIPCPod.Spec.SecurityContext.HostIPC = true
 | |
| 
 | |
| 	testCases := map[string]struct {
 | |
| 		pod          *api.Pod
 | |
| 		shouldAccept bool
 | |
| 	}{
 | |
| 		"priv": {
 | |
| 			shouldAccept: false,
 | |
| 			pod:          privPod,
 | |
| 		},
 | |
| 		"hostPID": {
 | |
| 			shouldAccept: true,
 | |
| 			pod:          hostPIDPod,
 | |
| 		},
 | |
| 		"hostIPC": {
 | |
| 			shouldAccept: true,
 | |
| 			pod:          hostIPCPod,
 | |
| 		},
 | |
| 		"non privileged": {
 | |
| 			shouldAccept: true,
 | |
| 			pod:          validPod("nonPrivileged"),
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// use the same code as NewDenyExecOnPrivileged, using the direct object though to allow testAdmission to
 | |
| 	// inject the client
 | |
| 	handler := &denyExec{
 | |
| 		Handler:    admission.NewHandler(admission.Connect),
 | |
| 		hostIPC:    false,
 | |
| 		hostPID:    false,
 | |
| 		privileged: true,
 | |
| 	}
 | |
| 	for _, tc := range testCases {
 | |
| 		testAdmission(t, tc.pod, handler, tc.shouldAccept)
 | |
| 	}
 | |
| 
 | |
| 	// test init containers
 | |
| 	for _, tc := range testCases {
 | |
| 		tc.pod.Spec.InitContainers = tc.pod.Spec.Containers
 | |
| 		tc.pod.Spec.Containers = nil
 | |
| 		testAdmission(t, tc.pod, handler, tc.shouldAccept)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func validPod(name string) *api.Pod {
 | |
| 	return &api.Pod{
 | |
| 		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"},
 | |
| 		Spec: api.PodSpec{
 | |
| 			Containers: []api.Container{
 | |
| 				{Name: "ctr1", Image: "image"},
 | |
| 				{Name: "ctr2", Image: "image2"},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| }
 | 
