admission_test.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. "context"
  16. "testing"
  17. corev1 "k8s.io/api/core/v1"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/runtime"
  20. "k8s.io/apiserver/pkg/admission"
  21. "k8s.io/client-go/kubernetes/fake"
  22. core "k8s.io/client-go/testing"
  23. api "k8s.io/kubernetes/pkg/apis/core"
  24. )
  25. // newAllowEscalatingExec returns `admission.Interface` that allows execution on
  26. // "hostIPC", "hostPID" and "privileged".
  27. func newAllowEscalatingExec() *DenyExec {
  28. return &DenyExec{
  29. Handler: admission.NewHandler(admission.Connect),
  30. hostIPC: false,
  31. hostPID: false,
  32. privileged: false,
  33. }
  34. }
  35. func TestAdmission(t *testing.T) {
  36. privPod := validPod("privileged")
  37. priv := true
  38. privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
  39. Privileged: &priv,
  40. }
  41. hostPIDPod := validPod("hostPID")
  42. hostPIDPod.Spec.HostPID = true
  43. hostIPCPod := validPod("hostIPC")
  44. hostIPCPod.Spec.HostIPC = true
  45. testCases := map[string]struct {
  46. pod *corev1.Pod
  47. shouldAccept bool
  48. }{
  49. "priv": {
  50. shouldAccept: false,
  51. pod: privPod,
  52. },
  53. "hostPID": {
  54. shouldAccept: false,
  55. pod: hostPIDPod,
  56. },
  57. "hostIPC": {
  58. shouldAccept: false,
  59. pod: hostIPCPod,
  60. },
  61. "non privileged": {
  62. shouldAccept: true,
  63. pod: validPod("nonPrivileged"),
  64. },
  65. }
  66. // Get the direct object though to allow testAdmission to inject the client
  67. handler := NewDenyEscalatingExec()
  68. for _, tc := range testCases {
  69. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  70. }
  71. // run with a permissive config and all cases should pass
  72. handler = newAllowEscalatingExec()
  73. for _, tc := range testCases {
  74. testAdmission(t, tc.pod, handler, true)
  75. }
  76. // run against an init container
  77. handler = NewDenyEscalatingExec()
  78. for _, tc := range testCases {
  79. tc.pod.Spec.InitContainers = tc.pod.Spec.Containers
  80. tc.pod.Spec.Containers = nil
  81. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  82. }
  83. // run with a permissive config and all cases should pass
  84. handler = newAllowEscalatingExec()
  85. for _, tc := range testCases {
  86. testAdmission(t, tc.pod, handler, true)
  87. }
  88. }
  89. func testAdmission(t *testing.T, pod *corev1.Pod, handler *DenyExec, shouldAccept bool) {
  90. mockClient := &fake.Clientset{}
  91. mockClient.AddReactor("get", "pods", func(action core.Action) (bool, runtime.Object, error) {
  92. if action.(core.GetAction).GetName() == pod.Name {
  93. return true, pod, nil
  94. }
  95. t.Errorf("Unexpected API call: %#v", action)
  96. return true, nil, nil
  97. })
  98. handler.SetExternalKubeClientSet(mockClient)
  99. admission.ValidateInitialization(handler)
  100. // pods/exec
  101. {
  102. err := handler.Validate(context.TODO(), admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "exec", admission.Connect, nil, false, nil), nil)
  103. if shouldAccept && err != nil {
  104. t.Errorf("Unexpected error returned from admission handler: %v", err)
  105. }
  106. if !shouldAccept && err == nil {
  107. t.Errorf("An error was expected from the admission handler. Received nil")
  108. }
  109. }
  110. // pods/attach
  111. {
  112. err := handler.Validate(context.TODO(), admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "attach", admission.Connect, nil, false, nil), nil)
  113. if shouldAccept && err != nil {
  114. t.Errorf("Unexpected error returned from admission handler: %v", err)
  115. }
  116. if !shouldAccept && err == nil {
  117. t.Errorf("An error was expected from the admission handler. Received nil")
  118. }
  119. }
  120. }
  121. // Test to ensure legacy admission controller works as expected.
  122. func TestDenyExecOnPrivileged(t *testing.T) {
  123. privPod := validPod("privileged")
  124. priv := true
  125. privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
  126. Privileged: &priv,
  127. }
  128. hostPIDPod := validPod("hostPID")
  129. hostPIDPod.Spec.HostPID = true
  130. hostIPCPod := validPod("hostIPC")
  131. hostIPCPod.Spec.HostIPC = true
  132. testCases := map[string]struct {
  133. pod *corev1.Pod
  134. shouldAccept bool
  135. }{
  136. "priv": {
  137. shouldAccept: false,
  138. pod: privPod,
  139. },
  140. "hostPID": {
  141. shouldAccept: true,
  142. pod: hostPIDPod,
  143. },
  144. "hostIPC": {
  145. shouldAccept: true,
  146. pod: hostIPCPod,
  147. },
  148. "non privileged": {
  149. shouldAccept: true,
  150. pod: validPod("nonPrivileged"),
  151. },
  152. }
  153. // Get the direct object though to allow testAdmission to inject the client
  154. handler := NewDenyExecOnPrivileged()
  155. for _, tc := range testCases {
  156. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  157. }
  158. // test init containers
  159. for _, tc := range testCases {
  160. tc.pod.Spec.InitContainers = tc.pod.Spec.Containers
  161. tc.pod.Spec.Containers = nil
  162. testAdmission(t, tc.pod, handler, tc.shouldAccept)
  163. }
  164. }
  165. func validPod(name string) *corev1.Pod {
  166. return &corev1.Pod{
  167. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"},
  168. Spec: corev1.PodSpec{
  169. Containers: []corev1.Container{
  170. {Name: "ctr1", Image: "image"},
  171. {Name: "ctr2", Image: "image2"},
  172. },
  173. },
  174. }
  175. }