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  }