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 }