k8s.io/kubernetes@v1.29.3/test/integration/certificates/controller_approval_test.go (about) 1 /* 2 Copyright 2020 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 certificates 18 19 import ( 20 "context" 21 "crypto/x509" 22 "crypto/x509/pkix" 23 "fmt" 24 "testing" 25 "time" 26 27 certv1 "k8s.io/api/certificates/v1" 28 rbacv1 "k8s.io/api/rbac/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 "k8s.io/apimachinery/pkg/util/wait" 32 "k8s.io/client-go/informers" 33 clientset "k8s.io/client-go/kubernetes" 34 restclient "k8s.io/client-go/rest" 35 "k8s.io/klog/v2/ktesting" 36 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 37 "k8s.io/kubernetes/pkg/controller/certificates" 38 "k8s.io/kubernetes/pkg/controller/certificates/approver" 39 "k8s.io/kubernetes/test/integration/authutil" 40 "k8s.io/kubernetes/test/integration/framework" 41 ) 42 43 // Integration tests that verify the behaviour of the CSR auto-approving controller. 44 func TestController_AutoApproval(t *testing.T) { 45 validKubeAPIServerClientKubeletUsername := "system:node:abc" 46 validKubeAPIServerClientKubeletCSR := pemWithTemplate(&x509.CertificateRequest{ 47 Subject: pkix.Name{ 48 CommonName: validKubeAPIServerClientKubeletUsername, 49 Organization: []string{"system:nodes"}, 50 }, 51 }) 52 validKubeAPIServerClientKubeletUsages := []certv1.KeyUsage{ 53 certv1.UsageDigitalSignature, 54 certv1.UsageKeyEncipherment, 55 certv1.UsageClientAuth, 56 } 57 tests := map[string]struct { 58 signerName string 59 request []byte 60 usages []certv1.KeyUsage 61 username string 62 autoApproved bool 63 grantNodeClient bool 64 grantSelfNodeClient bool 65 }{ 66 "should auto-approve CSR that has kube-apiserver-client-kubelet signerName and matches requirements": { 67 signerName: certv1.KubeAPIServerClientKubeletSignerName, 68 request: validKubeAPIServerClientKubeletCSR, 69 usages: validKubeAPIServerClientKubeletUsages, 70 username: validKubeAPIServerClientKubeletUsername, 71 grantSelfNodeClient: true, 72 autoApproved: true, 73 }, 74 "should auto-approve CSR that has kube-apiserver-client-kubelet signerName and matches requirements despite missing username if nodeclient permissions are granted": { 75 signerName: certv1.KubeAPIServerClientKubeletSignerName, 76 request: validKubeAPIServerClientKubeletCSR, 77 usages: validKubeAPIServerClientKubeletUsages, 78 username: "does-not-match-cn", 79 grantNodeClient: true, 80 autoApproved: true, 81 }, 82 "should not auto-approve CSR that has kube-apiserver-client signerName that DOES match kubelet CSR requirements": { 83 signerName: certv1.KubeAPIServerClientSignerName, 84 request: validKubeAPIServerClientKubeletCSR, 85 usages: validKubeAPIServerClientKubeletUsages, 86 username: validKubeAPIServerClientKubeletUsername, 87 autoApproved: false, 88 }, 89 } 90 for name, test := range tests { 91 t.Run(name, func(t *testing.T) { 92 _, ctx := ktesting.NewTestContext(t) 93 ctx, cancel := context.WithCancel(ctx) 94 defer cancel() 95 // Run an apiserver with the default configuration options. 96 s := kubeapiservertesting.StartTestServerOrDie(t, kubeapiservertesting.NewDefaultTestServerOptions(), []string{""}, framework.SharedEtcd()) 97 defer s.TearDownFn() 98 client := clientset.NewForConfigOrDie(s.ClientConfig) 99 informers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(s.ClientConfig, "certificatesigningrequest-informers")), time.Second) 100 101 // Register the controller 102 c := approver.NewCSRApprovingController(ctx, client, informers.Certificates().V1().CertificateSigningRequests()) 103 // Start the controller & informers 104 informers.Start(ctx.Done()) 105 go c.Run(ctx, 1) 106 107 // Configure appropriate permissions 108 if test.grantNodeClient { 109 grantUserNodeClientPermissions(t, client, test.username, false) 110 } 111 if test.grantSelfNodeClient { 112 grantUserNodeClientPermissions(t, client, test.username, true) 113 } 114 115 // Use a client that impersonates the test case 'username' to ensure the `spec.username` 116 // field on the CSR is set correctly. 117 impersonationConfig := restclient.CopyConfig(s.ClientConfig) 118 impersonationConfig.Impersonate.UserName = test.username 119 impersonationClient, err := clientset.NewForConfig(impersonationConfig) 120 if err != nil { 121 t.Fatalf("Error in create clientset: %v", err) 122 } 123 csr := &certv1.CertificateSigningRequest{ 124 ObjectMeta: metav1.ObjectMeta{ 125 Name: "csr", 126 }, 127 Spec: certv1.CertificateSigningRequestSpec{ 128 Request: test.request, 129 Usages: test.usages, 130 SignerName: test.signerName, 131 }, 132 } 133 _, err = impersonationClient.CertificatesV1().CertificateSigningRequests().Create(context.TODO(), csr, metav1.CreateOptions{}) 134 if err != nil { 135 t.Fatalf("failed to create testing CSR: %v", err) 136 } 137 138 if test.autoApproved { 139 if err := waitForCertificateRequestApproved(client, csr.Name); err != nil { 140 t.Errorf("failed to wait for CSR to be auto-approved: %v", err) 141 } 142 } else { 143 if err := ensureCertificateRequestNotApproved(client, csr.Name); err != nil { 144 t.Errorf("failed to ensure that CSR was not auto-approved: %v", err) 145 } 146 } 147 }) 148 } 149 } 150 151 const ( 152 interval = 100 * time.Millisecond 153 timeout = 5 * time.Second 154 ) 155 156 func waitForCertificateRequestApproved(client clientset.Interface, name string) error { 157 if err := wait.Poll(interval, timeout, func() (bool, error) { 158 csr, err := client.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{}) 159 if err != nil { 160 return false, err 161 } 162 if certificates.IsCertificateRequestApproved(csr) { 163 return true, nil 164 } 165 return false, nil 166 }); err != nil { 167 return err 168 } 169 return nil 170 } 171 172 func ensureCertificateRequestNotApproved(client clientset.Interface, name string) error { 173 // If waiting for the CSR to be approved times out, we class this as 'not auto approved'. 174 // There is currently no way to explicitly check if the CSR has been rejected for auto-approval. 175 err := waitForCertificateRequestApproved(client, name) 176 switch { 177 case err == wait.ErrWaitTimeout: 178 return nil 179 case err == nil: 180 return fmt.Errorf("CertificateSigningRequest was auto-approved") 181 default: 182 return err 183 } 184 } 185 186 func grantUserNodeClientPermissions(t *testing.T, client clientset.Interface, username string, selfNodeClient bool) { 187 resourceType := "certificatesigningrequests/nodeclient" 188 if selfNodeClient { 189 resourceType = "certificatesigningrequests/selfnodeclient" 190 } 191 cr := buildNodeClientRoleForUser("role", resourceType) 192 crb := buildClusterRoleBindingForUser("rolebinding", username, cr.Name) 193 if _, err := client.RbacV1().ClusterRoles().Create(context.TODO(), cr, metav1.CreateOptions{}); err != nil { 194 t.Fatalf("failed to create test fixtures: %v", err) 195 } 196 if _, err := client.RbacV1().ClusterRoleBindings().Create(context.TODO(), crb, metav1.CreateOptions{}); err != nil { 197 t.Fatalf("failed to create test fixtures: %v", err) 198 } 199 rule := cr.Rules[0] 200 authutil.WaitForNamedAuthorizationUpdate(t, context.TODO(), client.AuthorizationV1(), username, "", rule.Verbs[0], "", schema.GroupResource{Group: rule.APIGroups[0], Resource: rule.Resources[0]}, true) 201 } 202 203 func buildNodeClientRoleForUser(name string, resourceType string) *rbacv1.ClusterRole { 204 return &rbacv1.ClusterRole{ 205 ObjectMeta: metav1.ObjectMeta{ 206 Name: name, 207 }, 208 Rules: []rbacv1.PolicyRule{ 209 { 210 Verbs: []string{"create"}, 211 APIGroups: []string{certv1.SchemeGroupVersion.Group}, 212 Resources: []string{resourceType}, 213 }, 214 }, 215 } 216 }