admission_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package exec
  14. import (
  15. "testing"
  16. corev1 "k8s.io/api/core/v1"
  17. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  18. "k8s.io/apimachinery/pkg/runtime"
  19. "k8s.io/apiserver/pkg/admission"
  20. "k8s.io/client-go/kubernetes/fake"
  21. core "k8s.io/client-go/testing"
  22. api "k8s.io/kubernetes/pkg/apis/core"
  23. )
  24. // newAllowEscalatingExec returns `admission.Interface` that allows execution on
  25. // "hostIPC", "hostPID" and "privileged".
  26. func newAllowEscalatingExec() *DenyExec {
  27. return &DenyExec{
  28. Handler: admission.NewHandler(admission.Connect),
  29. hostIPC: false,
  30. hostPID: false,
  31. privileged: false,
  32. }
  33. }
  34. func TestAdmission(t *testing.T) {
  35. privPod := validPod("privileged")
  36. priv := true
  37. privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
  38. Privileged: &priv,
  39. }
  40. hostPIDPod := validPod("hostPID")
  41. hostPIDPod.Spec.HostPID = true
  42. hostIPCPod := validPod("hostIPC")
  43. hostIPCPod.Spec.HostIPC = true
  44. testCases := map[string]struct {
  45. pod *corev1.Pod
  46. shouldAccept bool
  47. }{
  48. "priv": {
  49. shouldAccept: false,
  50. pod: privPod,
  51. },
  52. "hostPID": {
  53. shouldAccept: false,
  54. pod: hostPIDPod,
  55. },
  56. "hostIPC": {
  57. shouldAccept: false,
  58. pod: hostIPCPod,
  59. },
  60. "non privileged": {
  61. shouldAccept: true,
  62. pod: validPod("nonPrivileged"),
  63. },
  64. }
  65. // Get the direct object though to allow testAdmission to inject the client
  66. handler := NewDenyEscalatingExec()
  67. for _, tc := range testCases {
  68. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  69. }
  70. // run with a permissive config and all cases should pass
  71. handler = newAllowEscalatingExec()
  72. for _, tc := range testCases {
  73. testAdmission(t, tc.pod, handler, true)
  74. }
  75. // run against an init container
  76. handler = NewDenyEscalatingExec()
  77. for _, tc := range testCases {
  78. tc.pod.Spec.InitContainers = tc.pod.Spec.Containers
  79. tc.pod.Spec.Containers = nil
  80. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  81. }
  82. // run with a permissive config and all cases should pass
  83. handler = newAllowEscalatingExec()
  84. for _, tc := range testCases {
  85. testAdmission(t, tc.pod, handler, true)
  86. }
  87. }
  88. func testAdmission(t *testing.T, pod *corev1.Pod, handler *DenyExec, shouldAccept bool) {
  89. mockClient := &fake.Clientset{}
  90. mockClient.AddReactor("get", "pods", func(action core.Action) (bool, runtime.Object, error) {
  91. if action.(core.GetAction).GetName() == pod.Name {
  92. return true, pod, nil
  93. }
  94. t.Errorf("Unexpected API call: %#v", action)
  95. return true, nil, nil
  96. })
  97. handler.SetExternalKubeClientSet(mockClient)
  98. admission.ValidateInitialization(handler)
  99. // pods/exec
  100. {
  101. err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "exec", admission.Connect, nil, false, nil), nil)
  102. if shouldAccept && err != nil {
  103. t.Errorf("Unexpected error returned from admission handler: %v", err)
  104. }
  105. if !shouldAccept && err == nil {
  106. t.Errorf("An error was expected from the admission handler. Received nil")
  107. }
  108. }
  109. // pods/attach
  110. {
  111. err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "attach", admission.Connect, nil, false, nil), nil)
  112. if shouldAccept && err != nil {
  113. t.Errorf("Unexpected error returned from admission handler: %v", err)
  114. }
  115. if !shouldAccept && err == nil {
  116. t.Errorf("An error was expected from the admission handler. Received nil")
  117. }
  118. }
  119. }
  120. // Test to ensure legacy admission controller works as expected.
  121. func TestDenyExecOnPrivileged(t *testing.T) {
  122. privPod := validPod("privileged")
  123. priv := true
  124. privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
  125. Privileged: &priv,
  126. }
  127. hostPIDPod := validPod("hostPID")
  128. hostPIDPod.Spec.HostPID = true
  129. hostIPCPod := validPod("hostIPC")
  130. hostIPCPod.Spec.HostIPC = true
  131. testCases := map[string]struct {
  132. pod *corev1.Pod
  133. shouldAccept bool
  134. }{
  135. "priv": {
  136. shouldAccept: false,
  137. pod: privPod,
  138. },
  139. "hostPID": {
  140. shouldAccept: true,
  141. pod: hostPIDPod,
  142. },
  143. "hostIPC": {
  144. shouldAccept: true,
  145. pod: hostIPCPod,
  146. },
  147. "non privileged": {
  148. shouldAccept: true,
  149. pod: validPod("nonPrivileged"),
  150. },
  151. }
  152. // Get the direct object though to allow testAdmission to inject the client
  153. handler := NewDenyExecOnPrivileged()
  154. for _, tc := range testCases {
  155. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  156. }
  157. // test init containers
  158. for _, tc := range testCases {
  159. tc.pod.Spec.InitContainers = tc.pod.Spec.Containers
  160. tc.pod.Spec.Containers = nil
  161. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  162. }
  163. }
  164. func validPod(name string) *corev1.Pod {
  165. return &corev1.Pod{
  166. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"},
  167. Spec: corev1.PodSpec{
  168. Containers: []corev1.Container{
  169. {Name: "ctr1", Image: "image"},
  170. {Name: "ctr2", Image: "image2"},
  171. },
  172. },
  173. }
  174. }