istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/csrctrl/controllers/csr_controller.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // An example implementation of a CSR Controller. 16 package csrctrl 17 18 import ( 19 "fmt" 20 "strings" 21 "time" 22 23 certv1 "k8s.io/api/certificates/v1" 24 "k8s.io/apimachinery/pkg/types" 25 26 "istio.io/istio/pkg/kube" 27 "istio.io/istio/pkg/kube/controllers" 28 "istio.io/istio/pkg/kube/kclient" 29 "istio.io/istio/pkg/log" 30 "istio.io/istio/pkg/maps" 31 "istio.io/istio/pkg/test/csrctrl/signer" 32 "istio.io/istio/security/pkg/pki/util" 33 ) 34 35 const RequestLifeTimeAnnotationForCertManager = "experimental.cert-manager.io/request-duration" 36 37 type Signer struct { 38 csrs kclient.Client[*certv1.CertificateSigningRequest] 39 signers map[string]*signer.Signer 40 queue controllers.Queue 41 } 42 43 func NewSigner(cl kube.Client, signers map[string]*signer.Signer) *Signer { 44 c := &Signer{ 45 csrs: kclient.New[*certv1.CertificateSigningRequest](cl), 46 signers: signers, 47 } 48 c.queue = controllers.NewQueue("csr", 49 controllers.WithReconciler(c.Reconcile), 50 controllers.WithMaxAttempts(5)) 51 c.csrs.AddEventHandler(controllers.ObjectHandler(c.queue.AddObject)) 52 return c 53 } 54 55 func (s *Signer) Reconcile(key types.NamespacedName) error { 56 csr := s.csrs.Get(key.Name, key.Namespace) 57 if csr == nil { 58 // CSR was deleted, no action needed 59 return nil 60 } 61 62 var exist bool 63 signer, exist := s.signers[csr.Spec.SignerName] 64 switch { 65 case !csr.DeletionTimestamp.IsZero(): 66 log.Info("CSR has been deleted. Ignoring.") 67 case csr.Spec.SignerName == "": 68 log.Info("CSR does not have a signer name. Ignoring.") 69 case !exist: 70 log.Infof("CSR signer name does not match. Ignoring. signer-name: %s, have %v", csr.Spec.SignerName, strings.Join(maps.Keys(s.signers), ",")) 71 case csr.Status.Certificate != nil: 72 log.Info("CSR has already been signed. Ignoring.") 73 case !isCertificateRequestApproved(csr): 74 log.Info("CSR is not approved, Ignoring.") 75 default: 76 log.Info("Signing") 77 x509cr, err := util.ParsePemEncodedCSR(csr.Spec.Request) 78 if err != nil { 79 log.Infof("unable to parse csr: %v", err) 80 return nil 81 } 82 83 requestedLifeTime := signer.CertTTL 84 requestedDuration, ok := csr.Annotations[RequestLifeTimeAnnotationForCertManager] 85 if ok { 86 duration, err := time.ParseDuration(requestedDuration) 87 if err == nil { 88 requestedLifeTime = duration 89 } 90 } 91 cert, err := signer.Sign(x509cr, csr.Spec.Usages, requestedLifeTime, false) 92 if err != nil { 93 return fmt.Errorf("signing csr: %v", err) 94 } 95 csr.Status.Certificate = cert 96 if _, err := s.csrs.UpdateStatus(csr); err != nil { 97 return fmt.Errorf("patch: %v", err) 98 } 99 log.Infof("CSR %q has been signed", csr.Spec.SignerName) 100 } 101 return nil 102 } 103 104 func (s *Signer) Run(stop <-chan struct{}) { 105 kube.WaitForCacheSync("csr", stop, s.csrs.HasSynced) 106 s.queue.Run(stop) 107 } 108 109 func (s *Signer) HasSynced() bool { 110 return s.queue.HasSynced() 111 } 112 113 // isCertificateRequestApproved returns true if a certificate request has the 114 // "Approved" condition and no "Denied" conditions; false otherwise. 115 func isCertificateRequestApproved(csr *certv1.CertificateSigningRequest) bool { 116 approved, denied := getCertApprovalCondition(&csr.Status) 117 return approved && !denied 118 } 119 120 func getCertApprovalCondition(status *certv1.CertificateSigningRequestStatus) (approved bool, denied bool) { 121 for _, c := range status.Conditions { 122 if c.Type == certv1.CertificateApproved { 123 approved = true 124 } 125 if c.Type == certv1.CertificateDenied { 126 denied = true 127 } 128 } 129 return 130 }