github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/renewer.go (about) 1 package ca 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/docker/go-events" 9 "github.com/docker/swarmkit/connectionbroker" 10 "github.com/docker/swarmkit/log" 11 "github.com/pkg/errors" 12 "github.com/sirupsen/logrus" 13 ) 14 15 // RenewTLSExponentialBackoff sets the exponential backoff when trying to renew TLS certificates that have expired 16 var RenewTLSExponentialBackoff = events.ExponentialBackoffConfig{ 17 Base: time.Second * 5, 18 Factor: time.Second * 5, 19 Max: 1 * time.Hour, 20 } 21 22 // TLSRenewer handles renewing TLS certificates, either automatically or upon 23 // request. 24 type TLSRenewer struct { 25 mu sync.Mutex 26 s *SecurityConfig 27 connBroker *connectionbroker.Broker 28 renew chan struct{} 29 expectedRole string 30 rootPaths CertPaths 31 } 32 33 // NewTLSRenewer creates a new TLS renewer. It must be started with Start. 34 func NewTLSRenewer(s *SecurityConfig, connBroker *connectionbroker.Broker, rootPaths CertPaths) *TLSRenewer { 35 return &TLSRenewer{ 36 s: s, 37 connBroker: connBroker, 38 renew: make(chan struct{}, 1), 39 rootPaths: rootPaths, 40 } 41 } 42 43 // SetExpectedRole sets the expected role. If a renewal is forced, and the role 44 // doesn't match this expectation, renewal will be retried with exponential 45 // backoff until it does match. 46 func (t *TLSRenewer) SetExpectedRole(role string) { 47 t.mu.Lock() 48 t.expectedRole = role 49 t.mu.Unlock() 50 } 51 52 // Renew causes the TLSRenewer to renew the certificate (nearly) right away, 53 // instead of waiting for the next automatic renewal. 54 func (t *TLSRenewer) Renew() { 55 select { 56 case t.renew <- struct{}{}: 57 default: 58 } 59 } 60 61 // Start will continuously monitor for the necessity of renewing the local certificates, either by 62 // issuing them locally if key-material is available, or requesting them from a remote CA. 63 func (t *TLSRenewer) Start(ctx context.Context) <-chan CertificateUpdate { 64 updates := make(chan CertificateUpdate) 65 66 go func() { 67 var ( 68 retry time.Duration 69 forceRetry bool 70 ) 71 expBackoff := events.NewExponentialBackoff(RenewTLSExponentialBackoff) 72 defer close(updates) 73 for { 74 ctx = log.WithModule(ctx, "tls") 75 log := log.G(ctx).WithFields(logrus.Fields{ 76 "node.id": t.s.ClientTLSCreds.NodeID(), 77 "node.role": t.s.ClientTLSCreds.Role(), 78 }) 79 // Our starting default will be 5 minutes 80 retry = 5 * time.Minute 81 82 // Since the expiration of the certificate is managed remotely we should update our 83 // retry timer on every iteration of this loop. 84 // Retrieve the current certificate expiration information. 85 validFrom, validUntil, err := readCertValidity(t.s.KeyReader()) 86 if err != nil { 87 // We failed to read the expiration, let's stick with the starting default 88 log.Errorf("failed to read the expiration of the TLS certificate in: %s", t.s.KeyReader().Target()) 89 90 select { 91 case updates <- CertificateUpdate{Err: errors.New("failed to read certificate expiration")}: 92 case <-ctx.Done(): 93 log.Info("shutting down certificate renewal routine") 94 return 95 } 96 } else { 97 // If we have an expired certificate, try to renew immediately: the hope that this is a temporary clock skew, or 98 // we can issue our own TLS certs. 99 if validUntil.Before(time.Now()) { 100 log.Warn("the current TLS certificate is expired, so an attempt to renew it will be made immediately") 101 // retry immediately(ish) with exponential backoff 102 retry = expBackoff.Proceed(nil) 103 } else if forceRetry { 104 // A forced renewal was requested, but did not succeed yet. 105 // retry immediately(ish) with exponential backoff 106 retry = expBackoff.Proceed(nil) 107 } else { 108 // Random retry time between 50% and 80% of the total time to expiration 109 retry = calculateRandomExpiry(validFrom, validUntil) 110 } 111 } 112 113 log.WithFields(logrus.Fields{ 114 "time": time.Now().Add(retry), 115 }).Debugf("next certificate renewal scheduled for %v from now", retry) 116 117 select { 118 case <-time.After(retry): 119 log.Info("renewing certificate") 120 case <-t.renew: 121 forceRetry = true 122 log.Info("forced certificate renewal") 123 124 // Pause briefly before attempting the renewal, 125 // to give the CA a chance to reconcile the 126 // desired role. 127 select { 128 case <-time.After(500 * time.Millisecond): 129 case <-ctx.Done(): 130 log.Info("shutting down certificate renewal routine") 131 return 132 } 133 case <-ctx.Done(): 134 log.Info("shutting down certificate renewal routine") 135 return 136 } 137 138 // ignore errors - it will just try again later 139 var certUpdate CertificateUpdate 140 if err := RenewTLSConfigNow(ctx, t.s, t.connBroker, t.rootPaths); err != nil { 141 certUpdate.Err = err 142 expBackoff.Failure(nil, nil) 143 } else { 144 newRole := t.s.ClientTLSCreds.Role() 145 t.mu.Lock() 146 expectedRole := t.expectedRole 147 t.mu.Unlock() 148 if expectedRole != "" && expectedRole != newRole { 149 expBackoff.Failure(nil, nil) 150 continue 151 } 152 153 certUpdate.Role = newRole 154 expBackoff.Success(nil) 155 forceRetry = false 156 } 157 158 select { 159 case updates <- certUpdate: 160 case <-ctx.Done(): 161 log.Info("shutting down certificate renewal routine") 162 return 163 } 164 } 165 }() 166 167 return updates 168 }