k8s.io/kubernetes@v1.29.3/test/integration/certificates/admission_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  	"testing"
    22  
    23  	certv1 "k8s.io/api/certificates/v1"
    24  	v1 "k8s.io/api/core/v1"
    25  	rbacv1 "k8s.io/api/rbac/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime/schema"
    28  	clientset "k8s.io/client-go/kubernetes"
    29  	restclient "k8s.io/client-go/rest"
    30  	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    31  	"k8s.io/kubernetes/test/integration/authutil"
    32  	"k8s.io/kubernetes/test/integration/framework"
    33  )
    34  
    35  // Verifies that the CSR approval admission plugin correctly enforces that a
    36  // user has permission to approve CSRs for the named signer
    37  func TestCSRSignerNameApprovalPlugin(t *testing.T) {
    38  	tests := map[string]struct {
    39  		allowedSignerName string
    40  		signerName        string
    41  		error             string
    42  	}{
    43  		"should admit when a user has permission for the exact signerName": {
    44  			allowedSignerName: "example.com/something",
    45  			signerName:        "example.com/something",
    46  		},
    47  		"should admit when a user has permission for the wildcard-suffixed signerName": {
    48  			allowedSignerName: "example.com/*",
    49  			signerName:        "example.com/something",
    50  		},
    51  		"should deny if a user does not have permission for the given signerName": {
    52  			allowedSignerName: "example.com/not-something",
    53  			signerName:        "example.com/something",
    54  			error:             `certificatesigningrequests.certificates.k8s.io "csr" is forbidden: user not permitted to approve requests with signerName "example.com/something"`,
    55  		},
    56  	}
    57  	for name, test := range tests {
    58  		t.Run(name, func(t *testing.T) {
    59  			// Run an apiserver with the default configuration options.
    60  			s := kubeapiservertesting.StartTestServerOrDie(t, kubeapiservertesting.NewDefaultTestServerOptions(), []string{"--authorization-mode=RBAC"}, framework.SharedEtcd())
    61  			defer s.TearDownFn()
    62  			client := clientset.NewForConfigOrDie(s.ClientConfig)
    63  
    64  			// Grant 'test-user' permission to approve CertificateSigningRequests with the specified signerName.
    65  			const username = "test-user"
    66  			grantUserPermissionToApproveFor(t, client, username, test.allowedSignerName)
    67  			// Create a CSR to attempt to approve.
    68  			csr := createTestingCSR(t, client.CertificatesV1().CertificateSigningRequests(), "csr", test.signerName, "")
    69  
    70  			// Create a second client, impersonating the 'test-user' for us to test with.
    71  			testuserConfig := restclient.CopyConfig(s.ClientConfig)
    72  			testuserConfig.Impersonate = restclient.ImpersonationConfig{UserName: username}
    73  			testuserClient := clientset.NewForConfigOrDie(testuserConfig)
    74  
    75  			// Attempt to update the Approved condition.
    76  			csr.Status.Conditions = append(csr.Status.Conditions, certv1.CertificateSigningRequestCondition{
    77  				Type:    certv1.CertificateApproved,
    78  				Status:  v1.ConditionTrue,
    79  				Reason:  "AutoApproved",
    80  				Message: "Approved during integration test",
    81  			})
    82  			_, err := testuserClient.CertificatesV1().CertificateSigningRequests().UpdateApproval(context.TODO(), csr.Name, csr, metav1.UpdateOptions{})
    83  			if err != nil && test.error != err.Error() {
    84  				t.Errorf("expected error %q but got: %v", test.error, err)
    85  			}
    86  			if err == nil && test.error != "" {
    87  				t.Errorf("expected to get an error %q but got none", test.error)
    88  			}
    89  		})
    90  	}
    91  }
    92  
    93  func grantUserPermissionToApproveFor(t *testing.T, client clientset.Interface, username string, signerNames ...string) {
    94  	resourceName := "signername-" + username
    95  	cr := buildApprovalClusterRoleForSigners(resourceName, signerNames...)
    96  	crb := buildClusterRoleBindingForUser(resourceName, username, cr.Name)
    97  	if _, err := client.RbacV1().ClusterRoles().Create(context.TODO(), cr, metav1.CreateOptions{}); err != nil {
    98  		t.Fatalf("unable to create test fixture RBAC rules: %v", err)
    99  	}
   100  	if _, err := client.RbacV1().ClusterRoleBindings().Create(context.TODO(), crb, metav1.CreateOptions{}); err != nil {
   101  		t.Fatalf("unable to create test fixture RBAC rules: %v", err)
   102  	}
   103  	approveRule := cr.Rules[0]
   104  	updateRule := cr.Rules[1]
   105  	authutil.WaitForNamedAuthorizationUpdate(t, context.TODO(), client.AuthorizationV1(), username, "", approveRule.Verbs[0], approveRule.ResourceNames[0], schema.GroupResource{Group: approveRule.APIGroups[0], Resource: approveRule.Resources[0]}, true)
   106  	authutil.WaitForNamedAuthorizationUpdate(t, context.TODO(), client.AuthorizationV1(), username, "", updateRule.Verbs[0], "", schema.GroupResource{Group: updateRule.APIGroups[0], Resource: updateRule.Resources[0]}, true)
   107  }
   108  
   109  func buildApprovalClusterRoleForSigners(name string, signerNames ...string) *rbacv1.ClusterRole {
   110  	return &rbacv1.ClusterRole{
   111  		ObjectMeta: metav1.ObjectMeta{
   112  			Name: name,
   113  		},
   114  		Rules: []rbacv1.PolicyRule{
   115  			// must have permission to 'approve' the 'certificatesigners' named
   116  			// 'signerName' to approve CSRs with the given signerName.
   117  			{
   118  				Verbs:         []string{"approve"},
   119  				APIGroups:     []string{certv1.SchemeGroupVersion.Group},
   120  				Resources:     []string{"signers"},
   121  				ResourceNames: signerNames,
   122  			},
   123  			{
   124  				Verbs:     []string{"update"},
   125  				APIGroups: []string{certv1.SchemeGroupVersion.Group},
   126  				Resources: []string{"certificatesigningrequests/approval"},
   127  			},
   128  		},
   129  	}
   130  }
   131  
   132  func buildClusterRoleBindingForUser(name, username, clusterRoleName string) *rbacv1.ClusterRoleBinding {
   133  	return &rbacv1.ClusterRoleBinding{
   134  		ObjectMeta: metav1.ObjectMeta{
   135  			Name: name,
   136  		},
   137  		Subjects: []rbacv1.Subject{
   138  			{
   139  				Kind: rbacv1.UserKind,
   140  				Name: username,
   141  			},
   142  		},
   143  		RoleRef: rbacv1.RoleRef{
   144  			APIGroup: rbacv1.SchemeGroupVersion.Group,
   145  			Kind:     "ClusterRole",
   146  			Name:     clusterRoleName,
   147  		},
   148  	}
   149  }