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  }