1
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
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
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
102 c := approver.NewCSRApprovingController(ctx, client, informers.Certificates().V1().CertificateSigningRequests())
103
104 informers.Start(ctx.Done())
105 go c.Run(ctx, 1)
106
107
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
116
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
174
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 }
217
View as plain text