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(client certificatesclient.CertificateSigningRequestInterface, req *certificates.CertificateSigningRequest, timeout time.Duration) (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	ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout)
98	defer cancel()
99	event, err := watchtools.UntilWithSync(
100		ctx,
101		lw,
102		&certificates.CertificateSigningRequest{},
103		nil,
104		func(event watch.Event) (bool, error) {
105			switch event.Type {
106			case watch.Modified, watch.Added:
107			case watch.Deleted:
108				return false, fmt.Errorf("csr %q was deleted", req.Name)
109			default:
110				return false, nil
111			}
112			csr := event.Object.(*certificates.CertificateSigningRequest)
113			if csr.UID != req.UID {
114				return false, fmt.Errorf("csr %q changed UIDs", csr.Name)
115			}
116			for _, c := range csr.Status.Conditions {
117				if c.Type == certificates.CertificateDenied {
118					return false, fmt.Errorf("certificate signing request is not approved, reason: %v, message: %v", c.Reason, c.Message)
119				}
120				if c.Type == certificates.CertificateApproved && csr.Status.Certificate != nil {
121					return true, nil
122				}
123			}
124			return false, nil
125		},
126	)
127	if err == wait.ErrWaitTimeout {
128		return nil, wait.ErrWaitTimeout
129	}
130	if err != nil {
131		return nil, formatError("cannot watch on the certificate signing request: %v", err)
132	}
133
134	return event.Object.(*certificates.CertificateSigningRequest).Status.Certificate, nil
135}
136
137// ensureCompatible ensures that a CSR object is compatible with an original CSR
138func ensureCompatible(new, orig *certificates.CertificateSigningRequest, privateKey interface{}) error {
139	newCSR, err := parseCSR(new)
140	if err != nil {
141		return fmt.Errorf("unable to parse new csr: %v", err)
142	}
143	origCSR, err := parseCSR(orig)
144	if err != nil {
145		return fmt.Errorf("unable to parse original csr: %v", err)
146	}
147	if !reflect.DeepEqual(newCSR.Subject, origCSR.Subject) {
148		return fmt.Errorf("csr subjects differ: new: %#v, orig: %#v", newCSR.Subject, origCSR.Subject)
149	}
150	signer, ok := privateKey.(crypto.Signer)
151	if !ok {
152		return fmt.Errorf("privateKey is not a signer")
153	}
154	newCSR.PublicKey = signer.Public()
155	if err := newCSR.CheckSignature(); err != nil {
156		return fmt.Errorf("error validating signature new CSR against old key: %v", err)
157	}
158	if len(new.Status.Certificate) > 0 {
159		certs, err := certutil.ParseCertsPEM(new.Status.Certificate)
160		if err != nil {
161			return fmt.Errorf("error parsing signed certificate for CSR: %v", err)
162		}
163		now := time.Now()
164		for _, cert := range certs {
165			if now.After(cert.NotAfter) {
166				return fmt.Errorf("one of the certificates for the CSR has expired: %s", cert.NotAfter)
167			}
168		}
169	}
170	return nil
171}
172
173// formatError preserves the type of an API message but alters the message. Expects
174// a single argument format string, and returns the wrapped error.
175func formatError(format string, err error) error {
176	if s, ok := err.(errors.APIStatus); ok {
177		se := &errors.StatusError{ErrStatus: s.Status()}
178		se.ErrStatus.Message = fmt.Sprintf(format, se.ErrStatus.Message)
179		return se
180	}
181	return fmt.Errorf(format, err)
182}
183
184// parseCSR extracts the CSR from the API object and decodes it.
185func parseCSR(obj *certificates.CertificateSigningRequest) (*x509.CertificateRequest, error) {
186	// extract PEM from request object
187	block, _ := pem.Decode(obj.Spec.Request)
188	if block == nil || block.Type != "CERTIFICATE REQUEST" {
189		return nil, fmt.Errorf("PEM block type must be CERTIFICATE REQUEST")
190	}
191	return x509.ParseCertificateRequest(block.Bytes)
192}
193