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