sarapprove_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. Copyright 2017 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 approver
  14. import (
  15. "crypto/ecdsa"
  16. "crypto/elliptic"
  17. "crypto/x509"
  18. "crypto/x509/pkix"
  19. "encoding/pem"
  20. "fmt"
  21. "math/rand"
  22. "net"
  23. "testing"
  24. authorization "k8s.io/api/authorization/v1"
  25. capi "k8s.io/api/certificates/v1beta1"
  26. "k8s.io/apimachinery/pkg/runtime"
  27. "k8s.io/apimachinery/pkg/runtime/schema"
  28. "k8s.io/client-go/kubernetes/fake"
  29. testclient "k8s.io/client-go/testing"
  30. k8s_certificates_v1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
  31. )
  32. func TestHasKubeletUsages(t *testing.T) {
  33. cases := []struct {
  34. usages []capi.KeyUsage
  35. expected bool
  36. }{
  37. {
  38. usages: nil,
  39. expected: false,
  40. },
  41. {
  42. usages: []capi.KeyUsage{},
  43. expected: false,
  44. },
  45. {
  46. usages: []capi.KeyUsage{
  47. capi.UsageKeyEncipherment,
  48. capi.UsageDigitalSignature,
  49. },
  50. expected: false,
  51. },
  52. {
  53. usages: []capi.KeyUsage{
  54. capi.UsageKeyEncipherment,
  55. capi.UsageDigitalSignature,
  56. capi.UsageServerAuth,
  57. },
  58. expected: false,
  59. },
  60. {
  61. usages: []capi.KeyUsage{
  62. capi.UsageKeyEncipherment,
  63. capi.UsageDigitalSignature,
  64. capi.UsageClientAuth,
  65. },
  66. expected: true,
  67. },
  68. }
  69. for _, c := range cases {
  70. if hasExactUsages(&capi.CertificateSigningRequest{
  71. Spec: capi.CertificateSigningRequestSpec{
  72. Usages: c.usages,
  73. },
  74. }, kubeletClientUsages) != c.expected {
  75. t.Errorf("unexpected result of hasKubeletUsages(%v), expecting: %v", c.usages, c.expected)
  76. }
  77. }
  78. }
  79. func TestHandle(t *testing.T) {
  80. cases := []struct {
  81. allowed bool
  82. recognized bool
  83. err bool
  84. verify func(*testing.T, []testclient.Action)
  85. }{
  86. {
  87. recognized: false,
  88. allowed: false,
  89. verify: func(t *testing.T, as []testclient.Action) {
  90. if len(as) != 0 {
  91. t.Errorf("expected no client calls but got: %#v", as)
  92. }
  93. },
  94. },
  95. {
  96. recognized: false,
  97. allowed: true,
  98. verify: func(t *testing.T, as []testclient.Action) {
  99. if len(as) != 0 {
  100. t.Errorf("expected no client calls but got: %#v", as)
  101. }
  102. },
  103. },
  104. {
  105. recognized: true,
  106. allowed: false,
  107. verify: func(t *testing.T, as []testclient.Action) {
  108. if len(as) != 1 {
  109. t.Errorf("expected 1 call but got: %#v", as)
  110. return
  111. }
  112. _ = as[0].(testclient.CreateActionImpl)
  113. },
  114. err: true,
  115. },
  116. {
  117. recognized: true,
  118. allowed: true,
  119. verify: func(t *testing.T, as []testclient.Action) {
  120. if len(as) != 2 {
  121. t.Errorf("expected two calls but got: %#v", as)
  122. return
  123. }
  124. _ = as[0].(testclient.CreateActionImpl)
  125. a := as[1].(testclient.UpdateActionImpl)
  126. if got, expected := a.Verb, "update"; got != expected {
  127. t.Errorf("got: %v, expected: %v", got, expected)
  128. }
  129. if got, expected := a.Resource, (schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"}); got != expected {
  130. t.Errorf("got: %v, expected: %v", got, expected)
  131. }
  132. if got, expected := a.Subresource, "approval"; got != expected {
  133. t.Errorf("got: %v, expected: %v", got, expected)
  134. }
  135. csr := a.Object.(*capi.CertificateSigningRequest)
  136. if len(csr.Status.Conditions) != 1 {
  137. t.Errorf("expected CSR to have approved condition: %#v", csr)
  138. }
  139. c := csr.Status.Conditions[0]
  140. if got, expected := c.Type, capi.CertificateApproved; got != expected {
  141. t.Errorf("got: %v, expected: %v", got, expected)
  142. }
  143. if got, expected := c.Reason, "AutoApproved"; got != expected {
  144. t.Errorf("got: %v, expected: %v", got, expected)
  145. }
  146. },
  147. },
  148. }
  149. for _, c := range cases {
  150. t.Run(fmt.Sprintf("recognized:%v,allowed: %v,err: %v", c.recognized, c.allowed, c.err), func(t *testing.T) {
  151. client := &fake.Clientset{}
  152. client.AddReactor("create", "subjectaccessreviews", func(action testclient.Action) (handled bool, ret runtime.Object, err error) {
  153. return true, &authorization.SubjectAccessReview{
  154. Status: authorization.SubjectAccessReviewStatus{
  155. Allowed: c.allowed,
  156. },
  157. }, nil
  158. })
  159. approver := sarApprover{
  160. client: client,
  161. recognizers: []csrRecognizer{
  162. {
  163. successMessage: "tester",
  164. permission: authorization.ResourceAttributes{Group: "foo", Resource: "bar", Subresource: "baz"},
  165. recognize: func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
  166. return c.recognized
  167. },
  168. },
  169. },
  170. }
  171. csr := makeTestCsr()
  172. if err := approver.handle(csr); err != nil && !c.err {
  173. t.Errorf("unexpected err: %v", err)
  174. }
  175. c.verify(t, client.Actions())
  176. })
  177. }
  178. }
  179. func TestRecognizers(t *testing.T) {
  180. goodCases := []func(b *csrBuilder){
  181. func(b *csrBuilder) {
  182. },
  183. }
  184. testRecognizer(t, goodCases, isNodeClientCert, true)
  185. testRecognizer(t, goodCases, isSelfNodeClientCert, true)
  186. badCases := []func(b *csrBuilder){
  187. func(b *csrBuilder) {
  188. b.cn = "mike"
  189. },
  190. func(b *csrBuilder) {
  191. b.orgs = nil
  192. },
  193. func(b *csrBuilder) {
  194. b.orgs = []string{"system:master"}
  195. },
  196. func(b *csrBuilder) {
  197. b.usages = append(b.usages, capi.UsageServerAuth)
  198. },
  199. }
  200. testRecognizer(t, badCases, isNodeClientCert, false)
  201. testRecognizer(t, badCases, isSelfNodeClientCert, false)
  202. // cn different then requestor
  203. differentCN := []func(b *csrBuilder){
  204. func(b *csrBuilder) {
  205. b.requestor = "joe"
  206. },
  207. func(b *csrBuilder) {
  208. b.cn = "system:node:bar"
  209. },
  210. }
  211. testRecognizer(t, differentCN, isNodeClientCert, true)
  212. testRecognizer(t, differentCN, isSelfNodeClientCert, false)
  213. }
  214. func testRecognizer(t *testing.T, cases []func(b *csrBuilder), recognizeFunc func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool, shouldRecognize bool) {
  215. for _, c := range cases {
  216. b := csrBuilder{
  217. cn: "system:node:foo",
  218. orgs: []string{"system:nodes"},
  219. requestor: "system:node:foo",
  220. usages: []capi.KeyUsage{
  221. capi.UsageKeyEncipherment,
  222. capi.UsageDigitalSignature,
  223. capi.UsageClientAuth,
  224. },
  225. }
  226. c(&b)
  227. t.Run(fmt.Sprintf("csr:%#v", b), func(t *testing.T) {
  228. csr := makeFancyTestCsr(b)
  229. x509cr, err := k8s_certificates_v1beta1.ParseCSR(csr)
  230. if err != nil {
  231. t.Errorf("unexpected err: %v", err)
  232. }
  233. if recognizeFunc(csr, x509cr) != shouldRecognize {
  234. t.Errorf("expected recognized to be %v", shouldRecognize)
  235. }
  236. })
  237. }
  238. }
  239. // noncryptographic for faster testing
  240. // DO NOT COPY THIS CODE
  241. var insecureRand = rand.New(rand.NewSource(0))
  242. func makeTestCsr() *capi.CertificateSigningRequest {
  243. return makeFancyTestCsr(csrBuilder{cn: "test-cert"})
  244. }
  245. type csrBuilder struct {
  246. cn string
  247. orgs []string
  248. requestor string
  249. usages []capi.KeyUsage
  250. dns []string
  251. emails []string
  252. ips []net.IP
  253. }
  254. func makeFancyTestCsr(b csrBuilder) *capi.CertificateSigningRequest {
  255. pk, err := ecdsa.GenerateKey(elliptic.P256(), insecureRand)
  256. if err != nil {
  257. panic(err)
  258. }
  259. csrb, err := x509.CreateCertificateRequest(insecureRand, &x509.CertificateRequest{
  260. Subject: pkix.Name{
  261. CommonName: b.cn,
  262. Organization: b.orgs,
  263. },
  264. DNSNames: b.dns,
  265. EmailAddresses: b.emails,
  266. IPAddresses: b.ips,
  267. }, pk)
  268. if err != nil {
  269. panic(err)
  270. }
  271. return &capi.CertificateSigningRequest{
  272. Spec: capi.CertificateSigningRequestSpec{
  273. Username: b.requestor,
  274. Usages: b.usages,
  275. Request: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrb}),
  276. },
  277. }
  278. }