1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package csr
18
19import (
20	"context"
21	"crypto"
22	"crypto/x509"
23	"encoding/pem"
24	"fmt"
25	"reflect"
26	"time"
27
28	"k8s.io/klog"
29
30	certificates "k8s.io/api/certificates/v1beta1"
31	"k8s.io/apimachinery/pkg/api/errors"
32	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33	"k8s.io/apimachinery/pkg/fields"
34	"k8s.io/apimachinery/pkg/runtime"
35	"k8s.io/apimachinery/pkg/util/wait"
36	"k8s.io/apimachinery/pkg/watch"
37	certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
38	"k8s.io/client-go/tools/cache"
39	watchtools "k8s.io/client-go/tools/watch"
40	certutil "k8s.io/client-go/util/cert"
41)
42
43// RequestCertificate will either use an existing (if this process has run
44// before but not to completion) or create a certificate signing request using the
45// PEM encoded CSR and send it to API server, then it will watch the object's
46// status, once approved by API server, it will return the API server's issued
47// certificate (pem-encoded). If there is any errors, or the watch timeouts, it
48// will return an error.
49func RequestCertificate(client certificatesclient.CertificateSigningRequestInterface, csrData []byte, name string, usages []certificates.KeyUsage, privateKey interface{}) (req *certificates.CertificateSigningRequest, err error) {
50	csr := &certificates.CertificateSigningRequest{
51		// Username, UID, Groups will be injected by API server.
52		TypeMeta: metav1.TypeMeta{Kind: "CertificateSigningRequest"},
53		ObjectMeta: metav1.ObjectMeta{
54			Name: name,
55		},
56		Spec: certificates.CertificateSigningRequestSpec{
57			Request: csrData,
58			Usages:  usages,
59		},
60	}
61	if len(csr.Name) == 0 {
62		csr.GenerateName = "csr-"
63	}
64
65	req, err = client.Create(csr)
66	switch {
67	case err == nil:
68	case errors.IsAlreadyExists(err) && len(name) > 0:
69		klog.Infof("csr for this node already exists, reusing")
70		req, err = client.Get(name, metav1.GetOptions{})
71		if err != nil {
72			return nil, formatError("cannot retrieve certificate signing request: %v", err)
73		}
74		if err := ensureCompatible(req, csr, privateKey); err != nil {
75			return nil, fmt.Errorf("retrieved csr is not compatible: %v", err)
76		}
77		klog.Infof("csr for this node is still valid")
78	default:
79		return nil, formatError("cannot create certificate signing request: %v", err)
80	}
81	return req, nil
82}
83
84// WaitForCertificate waits for a certificate to be issued until timeout, or returns an error.
85func WaitForCertificate(ctx context.Context, client certificatesclient.CertificateSigningRequestInterface, req *certificates.CertificateSigningRequest) (certData []byte, err error) {
86	fieldSelector := fields.OneTermEqualSelector("metadata.name", req.Name).String()
87	lw := &cache.ListWatch{
88		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
89			options.FieldSelector = fieldSelector
90			return client.List(options)
91		},
92		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
93			options.FieldSelector = fieldSelector
94			return client.Watch(options)
95		},
96	}
97	event, err := watchtools.UntilWithSync(
98		ctx,
99		lw,
100		&certificates.CertificateSigningRequest{},
101		nil,
102		func(event watch.Event) (bool, error) {
103			switch event.Type {
104			case watch.Modified, watch.Added:
105			case watch.Deleted:
106				return false, fmt.Errorf("csr %q was deleted", req.Name)
107			default:
108				return false, nil
109			}
110			csr := event.Object.(*certificates.CertificateSigningRequest)
111			if csr.UID != req.UID {
112				return false, fmt.Errorf("csr %q changed UIDs", csr.Name)
113			}
114			for _, c := range csr.Status.Conditions {
115				if c.Type == certificates.CertificateDenied {
116					return false, fmt.Errorf("certificate signing request is not approved, reason: %v, message: %v", c.Reason, c.Message)
117				}
118				if c.Type == certificates.CertificateApproved && csr.Status.Certificate != nil {
119					return true, nil
120				}
121			}
122			return false, nil
123		},
124	)
125	if err == wait.ErrWaitTimeout {
126		return nil, wait.ErrWaitTimeout
127	}
128	if err != nil {
129		return nil, formatError("cannot watch on the certificate signing request: %v", err)
130	}
131
132	return event.Object.(*certificates.CertificateSigningRequest).Status.Certificate, nil
133}
134
135// ensureCompatible ensures that a CSR object is compatible with an original CSR
136func ensureCompatible(new, orig *certificates.CertificateSigningRequest, privateKey interface{}) error {
137	newCSR, err := parseCSR(new)
138	if err != nil {
139		return fmt.Errorf("unable to parse new csr: %v", err)
140	}
141	origCSR, err := parseCSR(orig)
142	if err != nil {
143		return fmt.Errorf("unable to parse original csr: %v", err)
144	}
145	if !reflect.DeepEqual(newCSR.Subject, origCSR.Subject) {
146		return fmt.Errorf("csr subjects differ: new: %#v, orig: %#v", newCSR.Subject, origCSR.Subject)
147	}
148	signer, ok := privateKey.(crypto.Signer)
149	if !ok {
150		return fmt.Errorf("privateKey is not a signer")
151	}
152	newCSR.PublicKey = signer.Public()
153	if err := newCSR.CheckSignature(); err != nil {
154		return fmt.Errorf("error validating signature new CSR against old key: %v", err)
155	}
156	if len(new.Status.Certificate) > 0 {
157		certs, err := certutil.ParseCertsPEM(new.Status.Certificate)
158		if err != nil {
159			return fmt.Errorf("error parsing signed certificate for CSR: %v", err)
160		}
161		now := time.Now()
162		for _, cert := range certs {
163			if now.After(cert.NotAfter) {
164				return fmt.Errorf("one of the certificates for the CSR has expired: %s", cert.NotAfter)
165			}
166		}
167	}
168	return nil
169}
170
171// formatError preserves the type of an API message but alters the message. Expects
172// a single argument format string, and returns the wrapped error.
173func formatError(format string, err error) error {
174	if s, ok := err.(errors.APIStatus); ok {
175		se := &errors.StatusError{ErrStatus: s.Status()}
176		se.ErrStatus.Message = fmt.Sprintf(format, se.ErrStatus.Message)
177		return se
178	}
179	return fmt.Errorf(format, err)
180}
181
182// parseCSR extracts the CSR from the API object and decodes it.
183func parseCSR(obj *certificates.CertificateSigningRequest) (*x509.CertificateRequest, error) {
184	// extract PEM from request object
185	block, _ := pem.Decode(obj.Spec.Request)
186	if block == nil || block.Type != "CERTIFICATE REQUEST" {
187		return nil, fmt.Errorf("PEM block type must be CERTIFICATE REQUEST")
188	}
189	return x509.ParseCertificateRequest(block.Bytes)
190}
191