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  }