From b622acf8ecb6f3c5a22b79408e67d2a352be6dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mudrini=C4=87?= Date: Fri, 24 Aug 2018 16:47:21 +0200 Subject: [PATCH] admission/exec: externalize exec admission controller --- plugin/pkg/admission/exec/BUILD | 9 ++- plugin/pkg/admission/exec/admission.go | 84 ++++++++++----------- plugin/pkg/admission/exec/admission_test.go | 35 ++++----- 3 files changed, 61 insertions(+), 67 deletions(-) diff --git a/plugin/pkg/admission/exec/BUILD b/plugin/pkg/admission/exec/BUILD index aed101de94..4d571efc75 100644 --- a/plugin/pkg/admission/exec/BUILD +++ b/plugin/pkg/admission/exec/BUILD @@ -11,11 +11,11 @@ go_library( srcs = ["admission.go"], importpath = "k8s.io/kubernetes/plugin/pkg/admission/exec", deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/client/clientset_generated/internalclientset:go_default_library", - "//pkg/kubeapiserver/admission:go_default_library", + "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", + "//staging/src/k8s.io/client-go/kubernetes:go_default_library", ], ) @@ -25,10 +25,11 @@ go_test( embed = [":go_default_library"], deps = [ "//pkg/apis/core:go_default_library", - "//pkg/client/clientset_generated/internalclientset/fake:go_default_library", + "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/testing:go_default_library", ], ) diff --git a/plugin/pkg/admission/exec/admission.go b/plugin/pkg/admission/exec/admission.go index 00e3b6cfbc..b6cbf94bc3 100644 --- a/plugin/pkg/admission/exec/admission.go +++ b/plugin/pkg/admission/exec/admission.go @@ -20,11 +20,11 @@ import ( "fmt" "io" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" + genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" + "k8s.io/client-go/kubernetes" ) const ( @@ -52,7 +52,7 @@ func Register(plugins *admission.Plugins) { // a pod using host based configurations. type DenyExec struct { *admission.Handler - client internalclientset.Interface + client kubernetes.Interface // these flags control which items will be checked to deny exec/attach hostNetwork bool @@ -62,20 +62,7 @@ type DenyExec struct { } var _ admission.ValidationInterface = &DenyExec{} - -var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&DenyExec{}) - -// NewDenyEscalatingExec creates a new admission controller that denies an exec operation on a pod -// using host based configurations. -func NewDenyEscalatingExec() *DenyExec { - return &DenyExec{ - Handler: admission.NewHandler(admission.Connect), - hostNetwork: true, - hostIPC: true, - hostPID: true, - privileged: true, - } -} +var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&DenyExec{}) // NewDenyExecOnPrivileged creates a new admission controller that is only checking the privileged // option. This is for legacy support of the DenyExecOnPrivileged admission controller. @@ -90,6 +77,31 @@ func NewDenyExecOnPrivileged() *DenyExec { } } +// NewDenyEscalatingExec creates a new admission controller that denies an exec operation on a pod +// using host based configurations. +func NewDenyEscalatingExec() *DenyExec { + return &DenyExec{ + Handler: admission.NewHandler(admission.Connect), + hostNetwork: true, + hostIPC: true, + hostPID: true, + privileged: true, + } +} + +// SetExternalKubeClientSet implements the WantsInternalKubeClientSet interface. +func (d *DenyExec) SetExternalKubeClientSet(client kubernetes.Interface) { + d.client = client +} + +// ValidateInitialization implements the InitializationValidator interface. +func (d *DenyExec) ValidateInitialization() error { + if d.client == nil { + return fmt.Errorf("missing client") + } + return nil +} + // Validate makes an admission decision based on the request attributes func (d *DenyExec) Validate(a admission.Attributes) (err error) { path := a.GetResource().Resource @@ -100,24 +112,21 @@ func (d *DenyExec) Validate(a admission.Attributes) (err error) { if path != "pods/exec" && path != "pods/attach" { return nil } - pod, err := d.client.Core().Pods(a.GetNamespace()).Get(a.GetName(), metav1.GetOptions{}) + pod, err := d.client.CoreV1().Pods(a.GetNamespace()).Get(a.GetName(), metav1.GetOptions{}) if err != nil { return admission.NewForbidden(a, err) } - if pod.Spec.SecurityContext != nil { - securityContext := pod.Spec.SecurityContext - if d.hostNetwork && securityContext.HostNetwork { - return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host network")) - } + if d.hostNetwork && pod.Spec.HostNetwork { + return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host network")) + } - if d.hostPID && securityContext.HostPID { - return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host pid")) - } + if d.hostPID && pod.Spec.HostPID { + return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host pid")) + } - if d.hostIPC && securityContext.HostIPC { - return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host ipc")) - } + if d.hostIPC && pod.Spec.HostIPC { + return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host ipc")) } if d.privileged && isPrivileged(pod) { @@ -128,7 +137,7 @@ func (d *DenyExec) Validate(a admission.Attributes) (err error) { } // isPrivileged will return true a pod has any privileged containers -func isPrivileged(pod *api.Pod) bool { +func isPrivileged(pod *corev1.Pod) bool { for _, c := range pod.Spec.InitContainers { if c.SecurityContext == nil || c.SecurityContext.Privileged == nil { continue @@ -147,16 +156,3 @@ func isPrivileged(pod *api.Pod) bool { } return false } - -// SetInternalKubeClientSet implements the WantsInternalKubeClientSet interface. -func (d *DenyExec) SetInternalKubeClientSet(client internalclientset.Interface) { - d.client = client -} - -// ValidateInitialization implements the InitializationValidator interface. -func (d *DenyExec) ValidateInitialization() error { - if d.client == nil { - return fmt.Errorf("missing client") - } - return nil -} diff --git a/plugin/pkg/admission/exec/admission_test.go b/plugin/pkg/admission/exec/admission_test.go index 89370d1c57..c8d0b665c1 100644 --- a/plugin/pkg/admission/exec/admission_test.go +++ b/plugin/pkg/admission/exec/admission_test.go @@ -19,12 +19,13 @@ package exec import ( "testing" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/admission" + "k8s.io/client-go/kubernetes/fake" core "k8s.io/client-go/testing" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" ) // newAllowEscalatingExec returns `admission.Interface` that allows execution on @@ -41,20 +42,18 @@ func newAllowEscalatingExec() *DenyExec { func TestAdmission(t *testing.T) { privPod := validPod("privileged") priv := true - privPod.Spec.Containers[0].SecurityContext = &api.SecurityContext{ + privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{ Privileged: &priv, } hostPIDPod := validPod("hostPID") - hostPIDPod.Spec.SecurityContext = &api.PodSecurityContext{} - hostPIDPod.Spec.SecurityContext.HostPID = true + hostPIDPod.Spec.HostPID = true hostIPCPod := validPod("hostIPC") - hostIPCPod.Spec.SecurityContext = &api.PodSecurityContext{} - hostIPCPod.Spec.SecurityContext.HostIPC = true + hostIPCPod.Spec.HostIPC = true testCases := map[string]struct { - pod *api.Pod + pod *corev1.Pod shouldAccept bool }{ "priv": { @@ -106,7 +105,7 @@ func TestAdmission(t *testing.T) { } } -func testAdmission(t *testing.T, pod *api.Pod, handler *DenyExec, shouldAccept bool) { +func testAdmission(t *testing.T, pod *corev1.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 { @@ -116,7 +115,7 @@ func testAdmission(t *testing.T, pod *api.Pod, handler *DenyExec, shouldAccept b return true, nil, nil }) - handler.SetInternalKubeClientSet(mockClient) + handler.SetExternalKubeClientSet(mockClient) admission.ValidateInitialization(handler) // pods/exec @@ -146,20 +145,18 @@ func testAdmission(t *testing.T, pod *api.Pod, handler *DenyExec, shouldAccept b func TestDenyExecOnPrivileged(t *testing.T) { privPod := validPod("privileged") priv := true - privPod.Spec.Containers[0].SecurityContext = &api.SecurityContext{ + privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{ Privileged: &priv, } hostPIDPod := validPod("hostPID") - hostPIDPod.Spec.SecurityContext = &api.PodSecurityContext{} - hostPIDPod.Spec.SecurityContext.HostPID = true + hostPIDPod.Spec.HostPID = true hostIPCPod := validPod("hostIPC") - hostIPCPod.Spec.SecurityContext = &api.PodSecurityContext{} - hostIPCPod.Spec.SecurityContext.HostIPC = true + hostIPCPod.Spec.HostIPC = true testCases := map[string]struct { - pod *api.Pod + pod *corev1.Pod shouldAccept bool }{ "priv": { @@ -195,11 +192,11 @@ func TestDenyExecOnPrivileged(t *testing.T) { } } -func validPod(name string) *api.Pod { - return &api.Pod{ +func validPod(name string) *corev1.Pod { + return &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"}, - Spec: api.PodSpec{ - Containers: []api.Container{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ {Name: "ctr1", Image: "image"}, {Name: "ctr2", Image: "image2"}, },