github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/hostkeyreporter/worker.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package hostkeyreporter 5 6 import ( 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/worker/v3" 14 "github.com/juju/worker/v3/dependency" 15 "gopkg.in/tomb.v2" 16 17 "github.com/juju/juju/wrench" 18 ) 19 20 var logger = loggo.GetLogger("juju.worker.hostkeyreporter") 21 22 // Facade exposes controller functionality to a Worker. 23 type Facade interface { 24 ReportKeys(machineId string, publicKeys []string) error 25 } 26 27 // Config defines the parameters of the hostkeyreporter worker. 28 type Config struct { 29 Facade Facade 30 MachineId string 31 RootDir string 32 } 33 34 // Validate returns an error if Config cannot drive a hostkeyreporter. 35 func (config Config) Validate() error { 36 if config.Facade == nil { 37 return errors.NotValidf("nil Facade") 38 } 39 if config.MachineId == "" { 40 return errors.NotValidf("empty MachineId") 41 } 42 return nil 43 } 44 45 // New returns a Worker backed by config, or an error. 46 func New(config Config) (worker.Worker, error) { 47 if err := config.Validate(); err != nil { 48 return nil, errors.Trace(err) 49 } 50 w := &hostkeyreporter{config: config} 51 w.tomb.Go(w.run) 52 return w, nil 53 } 54 55 // Worker waits for a model migration to be active, then locks down the 56 // configured fortress and implements the migration. 57 type hostkeyreporter struct { 58 tomb tomb.Tomb 59 config Config 60 } 61 62 // Kill implements worker.Worker. 63 func (w *hostkeyreporter) Kill() { 64 w.tomb.Kill(nil) 65 } 66 67 // Wait implements worker.Worker. 68 func (w *hostkeyreporter) Wait() error { 69 return w.tomb.Wait() 70 } 71 72 func (w *hostkeyreporter) run() error { 73 if wrench.IsActive("hostkeyreporter", "delay") { 74 time.Sleep(time.Minute) 75 } 76 keys, err := w.readSSHKeys() 77 if err != nil { 78 return errors.Trace(err) 79 } 80 if len(keys) < 1 { 81 return errors.New("no SSH host keys found") 82 } 83 err = w.config.Facade.ReportKeys(w.config.MachineId, keys) 84 if err != nil { 85 return errors.Trace(err) 86 } 87 logger.Debugf("%d SSH host keys reported for machine %s", len(keys), w.config.MachineId) 88 return dependency.ErrUninstall 89 } 90 91 func (w *hostkeyreporter) readSSHKeys() ([]string, error) { 92 sshDir := w.sshDir() 93 _, err := os.Stat(sshDir) 94 if os.IsNotExist(err) { 95 logger.Errorf("%s doesn't exist - giving up", sshDir) 96 return nil, dependency.ErrUninstall 97 } 98 if err != nil { 99 return nil, errors.Trace(err) 100 } 101 102 filenames, err := filepath.Glob(sshDir + "/ssh_host_*_key.pub") 103 if err != nil { 104 return nil, errors.Trace(err) 105 } 106 keys := make([]string, 0, len(filenames)) 107 for _, filename := range filenames { 108 key, err := os.ReadFile(filename) 109 if err != nil { 110 logger.Debugf("unable to read SSH host key (skipping): %v", err) 111 continue 112 } 113 keys = append(keys, string(key)) 114 } 115 return keys, nil 116 } 117 118 func (w *hostkeyreporter) sshDir() string { 119 return filepath.Join(w.config.RootDir, "etc", "ssh") 120 }