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