github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/authenticationworker/worker_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package authenticationworker_test 5 6 import ( 7 "strings" 8 "time" 9 10 "github.com/juju/names/v5" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils/v3/ssh" 13 sshtesting "github.com/juju/utils/v3/ssh/testing" 14 "github.com/juju/worker/v3" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/agent" 18 "github.com/juju/juju/api" 19 "github.com/juju/juju/api/agent/keyupdater" 20 jujutesting "github.com/juju/juju/juju/testing" 21 "github.com/juju/juju/state" 22 coretesting "github.com/juju/juju/testing" 23 "github.com/juju/juju/worker/authenticationworker" 24 ) 25 26 type workerSuite struct { 27 jujutesting.JujuConnSuite 28 machine *state.Machine 29 keyupdaterAPI *keyupdater.State 30 31 existingEnvKey string 32 existingKeys []string 33 } 34 35 var _ = gc.Suite(&workerSuite{}) 36 37 func (s *workerSuite) SetUpTest(c *gc.C) { 38 s.JujuConnSuite.SetUpTest(c) 39 // Default ssh user is currently "ubuntu". 40 c.Assert(authenticationworker.SSHUser, gc.Equals, "ubuntu") 41 // Set the ssh user to empty (the current user) as required by the test infrastructure. 42 s.PatchValue(&authenticationworker.SSHUser, "") 43 44 // Replace the default dummy key in the test environment with a valid one. 45 // This will be added to the ssh authorised keys when the agent starts. 46 s.setAuthorisedKeys(c, sshtesting.ValidKeyOne.Key+" firstuser@host") 47 // Record the existing key with its prefix for testing later. 48 s.existingEnvKey = sshtesting.ValidKeyOne.Key + " Juju:firstuser@host" 49 50 // Set up an existing key (which is not in the environment) in the ssh authorised_keys file. 51 s.existingKeys = []string{sshtesting.ValidKeyTwo.Key + " existinguser@host"} 52 err := ssh.AddKeys(authenticationworker.SSHUser, s.existingKeys...) 53 c.Assert(err, jc.ErrorIsNil) 54 55 var apiRoot api.Connection 56 apiRoot, s.machine = s.OpenAPIAsNewMachine(c) 57 c.Assert(apiRoot, gc.NotNil) 58 s.keyupdaterAPI = keyupdater.NewState(apiRoot) 59 c.Assert(s.keyupdaterAPI, gc.NotNil) 60 } 61 62 func stop(c *gc.C, w worker.Worker) { 63 c.Assert(worker.Stop(w), gc.IsNil) 64 } 65 66 type mockConfig struct { 67 agent.Config 68 c *gc.C 69 tag names.Tag 70 } 71 72 func (mock *mockConfig) Tag() names.Tag { 73 return mock.tag 74 } 75 76 func agentConfig(c *gc.C, tag names.MachineTag) *mockConfig { 77 return &mockConfig{c: c, tag: tag} 78 } 79 80 func (s *workerSuite) setAuthorisedKeys(c *gc.C, keys ...string) { 81 keyStr := strings.Join(keys, "\n") 82 err := s.Model.UpdateModelConfig(map[string]interface{}{"authorized-keys": keyStr}, nil) 83 c.Assert(err, jc.ErrorIsNil) 84 } 85 86 func (s *workerSuite) waitSSHKeys(c *gc.C, expected []string) { 87 timeout := time.After(coretesting.LongWait) 88 for { 89 select { 90 case <-timeout: 91 c.Fatalf("timeout while waiting for authoirsed ssh keys to change") 92 case <-time.After(coretesting.ShortWait): 93 keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys) 94 c.Assert(err, jc.ErrorIsNil) 95 keysStr := strings.Join(keys, "\n") 96 expectedStr := strings.Join(expected, "\n") 97 if expectedStr != keysStr { 98 continue 99 } 100 return 101 } 102 } 103 } 104 105 func (s *workerSuite) TestKeyUpdateRetainsExisting(c *gc.C) { 106 authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag))) 107 c.Assert(err, jc.ErrorIsNil) 108 defer stop(c, authWorker) 109 110 newKey := sshtesting.ValidKeyThree.Key + " user@host" 111 s.setAuthorisedKeys(c, newKey) 112 newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host" 113 s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix)) 114 } 115 116 func (s *workerSuite) TestNewKeysInJujuAreSavedOnStartup(c *gc.C) { 117 newKey := sshtesting.ValidKeyThree.Key + " user@host" 118 s.setAuthorisedKeys(c, newKey) 119 120 authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag))) 121 c.Assert(err, jc.ErrorIsNil) 122 defer stop(c, authWorker) 123 124 newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host" 125 s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix)) 126 } 127 128 func (s *workerSuite) TestDeleteKey(c *gc.C) { 129 authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag))) 130 c.Assert(err, jc.ErrorIsNil) 131 defer stop(c, authWorker) 132 133 // Add another key 134 anotherKey := sshtesting.ValidKeyThree.Key + " another@host" 135 s.setAuthorisedKeys(c, s.existingEnvKey, anotherKey) 136 anotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:another@host" 137 s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey, anotherKeyWithCommentPrefix)) 138 139 // Delete the original key and check anotherKey plus the existing keys remain. 140 s.setAuthorisedKeys(c, anotherKey) 141 s.waitSSHKeys(c, append(s.existingKeys, anotherKeyWithCommentPrefix)) 142 } 143 144 func (s *workerSuite) TestMultipleChanges(c *gc.C) { 145 authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag))) 146 c.Assert(err, jc.ErrorIsNil) 147 defer stop(c, authWorker) 148 s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey)) 149 150 // Perform a set to add a key and delete a key. 151 // added: key 3 152 // deleted: key 1 (existing env key) 153 s.setAuthorisedKeys(c, sshtesting.ValidKeyThree.Key+" yetanother@host") 154 yetAnotherKeyWithComment := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host" 155 s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithComment)) 156 } 157 158 func (s *workerSuite) TestWorkerRestart(c *gc.C) { 159 authWorker, err := authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag))) 160 c.Assert(err, jc.ErrorIsNil) 161 defer stop(c, authWorker) 162 s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey)) 163 164 // Stop the worker and delete and add keys from the environment while it is down. 165 // added: key 3 166 // deleted: key 1 (existing env key) 167 stop(c, authWorker) 168 s.setAuthorisedKeys(c, sshtesting.ValidKeyThree.Key+" yetanother@host") 169 170 // Restart the worker and check that the ssh auth keys are as expected. 171 authWorker, err = authenticationworker.NewWorker(s.keyupdaterAPI, agentConfig(c, s.machine.Tag().(names.MachineTag))) 172 c.Assert(err, jc.ErrorIsNil) 173 defer stop(c, authWorker) 174 175 yetAnotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host" 176 s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithCommentPrefix)) 177 }