github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 12 "github.com/juju/juju/api/watcher" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/network" 16 "github.com/juju/juju/state" 17 "github.com/juju/juju/worker" 18 ) 19 20 var logger = loggo.GetLogger("juju.worker.certupdater") 21 22 // CertificateUpdater is responsible for generating state server certificates. 23 // 24 // In practice, CertificateUpdater is used by a state server's machine agent to watch 25 // that server's machines addresses in state, and write a new certificate to the 26 // agent's config file. 27 type CertificateUpdater struct { 28 addressWatcher AddressWatcher 29 getter StateServingInfoGetter 30 setter StateServingInfoSetter 31 configGetter EnvironConfigGetter 32 certChanged chan params.StateServingInfo 33 addresses []network.Address 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.Address) 41 } 42 43 // EnvironConfigGetter is an interface that is provided to NewCertificateUpdater 44 // which can be used to get environment config. 45 type EnvironConfigGetter interface { 46 EnvironConfig() (*config.Config, error) 47 } 48 49 // StateServingInfoGetter is an interface that is provided to NewCertificateUpdater 50 // whose StateServingInfo method will be invoked to get state serving info. 51 type StateServingInfoGetter interface { 52 StateServingInfo() (params.StateServingInfo, bool) 53 } 54 55 // StateServingInfoSetter defines a function that is called to set a 56 // StateServingInfo value with a newly generated certificate. 57 type StateServingInfoSetter func(info params.StateServingInfo, done <-chan struct{}) error 58 59 // NewCertificateUpdater returns a worker.Worker that watches for changes to 60 // machine addresses and then generates a new state server certificate with those 61 // addresses in the certificate's SAN value. 62 func NewCertificateUpdater(addressWatcher AddressWatcher, getter StateServingInfoGetter, 63 configGetter EnvironConfigGetter, setter StateServingInfoSetter, certChanged chan params.StateServingInfo, 64 ) worker.Worker { 65 return worker.NewNotifyWorker(&CertificateUpdater{ 66 addressWatcher: addressWatcher, 67 configGetter: configGetter, 68 getter: getter, 69 setter: setter, 70 certChanged: certChanged, 71 }) 72 } 73 74 // SetUp is defined on the NotifyWatchHandler interface. 75 func (c *CertificateUpdater) SetUp() (watcher.NotifyWatcher, error) { 76 return c.addressWatcher.WatchAddresses(), nil 77 } 78 79 // Handle is defined on the NotifyWatchHandler interface. 80 func (c *CertificateUpdater) Handle(done <-chan struct{}) error { 81 addresses := c.addressWatcher.Addresses() 82 logger.Debugf("new machine addresses: %#v", addresses) 83 if reflect.DeepEqual(addresses, c.addresses) { 84 // Sometimes the watcher will tell us things have changed, when they 85 // haven't as far as we can tell. 86 logger.Debugf("addresses haven't really changed since last updated cert") 87 return nil 88 } 89 c.addresses = addresses 90 91 // Older Juju deployments will not have the CA cert private key 92 // available. 93 stateInfo, ok := c.getter.StateServingInfo() 94 if !ok { 95 logger.Warningf("no state serving info, cannot regenerate server certificate") 96 return nil 97 } 98 caPrivateKey := stateInfo.CAPrivateKey 99 if caPrivateKey == "" { 100 logger.Warningf("no CA cert private key, cannot regenerate server certificate") 101 return nil 102 } 103 // Grab the env config and update a copy with ca cert private key. 104 envConfig, err := c.configGetter.EnvironConfig() 105 if err != nil { 106 return errors.Annotate(err, "cannot read environment config") 107 } 108 envConfig, err = envConfig.Apply(map[string]interface{}{"ca-private-key": caPrivateKey}) 109 if err != nil { 110 return errors.Annotate(err, "cannot add CA private key to environment config") 111 } 112 113 // For backwards compatibility, we must include "anything", "juju-apiserver" 114 // and "juju-mongodb" as hostnames as that is what clients specify 115 // as the hostname for verification (this certicate is used both 116 // for serving MongoDB and API server connections). We also 117 // explicitly include localhost. 118 serverAddrs := []string{"localhost", "juju-apiserver", "juju-mongodb", "anything"} 119 for _, addr := range addresses { 120 if addr.Value == "localhost" { 121 continue 122 } 123 serverAddrs = append(serverAddrs, addr.Value) 124 } 125 126 // Generate a new state server certificate with the machine addresses in the SAN value. 127 newCert, newKey, err := envConfig.GenerateStateServerCertAndKey(serverAddrs) 128 if err != nil { 129 return errors.Annotate(err, "cannot generate state server certificate") 130 } 131 stateInfo.Cert = string(newCert) 132 stateInfo.PrivateKey = string(newKey) 133 c.setter(stateInfo, done) 134 logger.Infof("State Server cerificate addresses updated to %q", addresses) 135 return nil 136 } 137 138 // TearDown is defined on the NotifyWatchHandler interface. 139 func (c *CertificateUpdater) TearDown() error { 140 close(c.certChanged) 141 return nil 142 }