...

Source file src/k8s.io/kubernetes/pkg/controller/certificates/approver/sarapprove_test.go

Documentation: k8s.io/kubernetes/pkg/controller/certificates/approver

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

View as plain text