api/admin: make admin.vm.Console call go through qubesd
Ask qubesd for admin.vm.Console call. This allows to intercept it with admin-permission event. While at it, extract tty path extraction to python, where libvirt domain object is already available. Fixes QubesOS/qubes-issues#5030
This commit is contained in:
parent
c5aaf8cdd7
commit
b6c4f8456f
@ -1,11 +1,23 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# TODO: handle 'admin-permission' event for qubesd
|
|
||||||
|
|
||||||
lock="/var/run/qubes/$QREXEC_REQUESTED_TARGET.terminal.lock"
|
lock="/var/run/qubes/$QREXEC_REQUESTED_TARGET.terminal.lock"
|
||||||
|
|
||||||
qvm-check --quiet --running "$QREXEC_REQUESTED_TARGET" > /dev/null 2>&1 || { echo "Error: domain '$QREXEC_REQUESTED_TARGET' does not exist or is not running"; exit 1; }
|
# use temporary file, because env variables deal poorly with \0 inside
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
trap "rm -f $tmpfile" EXIT
|
||||||
|
qubesd-query -e \
|
||||||
|
"$QREXEC_REMOTE_DOMAIN" \
|
||||||
|
"admin.vm.Console" \
|
||||||
|
"$QREXEC_REQUESTED_TARGET" \
|
||||||
|
"$1" >$tmpfile
|
||||||
|
|
||||||
|
# exit if qubesd returned an error (not '0\0')
|
||||||
|
if [ "$(head -c 2 $tmpfile | xxd -p)" != "3000" ]; then
|
||||||
|
cat "$tmpfile"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
path=$(tail -c +3 "$tmpfile")
|
||||||
|
|
||||||
# Create an exclusive lock to ensure that multiple qubes cannot access to the same socket
|
# Create an exclusive lock to ensure that multiple qubes cannot access to the same socket
|
||||||
# In the case of multiple qrexec calls it returns a specific exit code
|
# In the case of multiple qrexec calls it returns a specific exit code
|
||||||
sudo flock -n -E 200 -x "$lock" socat - OPEN:"$(virsh -c xen ttyconsole "$QREXEC_REQUESTED_TARGET")"
|
sudo flock -n -E 200 -x "$lock" socat - OPEN:"$path"
|
||||||
|
@ -29,6 +29,7 @@ import string
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import libvirt
|
import libvirt
|
||||||
|
import lxml.etree
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@ -575,6 +576,23 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
|
|||||||
raise qubes.exc.QubesTagNotFoundError(self.dest, self.arg)
|
raise qubes.exc.QubesTagNotFoundError(self.dest, self.arg)
|
||||||
self.app.save()
|
self.app.save()
|
||||||
|
|
||||||
|
@qubes.api.method('admin.vm.Console', no_payload=True,
|
||||||
|
scope='local', write=True)
|
||||||
|
@asyncio.coroutine
|
||||||
|
def vm_console(self):
|
||||||
|
self.enforce(not self.arg)
|
||||||
|
|
||||||
|
self.fire_event_for_permission()
|
||||||
|
|
||||||
|
if not self.dest.is_running():
|
||||||
|
raise qubes.exc.QubesVMNotRunningError(self.dest)
|
||||||
|
|
||||||
|
xml_desc = lxml.etree.fromstring(self.dest.libvirt_domain.XMLDesc())
|
||||||
|
ttypath = xml_desc.xpath('string(/domain/devices/console/@tty)')
|
||||||
|
# this value is returned to /etc/qubes-rpc/admin.vm.Console script,
|
||||||
|
# which will call socat on it
|
||||||
|
return ttypath
|
||||||
|
|
||||||
@qubes.api.method('admin.pool.List', no_payload=True,
|
@qubes.api.method('admin.pool.List', no_payload=True,
|
||||||
scope='global', read=True)
|
scope='global', read=True)
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -2475,6 +2475,47 @@ class TC_00_VMs(AdminAPITestCase):
|
|||||||
b'test-vm1', b'private', b'abc')
|
b'test-vm1', b'private', b'abc')
|
||||||
self.assertFalse(self.app.save.called)
|
self.assertFalse(self.app.save.called)
|
||||||
|
|
||||||
|
def test_690_vm_console(self):
|
||||||
|
self.vm._libvirt_domain = unittest.mock.Mock()
|
||||||
|
xml_desc = (
|
||||||
|
'<domain type=\'xen\' id=\'42\'>\n'
|
||||||
|
'<name>test-vm1</name>\n'
|
||||||
|
'<devices>\n'
|
||||||
|
'<console type=\'pty\' tty=\'/dev/pts/42\'>\n'
|
||||||
|
'<source path=\'/dev/pts/42\'/>\n'
|
||||||
|
'<target type=\'xen\' port=\'0\'/>\n'
|
||||||
|
'</console>\n'
|
||||||
|
'</devices>\n'
|
||||||
|
'</domain>\n'
|
||||||
|
)
|
||||||
|
self.vm._libvirt_domain.configure_mock(
|
||||||
|
**{'XMLDesc.return_value': xml_desc,
|
||||||
|
'isActive.return_value': True}
|
||||||
|
)
|
||||||
|
self.app.vmm.configure_mock(offline_mode=False)
|
||||||
|
value = self.call_mgmt_func(b'admin.vm.Console', b'test-vm1')
|
||||||
|
self.assertEqual(value, '/dev/pts/42')
|
||||||
|
|
||||||
|
def test_691_vm_console_not_running(self):
|
||||||
|
self.vm._libvirt_domain = unittest.mock.Mock()
|
||||||
|
xml_desc = (
|
||||||
|
'<domain type=\'xen\' id=\'42\'>\n'
|
||||||
|
'<name>test-vm1</name>\n'
|
||||||
|
'<devices>\n'
|
||||||
|
'<console type=\'pty\' tty=\'/dev/pts/42\'>\n'
|
||||||
|
'<source path=\'/dev/pts/42\'/>\n'
|
||||||
|
'<target type=\'xen\' port=\'0\'/>\n'
|
||||||
|
'</console>\n'
|
||||||
|
'</devices>\n'
|
||||||
|
'</domain>\n'
|
||||||
|
)
|
||||||
|
self.vm._libvirt_domain.configure_mock(
|
||||||
|
**{'XMLDesc.return_value': xml_desc,
|
||||||
|
'isActive.return_value': False}
|
||||||
|
)
|
||||||
|
with self.assertRaises(qubes.exc.QubesVMNotRunningError):
|
||||||
|
self.call_mgmt_func(b'admin.vm.Console', b'test-vm1')
|
||||||
|
|
||||||
def test_990_vm_unexpected_payload(self):
|
def test_990_vm_unexpected_payload(self):
|
||||||
methods_with_no_payload = [
|
methods_with_no_payload = [
|
||||||
b'admin.vm.List',
|
b'admin.vm.List',
|
||||||
@ -2505,6 +2546,7 @@ class TC_00_VMs(AdminAPITestCase):
|
|||||||
b'admin.vm.Pause',
|
b'admin.vm.Pause',
|
||||||
b'admin.vm.Unpause',
|
b'admin.vm.Unpause',
|
||||||
b'admin.vm.Kill',
|
b'admin.vm.Kill',
|
||||||
|
b'admin.vm.Console',
|
||||||
b'admin.Events',
|
b'admin.Events',
|
||||||
b'admin.vm.feature.List',
|
b'admin.vm.feature.List',
|
||||||
b'admin.vm.feature.Get',
|
b'admin.vm.feature.Get',
|
||||||
@ -2549,6 +2591,7 @@ class TC_00_VMs(AdminAPITestCase):
|
|||||||
b'admin.vm.Pause',
|
b'admin.vm.Pause',
|
||||||
b'admin.vm.Unpause',
|
b'admin.vm.Unpause',
|
||||||
b'admin.vm.Kill',
|
b'admin.vm.Kill',
|
||||||
|
b'admin.vm.Console',
|
||||||
b'admin.Events',
|
b'admin.Events',
|
||||||
b'admin.vm.feature.List',
|
b'admin.vm.feature.List',
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user