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  }