github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/apiservercertwatcher/manifold.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apiservercertwatcher
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/worker/v3"
     9  	"github.com/juju/worker/v3/dependency"
    10  	"gopkg.in/tomb.v2"
    11  
    12  	"github.com/juju/juju/agent"
    13  	"github.com/juju/juju/pki"
    14  )
    15  
    16  type AuthorityWorker interface {
    17  	Authority() pki.Authority
    18  	worker.Worker
    19  }
    20  
    21  type NewCertWatcherWorker func(agent.Agent) (AuthorityWorker, error)
    22  
    23  type ManifoldConfig struct {
    24  	AgentName           string
    25  	CertWatcherWorkerFn NewCertWatcherWorker
    26  }
    27  
    28  // The manifold is intended to be a dependency for the apiserver.
    29  // Manifold provides a worker for supplying a pki Authority to other workers
    30  // that want to create and modify certificates in a Juju controller.
    31  func Manifold(config ManifoldConfig) dependency.Manifold {
    32  	return dependency.Manifold{
    33  		Inputs: []string{config.AgentName},
    34  		Start: func(context dependency.Context) (worker.Worker, error) {
    35  			var a agent.Agent
    36  			if err := context.Get(config.AgentName, &a); err != nil {
    37  				return nil, err
    38  			}
    39  
    40  			if config.CertWatcherWorkerFn != nil {
    41  				return config.CertWatcherWorkerFn(a)
    42  			}
    43  
    44  			w := &apiserverCertWatcher{
    45  				agent: a,
    46  			}
    47  			if err := w.setup(); err != nil {
    48  				return nil, errors.Annotate(err, "setting up initial ca authority")
    49  			}
    50  
    51  			w.tomb.Go(w.loop)
    52  			return w, nil
    53  		},
    54  		Output: outputFunc,
    55  	}
    56  }
    57  
    58  func outputFunc(in worker.Worker, out interface{}) error {
    59  	inWorker, _ := in.(AuthorityWorker)
    60  	if inWorker == nil {
    61  		return errors.Errorf("in should be a %T; got a %T", inWorker, in)
    62  	}
    63  	switch result := out.(type) {
    64  	case *pki.Authority:
    65  		*result = inWorker.Authority()
    66  	default:
    67  		return errors.Errorf("unexpected type")
    68  	}
    69  	return nil
    70  }
    71  
    72  type apiserverCertWatcher struct {
    73  	tomb      tomb.Tomb
    74  	agent     agent.Agent
    75  	authority pki.Authority
    76  }
    77  
    78  func (w *apiserverCertWatcher) loop() error {
    79  	select {
    80  	case <-w.tomb.Dying():
    81  		return tomb.ErrDying
    82  	}
    83  }
    84  
    85  func (w *apiserverCertWatcher) Authority() pki.Authority {
    86  	return w.authority
    87  }
    88  
    89  // Kill implements worker.Worker.
    90  func (w *apiserverCertWatcher) Kill() {
    91  	w.tomb.Kill(nil)
    92  }
    93  
    94  func (w *apiserverCertWatcher) setup() error {
    95  	config := w.agent.CurrentConfig()
    96  	info, ok := config.StateServingInfo()
    97  	if !ok {
    98  		return errors.New("no state serving info in agent config")
    99  	}
   100  
   101  	caCert := config.CACert()
   102  	if caCert == "" {
   103  		return errors.New("no ca certificate found in config")
   104  	}
   105  
   106  	caPrivateKey := info.CAPrivateKey
   107  	if caPrivateKey == "" {
   108  		return errors.New("no CA cert private key")
   109  	}
   110  
   111  	authority, err := pki.NewDefaultAuthorityPemCAKey([]byte(caCert),
   112  		[]byte(caPrivateKey))
   113  	if err != nil {
   114  		return errors.Annotate(err, "building authority from pem ca")
   115  	}
   116  
   117  	_, err = authority.LeafGroupFromPemCertKey(pki.DefaultLeafGroup,
   118  		[]byte(info.Cert), []byte(info.PrivateKey))
   119  	if err != nil {
   120  		return errors.Annotate(err, "loading default certificate for controller")
   121  	}
   122  
   123  	_, signers, err := pki.UnmarshalPemData([]byte(info.PrivateKey))
   124  	if err != nil {
   125  		return errors.Annotate(err, "setting default certificate signing key")
   126  	}
   127  	if len(signers) != 1 {
   128  		return errors.Annotate(err, "expected one signing key from certificate pem data")
   129  	}
   130  	authority.SetLeafSigner(signers[0])
   131  
   132  	w.authority = authority
   133  	return nil
   134  }
   135  
   136  // Wait implements worker.Worker.
   137  func (w *apiserverCertWatcher) Wait() error {
   138  	return w.tomb.Wait()
   139  }