123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package approver
- import (
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/x509"
- "crypto/x509/pkix"
- "encoding/pem"
- "fmt"
- "math/rand"
- "net"
- "testing"
- authorization "k8s.io/api/authorization/v1"
- capi "k8s.io/api/certificates/v1beta1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/client-go/kubernetes/fake"
- testclient "k8s.io/client-go/testing"
- k8s_certificates_v1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
- )
- func TestHasKubeletUsages(t *testing.T) {
- cases := []struct {
- usages []capi.KeyUsage
- expected bool
- }{
- {
- usages: nil,
- expected: false,
- },
- {
- usages: []capi.KeyUsage{},
- expected: false,
- },
- {
- usages: []capi.KeyUsage{
- capi.UsageKeyEncipherment,
- capi.UsageDigitalSignature,
- },
- expected: false,
- },
- {
- usages: []capi.KeyUsage{
- capi.UsageKeyEncipherment,
- capi.UsageDigitalSignature,
- capi.UsageServerAuth,
- },
- expected: false,
- },
- {
- usages: []capi.KeyUsage{
- capi.UsageKeyEncipherment,
- capi.UsageDigitalSignature,
- capi.UsageClientAuth,
- },
- expected: true,
- },
- }
- for _, c := range cases {
- if hasExactUsages(&capi.CertificateSigningRequest{
- Spec: capi.CertificateSigningRequestSpec{
- Usages: c.usages,
- },
- }, kubeletClientUsages) != c.expected {
- t.Errorf("unexpected result of hasKubeletUsages(%v), expecting: %v", c.usages, c.expected)
- }
- }
- }
- func TestHandle(t *testing.T) {
- cases := []struct {
- allowed bool
- recognized bool
- err bool
- verify func(*testing.T, []testclient.Action)
- }{
- {
- recognized: false,
- allowed: false,
- verify: func(t *testing.T, as []testclient.Action) {
- if len(as) != 0 {
- t.Errorf("expected no client calls but got: %#v", as)
- }
- },
- },
- {
- recognized: false,
- allowed: true,
- verify: func(t *testing.T, as []testclient.Action) {
- if len(as) != 0 {
- t.Errorf("expected no client calls but got: %#v", as)
- }
- },
- },
- {
- recognized: true,
- allowed: false,
- verify: func(t *testing.T, as []testclient.Action) {
- if len(as) != 1 {
- t.Errorf("expected 1 call but got: %#v", as)
- return
- }
- _ = as[0].(testclient.CreateActionImpl)
- },
- err: true,
- },
- {
- recognized: true,
- allowed: true,
- verify: func(t *testing.T, as []testclient.Action) {
- if len(as) != 2 {
- t.Errorf("expected two calls but got: %#v", as)
- return
- }
- _ = as[0].(testclient.CreateActionImpl)
- a := as[1].(testclient.UpdateActionImpl)
- if got, expected := a.Verb, "update"; got != expected {
- t.Errorf("got: %v, expected: %v", got, expected)
- }
- if got, expected := a.Resource, (schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"}); got != expected {
- t.Errorf("got: %v, expected: %v", got, expected)
- }
- if got, expected := a.Subresource, "approval"; got != expected {
- t.Errorf("got: %v, expected: %v", got, expected)
- }
- csr := a.Object.(*capi.CertificateSigningRequest)
- if len(csr.Status.Conditions) != 1 {
- t.Errorf("expected CSR to have approved condition: %#v", csr)
- }
- c := csr.Status.Conditions[0]
- if got, expected := c.Type, capi.CertificateApproved; got != expected {
- t.Errorf("got: %v, expected: %v", got, expected)
- }
- if got, expected := c.Reason, "AutoApproved"; got != expected {
- t.Errorf("got: %v, expected: %v", got, expected)
- }
- },
- },
- }
- for _, c := range cases {
- t.Run(fmt.Sprintf("recognized:%v,allowed: %v,err: %v", c.recognized, c.allowed, c.err), func(t *testing.T) {
- client := &fake.Clientset{}
- client.AddReactor("create", "subjectaccessreviews", func(action testclient.Action) (handled bool, ret runtime.Object, err error) {
- return true, &authorization.SubjectAccessReview{
- Status: authorization.SubjectAccessReviewStatus{
- Allowed: c.allowed,
- },
- }, nil
- })
- approver := sarApprover{
- client: client,
- recognizers: []csrRecognizer{
- {
- successMessage: "tester",
- permission: authorization.ResourceAttributes{Group: "foo", Resource: "bar", Subresource: "baz"},
- recognize: func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
- return c.recognized
- },
- },
- },
- }
- csr := makeTestCsr()
- if err := approver.handle(csr); err != nil && !c.err {
- t.Errorf("unexpected err: %v", err)
- }
- c.verify(t, client.Actions())
- })
- }
- }
- func TestRecognizers(t *testing.T) {
- goodCases := []func(b *csrBuilder){
- func(b *csrBuilder) {
- },
- }
- testRecognizer(t, goodCases, isNodeClientCert, true)
- testRecognizer(t, goodCases, isSelfNodeClientCert, true)
- badCases := []func(b *csrBuilder){
- func(b *csrBuilder) {
- b.cn = "mike"
- },
- func(b *csrBuilder) {
- b.orgs = nil
- },
- func(b *csrBuilder) {
- b.orgs = []string{"system:master"}
- },
- func(b *csrBuilder) {
- b.usages = append(b.usages, capi.UsageServerAuth)
- },
- }
- testRecognizer(t, badCases, isNodeClientCert, false)
- testRecognizer(t, badCases, isSelfNodeClientCert, false)
- // cn different then requestor
- differentCN := []func(b *csrBuilder){
- func(b *csrBuilder) {
- b.requestor = "joe"
- },
- func(b *csrBuilder) {
- b.cn = "system:node:bar"
- },
- }
- testRecognizer(t, differentCN, isNodeClientCert, true)
- testRecognizer(t, differentCN, isSelfNodeClientCert, false)
- }
- func testRecognizer(t *testing.T, cases []func(b *csrBuilder), recognizeFunc func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool, shouldRecognize bool) {
- for _, c := range cases {
- b := csrBuilder{
- cn: "system:node:foo",
- orgs: []string{"system:nodes"},
- requestor: "system:node:foo",
- usages: []capi.KeyUsage{
- capi.UsageKeyEncipherment,
- capi.UsageDigitalSignature,
- capi.UsageClientAuth,
- },
- }
- c(&b)
- t.Run(fmt.Sprintf("csr:%#v", b), func(t *testing.T) {
- csr := makeFancyTestCsr(b)
- x509cr, err := k8s_certificates_v1beta1.ParseCSR(csr)
- if err != nil {
- t.Errorf("unexpected err: %v", err)
- }
- if recognizeFunc(csr, x509cr) != shouldRecognize {
- t.Errorf("expected recognized to be %v", shouldRecognize)
- }
- })
- }
- }
- // noncryptographic for faster testing
- // DO NOT COPY THIS CODE
- var insecureRand = rand.New(rand.NewSource(0))
- func makeTestCsr() *capi.CertificateSigningRequest {
- return makeFancyTestCsr(csrBuilder{cn: "test-cert"})
- }
- type csrBuilder struct {
- cn string
- orgs []string
- requestor string
- usages []capi.KeyUsage
- dns []string
- emails []string
- ips []net.IP
- }
- func makeFancyTestCsr(b csrBuilder) *capi.CertificateSigningRequest {
- pk, err := ecdsa.GenerateKey(elliptic.P256(), insecureRand)
- if err != nil {
- panic(err)
- }
- csrb, err := x509.CreateCertificateRequest(insecureRand, &x509.CertificateRequest{
- Subject: pkix.Name{
- CommonName: b.cn,
- Organization: b.orgs,
- },
- DNSNames: b.dns,
- EmailAddresses: b.emails,
- IPAddresses: b.ips,
- }, pk)
- if err != nil {
- panic(err)
- }
- return &capi.CertificateSigningRequest{
- Spec: capi.CertificateSigningRequestSpec{
- Username: b.requestor,
- Usages: b.usages,
- Request: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrb}),
- },
- }
- }
|