1
16
17 package csr
18
19 import (
20 "context"
21 "crypto"
22 "crypto/x509"
23 "encoding/pem"
24 "fmt"
25 "reflect"
26 "time"
27
28 certificatesv1 "k8s.io/api/certificates/v1"
29 certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
30 apierrors "k8s.io/apimachinery/pkg/api/errors"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 "k8s.io/apimachinery/pkg/fields"
33 "k8s.io/apimachinery/pkg/runtime"
34 "k8s.io/apimachinery/pkg/types"
35 "k8s.io/apimachinery/pkg/util/wait"
36 "k8s.io/apimachinery/pkg/watch"
37 clientset "k8s.io/client-go/kubernetes"
38 "k8s.io/client-go/tools/cache"
39 watchtools "k8s.io/client-go/tools/watch"
40 certutil "k8s.io/client-go/util/cert"
41 "k8s.io/klog/v2"
42 "k8s.io/utils/pointer"
43 )
44
45
46
47
48
49
50 func RequestCertificate(client clientset.Interface, csrData []byte, name, signerName string, requestedDuration *time.Duration, usages []certificatesv1.KeyUsage, privateKey interface{}) (reqName string, reqUID types.UID, err error) {
51 csr := &certificatesv1.CertificateSigningRequest{
52
53 TypeMeta: metav1.TypeMeta{Kind: "CertificateSigningRequest"},
54 ObjectMeta: metav1.ObjectMeta{
55 Name: name,
56 },
57 Spec: certificatesv1.CertificateSigningRequestSpec{
58 Request: csrData,
59 Usages: usages,
60 SignerName: signerName,
61 },
62 }
63 if len(csr.Name) == 0 {
64 csr.GenerateName = "csr-"
65 }
66 if requestedDuration != nil {
67 csr.Spec.ExpirationSeconds = DurationToExpirationSeconds(*requestedDuration)
68 }
69
70 reqName, reqUID, err = create(client, csr)
71 switch {
72 case err == nil:
73 return reqName, reqUID, err
74
75 case apierrors.IsAlreadyExists(err) && len(name) > 0:
76 klog.Infof("csr for this node already exists, reusing")
77 req, err := get(client, name)
78 if err != nil {
79 return "", "", formatError("cannot retrieve certificate signing request: %v", err)
80 }
81 if err := ensureCompatible(req, csr, privateKey); err != nil {
82 return "", "", fmt.Errorf("retrieved csr is not compatible: %v", err)
83 }
84 klog.Infof("csr for this node is still valid")
85 return req.Name, req.UID, nil
86
87 default:
88 return "", "", formatError("cannot create certificate signing request: %v", err)
89 }
90 }
91
92 func DurationToExpirationSeconds(duration time.Duration) *int32 {
93 return pointer.Int32(int32(duration / time.Second))
94 }
95
96 func ExpirationSecondsToDuration(expirationSeconds int32) time.Duration {
97 return time.Duration(expirationSeconds) * time.Second
98 }
99
100 func get(client clientset.Interface, name string) (*certificatesv1.CertificateSigningRequest, error) {
101 v1req, v1err := client.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{})
102 if v1err == nil || !apierrors.IsNotFound(v1err) {
103 return v1req, v1err
104 }
105
106 v1beta1req, v1beta1err := client.CertificatesV1beta1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{})
107 if v1beta1err != nil {
108 return nil, v1beta1err
109 }
110
111 v1req = &certificatesv1.CertificateSigningRequest{
112 ObjectMeta: v1beta1req.ObjectMeta,
113 Spec: certificatesv1.CertificateSigningRequestSpec{
114 Request: v1beta1req.Spec.Request,
115 },
116 }
117 if v1beta1req.Spec.SignerName != nil {
118 v1req.Spec.SignerName = *v1beta1req.Spec.SignerName
119 }
120 for _, usage := range v1beta1req.Spec.Usages {
121 v1req.Spec.Usages = append(v1req.Spec.Usages, certificatesv1.KeyUsage(usage))
122 }
123 return v1req, nil
124 }
125
126 func create(client clientset.Interface, csr *certificatesv1.CertificateSigningRequest) (reqName string, reqUID types.UID, err error) {
127
128 if len(csr.Spec.Usages) > 0 && len(csr.Spec.SignerName) > 0 && csr.Spec.SignerName != "kubernetes.io/legacy-unknown" {
129 v1req, v1err := client.CertificatesV1().CertificateSigningRequests().Create(context.TODO(), csr, metav1.CreateOptions{})
130 switch {
131 case v1err != nil && apierrors.IsNotFound(v1err):
132
133
134 case v1err != nil:
135
136 return "", "", v1err
137
138 default:
139
140 return v1req.Name, v1req.UID, v1err
141 }
142 }
143
144
145 v1beta1csr := &certificatesv1beta1.CertificateSigningRequest{
146 ObjectMeta: csr.ObjectMeta,
147 Spec: certificatesv1beta1.CertificateSigningRequestSpec{
148 SignerName: &csr.Spec.SignerName,
149 Request: csr.Spec.Request,
150 },
151 }
152 for _, usage := range csr.Spec.Usages {
153 v1beta1csr.Spec.Usages = append(v1beta1csr.Spec.Usages, certificatesv1beta1.KeyUsage(usage))
154 }
155
156
157 v1beta1req, v1beta1err := client.CertificatesV1beta1().CertificateSigningRequests().Create(context.TODO(), v1beta1csr, metav1.CreateOptions{})
158 if v1beta1err != nil {
159 return "", "", v1beta1err
160 }
161 return v1beta1req.Name, v1beta1req.UID, nil
162 }
163
164
165 func WaitForCertificate(ctx context.Context, client clientset.Interface, reqName string, reqUID types.UID) (certData []byte, err error) {
166 fieldSelector := fields.OneTermEqualSelector("metadata.name", reqName).String()
167
168 var lw *cache.ListWatch
169 var obj runtime.Object
170 for {
171
172 if _, err := client.CertificatesV1().CertificateSigningRequests().List(ctx, metav1.ListOptions{FieldSelector: fieldSelector}); err == nil {
173
174 obj = &certificatesv1.CertificateSigningRequest{}
175 lw = &cache.ListWatch{
176 ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
177 options.FieldSelector = fieldSelector
178 return client.CertificatesV1().CertificateSigningRequests().List(ctx, options)
179 },
180 WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
181 options.FieldSelector = fieldSelector
182 return client.CertificatesV1().CertificateSigningRequests().Watch(ctx, options)
183 },
184 }
185 break
186 } else {
187 klog.V(2).Infof("error fetching v1 certificate signing request: %v", err)
188 }
189
190
191 if err := ctx.Err(); err != nil {
192 return nil, wait.ErrWaitTimeout
193 }
194
195
196 if _, err := client.CertificatesV1beta1().CertificateSigningRequests().List(ctx, metav1.ListOptions{FieldSelector: fieldSelector}); err == nil {
197
198 obj = &certificatesv1beta1.CertificateSigningRequest{}
199 lw = &cache.ListWatch{
200 ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
201 options.FieldSelector = fieldSelector
202 return client.CertificatesV1beta1().CertificateSigningRequests().List(ctx, options)
203 },
204 WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
205 options.FieldSelector = fieldSelector
206 return client.CertificatesV1beta1().CertificateSigningRequests().Watch(ctx, options)
207 },
208 }
209 break
210 } else {
211 klog.V(2).Infof("error fetching v1beta1 certificate signing request: %v", err)
212 }
213
214
215 if err := ctx.Err(); err != nil {
216 return nil, wait.ErrWaitTimeout
217 }
218
219
220 time.Sleep(time.Second)
221 }
222
223 var issuedCertificate []byte
224 _, err = watchtools.UntilWithSync(
225 ctx,
226 lw,
227 obj,
228 nil,
229 func(event watch.Event) (bool, error) {
230 switch event.Type {
231 case watch.Modified, watch.Added:
232 case watch.Deleted:
233 return false, fmt.Errorf("csr %q was deleted", reqName)
234 default:
235 return false, nil
236 }
237
238 switch csr := event.Object.(type) {
239 case *certificatesv1.CertificateSigningRequest:
240 if csr.UID != reqUID {
241 return false, fmt.Errorf("csr %q changed UIDs", csr.Name)
242 }
243 approved := false
244 for _, c := range csr.Status.Conditions {
245 if c.Type == certificatesv1.CertificateDenied {
246 return false, fmt.Errorf("certificate signing request is denied, reason: %v, message: %v", c.Reason, c.Message)
247 }
248 if c.Type == certificatesv1.CertificateFailed {
249 return false, fmt.Errorf("certificate signing request failed, reason: %v, message: %v", c.Reason, c.Message)
250 }
251 if c.Type == certificatesv1.CertificateApproved {
252 approved = true
253 }
254 }
255 if approved {
256 if len(csr.Status.Certificate) > 0 {
257 klog.V(2).Infof("certificate signing request %s is issued", csr.Name)
258 issuedCertificate = csr.Status.Certificate
259 return true, nil
260 }
261 klog.V(2).Infof("certificate signing request %s is approved, waiting to be issued", csr.Name)
262 }
263
264 case *certificatesv1beta1.CertificateSigningRequest:
265 if csr.UID != reqUID {
266 return false, fmt.Errorf("csr %q changed UIDs", csr.Name)
267 }
268 approved := false
269 for _, c := range csr.Status.Conditions {
270 if c.Type == certificatesv1beta1.CertificateDenied {
271 return false, fmt.Errorf("certificate signing request is denied, reason: %v, message: %v", c.Reason, c.Message)
272 }
273 if c.Type == certificatesv1beta1.CertificateFailed {
274 return false, fmt.Errorf("certificate signing request failed, reason: %v, message: %v", c.Reason, c.Message)
275 }
276 if c.Type == certificatesv1beta1.CertificateApproved {
277 approved = true
278 }
279 }
280 if approved {
281 if len(csr.Status.Certificate) > 0 {
282 klog.V(2).Infof("certificate signing request %s is issued", csr.Name)
283 issuedCertificate = csr.Status.Certificate
284 return true, nil
285 }
286 klog.V(2).Infof("certificate signing request %s is approved, waiting to be issued", csr.Name)
287 }
288
289 default:
290 return false, fmt.Errorf("unexpected type received: %T", event.Object)
291 }
292
293 return false, nil
294 },
295 )
296 if err == wait.ErrWaitTimeout {
297 return nil, wait.ErrWaitTimeout
298 }
299 if err != nil {
300 return nil, formatError("cannot watch on the certificate signing request: %v", err)
301 }
302
303 return issuedCertificate, nil
304 }
305
306
307 func ensureCompatible(new, orig *certificatesv1.CertificateSigningRequest, privateKey interface{}) error {
308 newCSR, err := parseCSR(new.Spec.Request)
309 if err != nil {
310 return fmt.Errorf("unable to parse new csr: %v", err)
311 }
312 origCSR, err := parseCSR(orig.Spec.Request)
313 if err != nil {
314 return fmt.Errorf("unable to parse original csr: %v", err)
315 }
316 if !reflect.DeepEqual(newCSR.Subject, origCSR.Subject) {
317 return fmt.Errorf("csr subjects differ: new: %#v, orig: %#v", newCSR.Subject, origCSR.Subject)
318 }
319 if len(new.Spec.SignerName) > 0 && len(orig.Spec.SignerName) > 0 && new.Spec.SignerName != orig.Spec.SignerName {
320 return fmt.Errorf("csr signerNames differ: new %q, orig: %q", new.Spec.SignerName, orig.Spec.SignerName)
321 }
322 signer, ok := privateKey.(crypto.Signer)
323 if !ok {
324 return fmt.Errorf("privateKey is not a signer")
325 }
326 newCSR.PublicKey = signer.Public()
327 if err := newCSR.CheckSignature(); err != nil {
328 return fmt.Errorf("error validating signature new CSR against old key: %v", err)
329 }
330 if len(new.Status.Certificate) > 0 {
331 certs, err := certutil.ParseCertsPEM(new.Status.Certificate)
332 if err != nil {
333 return fmt.Errorf("error parsing signed certificate for CSR: %v", err)
334 }
335 now := time.Now()
336 for _, cert := range certs {
337 if now.After(cert.NotAfter) {
338 return fmt.Errorf("one of the certificates for the CSR has expired: %s", cert.NotAfter)
339 }
340 }
341 }
342 return nil
343 }
344
345
346
347 func formatError(format string, err error) error {
348 if s, ok := err.(apierrors.APIStatus); ok {
349 se := &apierrors.StatusError{ErrStatus: s.Status()}
350 se.ErrStatus.Message = fmt.Sprintf(format, se.ErrStatus.Message)
351 return se
352 }
353 return fmt.Errorf(format, err)
354 }
355
356
357 func parseCSR(pemData []byte) (*x509.CertificateRequest, error) {
358
359 block, _ := pem.Decode(pemData)
360 if block == nil || block.Type != "CERTIFICATE REQUEST" {
361 return nil, fmt.Errorf("PEM block type must be CERTIFICATE REQUEST")
362 }
363 return x509.ParseCertificateRequest(block.Bytes)
364 }
365
View as plain text