k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/certificates/admission_sign_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 rbacv1 "k8s.io/api/rbac/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 clientset "k8s.io/client-go/kubernetes" 28 restclient "k8s.io/client-go/rest" 29 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 30 "k8s.io/kubernetes/test/integration/authutil" 31 "k8s.io/kubernetes/test/integration/framework" 32 ) 33 34 // Verifies that the CSR approval admission plugin correctly enforces that a 35 // user has permission to sign CSRs for the named signer 36 func TestCSRSignerNameSigningPlugin(t *testing.T) { 37 tests := map[string]struct { 38 allowedSignerName string 39 signerName string 40 error string 41 }{ 42 "should admit when a user has permission for the exact signerName": { 43 allowedSignerName: "example.com/something", 44 signerName: "example.com/something", 45 }, 46 "should admit when a user has permission for the wildcard-suffixed signerName": { 47 allowedSignerName: "example.com/*", 48 signerName: "example.com/something", 49 }, 50 "should deny if a user does not have permission for the given signerName": { 51 allowedSignerName: "example.com/not-something", 52 signerName: "example.com/something", 53 error: `certificatesigningrequests.certificates.k8s.io "csr" is forbidden: user not permitted to sign requests with signerName "example.com/something"`, 54 }, 55 } 56 for name, test := range tests { 57 t.Run(name, func(t *testing.T) { 58 // Run an apiserver with the default configuration options. 59 s := kubeapiservertesting.StartTestServerOrDie(t, kubeapiservertesting.NewDefaultTestServerOptions(), []string{"--authorization-mode=RBAC"}, framework.SharedEtcd()) 60 defer s.TearDownFn() 61 client := clientset.NewForConfigOrDie(s.ClientConfig) 62 63 // Drop the default RBAC superuser permissions to rely on the internal superuser authorizer 64 if err := client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), "cluster-admin", metav1.DeleteOptions{}); err != nil { 65 t.Fatal(err) 66 } 67 68 // Grant 'test-user' permission to sign CertificateSigningRequests with the specified signerName. 69 const username = "test-user" 70 grantUserPermissionToSignFor(t, client, username, test.allowedSignerName) 71 // Create a CSR to attempt to sign. 72 csr := createTestingCSR(t, client.CertificatesV1().CertificateSigningRequests(), "csr", test.signerName, "") 73 74 // Create a second client, impersonating the 'test-user' for us to test with. 75 testuserConfig := restclient.CopyConfig(s.ClientConfig) 76 testuserConfig.Impersonate = restclient.ImpersonationConfig{UserName: username} 77 testuserClient := clientset.NewForConfigOrDie(testuserConfig) 78 79 // Attempt to 'sign' the certificate. 80 // random valid pem 81 csr.Status.Certificate = []byte(` 82 Leading non-PEM content 83 -----BEGIN CERTIFICATE----- 84 MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw 85 GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx 86 MTYwOTE3MDUwNjAwWjAUMRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB 87 BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb 88 KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC 89 BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU 90 K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q 91 a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5 92 MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1dveps= 93 -----END CERTIFICATE----- 94 Intermediate non-PEM content 95 -----BEGIN CERTIFICATE----- 96 MIIBqDCCAU6gAwIBAgIUfqZtjoFgczZ+oQZbEC/BDSS2J6wwCgYIKoZIzj0EAwIw 97 EjEQMA4GA1UEAxMHUm9vdC1DQTAgFw0xNjEwMTEwNTA2MDBaGA8yMTE2MDkxNzA1 98 MDYwMFowGjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMFkwEwYHKoZIzj0CAQYI 99 KoZIzj0DAQcDQgAEyWHEMMCctJg8Xa5YWLqaCPbk3MjB+uvXac42JM9pj4k9jedD 100 kpUJRkWIPzgJI8Zk/3cSzluUTixP6JBSDKtwwaN4MHYwDgYDVR0PAQH/BAQDAgGm 101 MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE 102 FF+p0JcY31pz+mjNZnjv0Gum92vZMB8GA1UdIwQYMBaAFB7P6+i4/pfNjqZgJv/b 103 dgA7Fe4tMAoGCCqGSM49BAMCA0gAMEUCIQCTT1YWQZaAqfQ2oBxzOkJE2BqLFxhz 104 3smQlrZ5gCHddwIgcvT7puhYOzAgcvMn9+SZ1JOyZ7edODjshCVCRnuHK2c= 105 -----END CERTIFICATE----- 106 Trailing non-PEM content 107 `) 108 109 // superuser should always have permission to sign; dry-run so we don't actually modify the CSR so the non-superuser can attempt as well 110 if _, err := client.CertificatesV1().CertificateSigningRequests().UpdateStatus(context.TODO(), csr, metav1.UpdateOptions{DryRun: []string{metav1.DryRunAll}}); err != nil { 111 t.Errorf("expected no superuser error but got: %v", err) 112 } 113 114 _, err := testuserClient.CertificatesV1().CertificateSigningRequests().UpdateStatus(context.TODO(), csr, metav1.UpdateOptions{}) 115 if err != nil && test.error != err.Error() { 116 t.Errorf("expected error %q but got: %v", test.error, err) 117 } 118 if err == nil && test.error != "" { 119 t.Errorf("expected to get an error %q but got none", test.error) 120 } 121 }) 122 } 123 } 124 125 func grantUserPermissionToSignFor(t *testing.T, client clientset.Interface, username string, signerNames ...string) { 126 resourceName := "signername-" + username 127 cr := buildSigningClusterRoleForSigners(resourceName, signerNames...) 128 crb := buildClusterRoleBindingForUser(resourceName, username, cr.Name) 129 if _, err := client.RbacV1().ClusterRoles().Create(context.TODO(), cr, metav1.CreateOptions{}); err != nil { 130 t.Fatalf("failed to create test fixtures: %v", err) 131 } 132 if _, err := client.RbacV1().ClusterRoleBindings().Create(context.TODO(), crb, metav1.CreateOptions{}); err != nil { 133 t.Fatalf("failed to create test fixtures: %v", err) 134 } 135 signRule := cr.Rules[0] 136 statusRule := cr.Rules[1] 137 authutil.WaitForNamedAuthorizationUpdate(t, context.TODO(), client.AuthorizationV1(), username, "", signRule.Verbs[0], signRule.ResourceNames[0], schema.GroupResource{Group: signRule.APIGroups[0], Resource: signRule.Resources[0]}, true) 138 authutil.WaitForNamedAuthorizationUpdate(t, context.TODO(), client.AuthorizationV1(), username, "", statusRule.Verbs[0], "", schema.GroupResource{Group: statusRule.APIGroups[0], Resource: statusRule.Resources[0]}, true) 139 } 140 141 func buildSigningClusterRoleForSigners(name string, signerNames ...string) *rbacv1.ClusterRole { 142 return &rbacv1.ClusterRole{ 143 ObjectMeta: metav1.ObjectMeta{ 144 Name: name, 145 }, 146 Rules: []rbacv1.PolicyRule{ 147 // must have permission to 'approve' the 'certificatesigners' named 148 // 'signerName' to approve CSRs with the given signerName. 149 { 150 Verbs: []string{"sign"}, 151 APIGroups: []string{certv1.SchemeGroupVersion.Group}, 152 Resources: []string{"signers"}, 153 ResourceNames: signerNames, 154 }, 155 { 156 Verbs: []string{"update"}, 157 APIGroups: []string{certv1.SchemeGroupVersion.Group}, 158 Resources: []string{"certificatesigningrequests/status"}, 159 }, 160 }, 161 } 162 }