1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00
webvirtcloud/vrtManager/tests/test_agent_access.py
catborise 75fcc6f6c0 feat(instances): a running VM gets its root password and SSH key through its guest agent
The Access tab's Root Password and SSH Keys worked only on a shut-off
VM, through gstfsd on the compute. A running VM whose QEMU guest agent
is connected now gets them through the agent: guest-set-user-password
with the SHA-512 crypt hash, and guest-ssh-add-authorized-keys adding
the key to root's keys. A shut-off VM still goes through gstfsd; any
other VM is refused with "Shut the VM down, or start its QEMU guest
agent, and try again", and the buttons are disabled.

Each is one agent command with a 10 s timeout. libvirt's own calls for
them (setUserPassword, authorizedSSHKeysSet) wait for a hung agent
indefinitely, holding a worker and the VM's lock, and the domain's
agent timeout would change every other agent call of the VM too.

The agent's or libvirt's error is shown as is through error_text, so
users who do not see the hosts get no connection details, and the
attempt is logged as failed. The README names the SELinux boolean a
RHEL-family guest needs before its agent may add SSH keys.
2026-10-25 15:37:37 +03:00

49 lines
2.1 KiB
Python

"""A running guest's root password and SSH keys through its QEMU guest
agent: the password as a crypt(3) hash, the key added to those it has. Each
is one agent command with a timeout: libvirt's own calls for them would wait
for a hung agent indefinitely, and setting the domain's agent timeout would
change it for every other agent call too."""
import base64
import json
import unittest
from unittest.mock import MagicMock, patch
from django.conf import settings
if not settings.configured:
settings.configure(MAC_OUI="52:54:10")
from libvirt import libvirtError
from vrtManager import instance as module
from vrtManager.instance import wvmInstance
class AgentAccessTestCase(unittest.TestCase):
def setUp(self):
self.vm = wvmInstance.__new__(wvmInstance) # no connection: only these calls
self.vm.instance = MagicMock()
patcher = patch.object(module, "qemuAgentCommand", return_value='{"return":{}}')
self.agent = patcher.start()
self.addCleanup(patcher.stop)
def sent(self):
(dom, command, timeout, flags), = [c.args for c in self.agent.call_args_list]
self.assertIs(dom, self.vm.instance)
self.assertEqual((timeout, flags), (10, 0))
return json.loads(command)
def test_the_password_goes_as_a_hash(self):
self.vm.set_user_password("root", "$6$salt$hash")
self.assertEqual(self.sent(), {"execute": "guest-set-user-password", "arguments": {
"username": "root", "password": base64.b64encode(b"$6$salt$hash").decode(), "crypted": True}})
def test_the_key_is_added_to_the_users_keys(self):
self.vm.add_authorized_key("root", "ssh-ed25519 AAAA laptop")
self.assertEqual(self.sent(), {"execute": "guest-ssh-add-authorized-keys", "arguments": {
"username": "root", "keys": ["ssh-ed25519 AAAA laptop"], "reset": False}})
def test_the_agents_error_is_passed_on(self):
self.agent.side_effect = libvirtError("guest agent didn't respond to command within '10' seconds")
with self.assertRaisesRegex(libvirtError, "within '10' seconds"):
self.vm.set_user_password("root", "$6$salt$hash")