k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/clustertrustbundles/admission_establishtrust_test.go (about) 1 /* 2 Copyright 2022 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 clustertrustbundles 18 19 import ( 20 "context" 21 "crypto/x509" 22 "crypto/x509/pkix" 23 "math/big" 24 "testing" 25 26 certsv1alpha1 "k8s.io/api/certificates/v1alpha1" 27 rbacv1 "k8s.io/api/rbac/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/client-go/kubernetes" 31 "k8s.io/client-go/rest" 32 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 33 "k8s.io/kubernetes/test/integration/authutil" 34 "k8s.io/kubernetes/test/integration/framework" 35 ) 36 37 // Verifies that the ClusterTrustBundle attest admission plugin correctly 38 // enforces that a user has "attest" on the affected signer name. 39 func TestCTBAttestPlugin(t *testing.T) { 40 testCases := []struct { 41 description string 42 trustBundleName string 43 allowedSignerName string 44 targetSignerName string 45 wantError string 46 }{ 47 { 48 description: "should admit if the clustertrustbundle doesn't target a signer", 49 trustBundleName: "foo", 50 allowedSignerName: "foo.com/bar", 51 }, 52 { 53 description: "should admit if the user has attest for the exact signer name", 54 trustBundleName: "foo.com:bar:abc", 55 allowedSignerName: "foo.com/bar", 56 targetSignerName: "foo.com/bar", 57 }, 58 { 59 description: "should admit if the user has attest for the wildcard-suffixed signer name", 60 trustBundleName: "foo.com:bar:abc", 61 allowedSignerName: "foo.com/*", 62 targetSignerName: "foo.com/bar", 63 }, 64 { 65 description: "should deny if the user does not have permission for the signer name", 66 trustBundleName: "foo.com:bar:abc", 67 allowedSignerName: "abc.com/def", 68 targetSignerName: "foo.com/bar", 69 wantError: "clustertrustbundles.certificates.k8s.io \"foo.com:bar:abc\" is forbidden: user not permitted to attest for signerName \"foo.com/bar\"", 70 }, 71 } 72 for _, tc := range testCases { 73 t.Run(tc.description, func(t *testing.T) { 74 ctx := context.Background() 75 76 server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--authorization-mode=RBAC", "--feature-gates=ClusterTrustBundle=true"}, framework.SharedEtcd()) 77 defer server.TearDownFn() 78 79 client := kubernetes.NewForConfigOrDie(server.ClientConfig) 80 81 if tc.allowedSignerName != "" { 82 grantUserPermissionToAttestFor(ctx, t, client, "test-user", tc.allowedSignerName) 83 } 84 85 // Create a second client that impersonates test-user. 86 testUserConfig := rest.CopyConfig(server.ClientConfig) 87 testUserConfig.Impersonate = rest.ImpersonationConfig{UserName: "test-user"} 88 testUserClient := kubernetes.NewForConfigOrDie(testUserConfig) 89 90 bundle := &certsv1alpha1.ClusterTrustBundle{ 91 ObjectMeta: metav1.ObjectMeta{ 92 Name: tc.trustBundleName, 93 }, 94 Spec: certsv1alpha1.ClusterTrustBundleSpec{ 95 SignerName: tc.targetSignerName, 96 TrustBundle: mustMakePEMBlock("CERTIFICATE", nil, mustMakeCertificate(t, &x509.Certificate{ 97 SerialNumber: big.NewInt(0), 98 Subject: pkix.Name{ 99 CommonName: "root1", 100 }, 101 IsCA: true, 102 BasicConstraintsValid: true, 103 })), 104 }, 105 } 106 _, err := testUserClient.CertificatesV1alpha1().ClusterTrustBundles().Create(ctx, bundle, metav1.CreateOptions{}) 107 if err != nil && err.Error() != tc.wantError { 108 t.Fatalf("Bad error while creating ClusterTrustBundle; got %q want %q", err.Error(), tc.wantError) 109 } else if err == nil && tc.wantError != "" { 110 t.Fatalf("Bad error while creating ClusterTrustBundle; got nil want %q", tc.wantError) 111 } 112 }) 113 } 114 } 115 116 func grantUserPermissionToAttestFor(ctx context.Context, t *testing.T, client kubernetes.Interface, username string, signerNames ...string) { 117 resourceName := "signername-" + username 118 cr := buildApprovalClusterRoleForSigners(resourceName, signerNames...) 119 crb := buildClusterRoleBindingForUser(resourceName, username, cr.Name) 120 if _, err := client.RbacV1().ClusterRoles().Create(ctx, cr, metav1.CreateOptions{}); err != nil { 121 t.Fatalf("unable to create test fixture RBAC rules: %v", err) 122 } 123 if _, err := client.RbacV1().ClusterRoleBindings().Create(ctx, crb, metav1.CreateOptions{}); err != nil { 124 t.Fatalf("unable to create test fixture RBAC rules: %v", err) 125 } 126 attestRule := cr.Rules[0] 127 createRule := cr.Rules[1] 128 authutil.WaitForNamedAuthorizationUpdate(t, ctx, client.AuthorizationV1(), username, "", attestRule.Verbs[0], attestRule.ResourceNames[0], schema.GroupResource{Group: attestRule.APIGroups[0], Resource: attestRule.Resources[0]}, true) 129 authutil.WaitForNamedAuthorizationUpdate(t, ctx, client.AuthorizationV1(), username, "", createRule.Verbs[0], "", schema.GroupResource{Group: createRule.APIGroups[0], Resource: createRule.Resources[0]}, true) 130 } 131 132 func buildApprovalClusterRoleForSigners(name string, signerNames ...string) *rbacv1.ClusterRole { 133 return &rbacv1.ClusterRole{ 134 ObjectMeta: metav1.ObjectMeta{ 135 Name: name, 136 }, 137 Rules: []rbacv1.PolicyRule{ 138 { 139 Verbs: []string{"attest"}, 140 APIGroups: []string{"certificates.k8s.io"}, 141 Resources: []string{"signers"}, 142 ResourceNames: signerNames, 143 }, 144 { 145 Verbs: []string{"create"}, 146 APIGroups: []string{"certificates.k8s.io"}, 147 Resources: []string{"clustertrustbundles"}, 148 }, 149 }, 150 } 151 } 152 153 func buildClusterRoleBindingForUser(name, username, clusterRoleName string) *rbacv1.ClusterRoleBinding { 154 return &rbacv1.ClusterRoleBinding{ 155 ObjectMeta: metav1.ObjectMeta{ 156 Name: name, 157 }, 158 Subjects: []rbacv1.Subject{ 159 { 160 Kind: rbacv1.UserKind, 161 Name: username, 162 }, 163 }, 164 RoleRef: rbacv1.RoleRef{ 165 APIGroup: rbacv1.SchemeGroupVersion.Group, 166 Kind: "ClusterRole", 167 Name: clusterRoleName, 168 }, 169 } 170 }