github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/certupdater/certupdater.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package certupdater 5 6 import ( 7 "reflect" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/worker/v3" 12 13 "github.com/juju/juju/controller" 14 "github.com/juju/juju/core/network" 15 "github.com/juju/juju/pki" 16 "github.com/juju/juju/state" 17 "github.com/juju/juju/watcher/legacy" 18 ) 19 20 var ( 21 logger = loggo.GetLogger("juju.worker.certupdater") 22 ) 23 24 // CertificateUpdater is responsible for generating controller certificates. 25 // 26 // In practice, CertificateUpdater is used by a controller's machine agent to watch 27 // that server's machines addresses in state, and write a new certificate to the 28 // agent's config file. 29 type CertificateUpdater struct { 30 addressWatcher AddressWatcher 31 authority pki.Authority 32 hostPortsGetter APIHostPortsGetter 33 addresses network.SpaceAddresses 34 } 35 36 // AddressWatcher is an interface that is provided to NewCertificateUpdater 37 // which can be used to watch for machine address changes. 38 type AddressWatcher interface { 39 WatchAddresses() state.NotifyWatcher 40 Addresses() (addresses network.SpaceAddresses) 41 } 42 43 // StateServingInfoGetter is an interface that is provided to NewCertificateUpdater 44 // whose StateServingInfo method will be invoked to get state serving info. 45 type StateServingInfoGetter interface { 46 StateServingInfo() (controller.StateServingInfo, bool) 47 } 48 49 // APIHostPortsGetter is an interface that is provided to NewCertificateUpdater. 50 // It returns all known API addresses. 51 type APIHostPortsGetter interface { 52 APIHostPortsForClients() ([]network.SpaceHostPorts, error) 53 } 54 55 // Config holds the configuration for the certificate updater worker. 56 type Config struct { 57 AddressWatcher AddressWatcher 58 Authority pki.Authority 59 APIHostPortsGetter APIHostPortsGetter 60 } 61 62 // NewCertificateUpdater returns a worker.Worker that watches for changes to 63 // machine addresses and then generates a new controller certificate with those 64 // addresses in the certificate's SAN value. 65 func NewCertificateUpdater(config Config) worker.Worker { 66 return legacy.NewNotifyWorker(&CertificateUpdater{ 67 addressWatcher: config.AddressWatcher, 68 authority: config.Authority, 69 hostPortsGetter: config.APIHostPortsGetter, 70 }) 71 } 72 73 // SetUp is defined on the NotifyWatchHandler interface. 74 func (c *CertificateUpdater) SetUp() (state.NotifyWatcher, error) { 75 // Populate certificate SAN with any addresses we know about now. 76 apiHostPorts, err := c.hostPortsGetter.APIHostPortsForClients() 77 if err != nil { 78 return nil, errors.Annotate(err, "retrieving initial server addresses") 79 } 80 var initialSANAddresses network.SpaceAddresses 81 for _, server := range apiHostPorts { 82 for _, nhp := range server { 83 if nhp.Scope != network.ScopeCloudLocal { 84 continue 85 } 86 initialSANAddresses = append(initialSANAddresses, nhp.SpaceAddress) 87 } 88 } 89 if err := c.updateCertificate(initialSANAddresses); err != nil { 90 return nil, errors.Annotate(err, "setting initial certificate SAN list") 91 } 92 return c.addressWatcher.WatchAddresses(), nil 93 } 94 95 // Handle is defined on the NotifyWatchHandler interface. 96 func (c *CertificateUpdater) Handle(done <-chan struct{}) error { 97 addresses := c.addressWatcher.Addresses() 98 if reflect.DeepEqual(addresses, c.addresses) { 99 // Sometimes the watcher will tell us things have changed, when they 100 // haven't as far as we can tell. 101 logger.Debugf("addresses haven't really changed since last updated cert") 102 return nil 103 } 104 return c.updateCertificate(addresses) 105 } 106 107 func (c *CertificateUpdater) updateCertificate(addresses network.SpaceAddresses) error { 108 logger.Debugf("new machine addresses: %#v", addresses) 109 c.addresses = addresses 110 111 request := c.authority.LeafRequestForGroup(pki.ControllerIPLeafGroup) 112 113 for _, addr := range addresses { 114 if addr.Value == "localhost" { 115 continue 116 } 117 118 switch addr.Type { 119 case network.HostName: 120 request.AddDNSNames(addr.Value) 121 case network.IPv4Address: 122 ip := addr.IP() 123 if ip == nil { 124 return errors.Errorf( 125 "value %s is not a valid ip address", addr.Value) 126 } 127 request.AddIPAddresses(ip) 128 case network.IPv6Address: 129 ip := addr.IP() 130 if ip == nil { 131 return errors.Errorf( 132 "value %s is not a valid ip address", addr.Value) 133 } 134 request.AddIPAddresses(ip) 135 default: 136 logger.Warningf( 137 "unsupported space address type %s for controller certificate", 138 addr.Type) 139 } 140 141 } 142 143 if _, err := request.Commit(); err != nil { 144 return errors.Annotate(err, "generating default controller ip certificate") 145 } 146 return nil 147 } 148 149 // TearDown is defined on the NotifyWatchHandler interface. 150 func (c *CertificateUpdater) TearDown() error { 151 return nil 152 }