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