github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/utils/set" 12 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/cert" 15 "github.com/juju/juju/controller" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/watcher/legacy" 19 "github.com/juju/juju/worker" 20 ) 21 22 var logger = loggo.GetLogger("juju.worker.certupdater") 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 getter StateServingInfoGetter 32 setter StateServingInfoSetter 33 configGetter ControllerConfigGetter 34 hostPortsGetter APIHostPortsGetter 35 addresses []network.Address 36 } 37 38 // AddressWatcher is an interface that is provided to NewCertificateUpdater 39 // which can be used to watch for machine address changes. 40 type AddressWatcher interface { 41 WatchAddresses() state.NotifyWatcher 42 Addresses() (addresses []network.Address) 43 } 44 45 // ControllerConfigGetter is an interface that is provided to NewCertificateUpdater 46 // which can be used to get the controller config. 47 type ControllerConfigGetter interface { 48 ControllerConfig() (controller.Config, error) 49 } 50 51 // StateServingInfoGetter is an interface that is provided to NewCertificateUpdater 52 // whose StateServingInfo method will be invoked to get state serving info. 53 type StateServingInfoGetter interface { 54 StateServingInfo() (params.StateServingInfo, bool) 55 } 56 57 // StateServingInfoSetter defines a function that is called to set a 58 // StateServingInfo value with a newly generated certificate. 59 type StateServingInfoSetter func(info params.StateServingInfo, done <-chan struct{}) error 60 61 // APIHostPortsGetter is an interface that is provided to NewCertificateUpdater 62 // whose APIHostPorts method will be invoked to get controller addresses. 63 type APIHostPortsGetter interface { 64 APIHostPorts() ([][]network.HostPort, error) 65 } 66 67 // NewCertificateUpdater returns a worker.Worker that watches for changes to 68 // machine addresses and then generates a new controller certificate with those 69 // addresses in the certificate's SAN value. 70 func NewCertificateUpdater(addressWatcher AddressWatcher, getter StateServingInfoGetter, 71 configGetter ControllerConfigGetter, hostPortsGetter APIHostPortsGetter, setter StateServingInfoSetter, 72 ) worker.Worker { 73 return legacy.NewNotifyWorker(&CertificateUpdater{ 74 addressWatcher: addressWatcher, 75 configGetter: configGetter, 76 hostPortsGetter: hostPortsGetter, 77 getter: getter, 78 setter: setter, 79 }) 80 } 81 82 // SetUp is defined on the NotifyWatchHandler interface. 83 func (c *CertificateUpdater) SetUp() (state.NotifyWatcher, error) { 84 // Populate certificate SAN with any addresses we know about now. 85 apiHostPorts, err := c.hostPortsGetter.APIHostPorts() 86 if err != nil { 87 return nil, errors.Annotate(err, "retrieving initial server addesses") 88 } 89 var initialSANAddresses []network.Address 90 for _, server := range apiHostPorts { 91 for _, nhp := range server { 92 if nhp.Scope != network.ScopeCloudLocal { 93 continue 94 } 95 initialSANAddresses = append(initialSANAddresses, nhp.Address) 96 } 97 } 98 if err := c.updateCertificate(initialSANAddresses, make(chan struct{})); err != nil { 99 return nil, errors.Annotate(err, "setting initial cerificate SAN list") 100 } 101 // Return 102 return c.addressWatcher.WatchAddresses(), nil 103 } 104 105 // Handle is defined on the NotifyWatchHandler interface. 106 func (c *CertificateUpdater) Handle(done <-chan struct{}) error { 107 addresses := c.addressWatcher.Addresses() 108 if reflect.DeepEqual(addresses, c.addresses) { 109 // Sometimes the watcher will tell us things have changed, when they 110 // haven't as far as we can tell. 111 logger.Debugf("addresses haven't really changed since last updated cert") 112 return nil 113 } 114 return c.updateCertificate(addresses, done) 115 } 116 117 func (c *CertificateUpdater) updateCertificate(addresses []network.Address, done <-chan struct{}) error { 118 logger.Debugf("new machine addresses: %#v", addresses) 119 c.addresses = addresses 120 121 // Older Juju deployments will not have the CA cert private key 122 // available. 123 stateInfo, ok := c.getter.StateServingInfo() 124 if !ok { 125 return errors.New("no state serving info, cannot regenerate server certificate") 126 } 127 caPrivateKey := stateInfo.CAPrivateKey 128 if caPrivateKey == "" { 129 logger.Errorf("no CA cert private key, cannot regenerate server certificate") 130 return nil 131 } 132 133 cfg, err := c.configGetter.ControllerConfig() 134 if err != nil { 135 return errors.Annotate(err, "cannot read controller config") 136 } 137 138 // For backwards compatibility, we must include "anything", "juju-apiserver" 139 // and "juju-mongodb" as hostnames as that is what clients specify 140 // as the hostname for verification (this certicate is used both 141 // for serving MongoDB and API server connections). We also 142 // explicitly include localhost. 143 serverAddrs := []string{"localhost", "juju-apiserver", "juju-mongodb", "anything"} 144 for _, addr := range addresses { 145 if addr.Value == "localhost" { 146 continue 147 } 148 serverAddrs = append(serverAddrs, addr.Value) 149 } 150 newServerAddrs, update, err := updateRequired(stateInfo.Cert, serverAddrs) 151 if err != nil { 152 return errors.Annotate(err, "cannot determine if cert update needed") 153 } 154 if !update { 155 logger.Debugf("no certificate update required") 156 return nil 157 } 158 159 // Generate a new controller certificate with the machine addresses in the SAN value. 160 caCert, hasCACert := cfg.CACert() 161 if !hasCACert { 162 return errors.New("configuration has no ca-cert") 163 } 164 newCert, newKey, err := controller.GenerateControllerCertAndKey(caCert, caPrivateKey, newServerAddrs) 165 if err != nil { 166 return errors.Annotate(err, "cannot generate controller certificate") 167 } 168 stateInfo.Cert = string(newCert) 169 stateInfo.PrivateKey = string(newKey) 170 err = c.setter(stateInfo, done) 171 if err != nil { 172 return errors.Annotate(err, "cannot write agent config") 173 } 174 logger.Infof("controller cerificate addresses updated to %q", newServerAddrs) 175 return nil 176 } 177 178 // updateRequired returns true and a list of merged addresses if any of the 179 // new addresses are not yet contained in the server cert SAN list. 180 func updateRequired(serverCert string, newAddrs []string) ([]string, bool, error) { 181 x509Cert, err := cert.ParseCert(serverCert) 182 if err != nil { 183 return nil, false, errors.Annotate(err, "cannot parse existing TLS certificate") 184 } 185 existingAddr := set.NewStrings() 186 for _, ip := range x509Cert.IPAddresses { 187 existingAddr.Add(ip.String()) 188 } 189 logger.Debugf("existing cert addresses %v", existingAddr) 190 logger.Debugf("new addresses %v", newAddrs) 191 // Does newAddr contain any that are not already in existingAddr? 192 newAddrSet := set.NewStrings(newAddrs...) 193 update := newAddrSet.Difference(existingAddr).Size() > 0 194 newAddrSet = newAddrSet.Union(existingAddr) 195 return newAddrSet.SortedValues(), update, nil 196 } 197 198 // TearDown is defined on the NotifyWatchHandler interface. 199 func (c *CertificateUpdater) TearDown() error { 200 return nil 201 }