libvirt: Add config option to require secure SPICE.
This patch adds the following SPICE-related configuration option to the 'spice' configuration group: - require_secure When set to true, libvirt will be provided with domain XML which configures SPICE VDI consoles to require secure connections (that is, connections protected by TLS). Attempts to connect without TLS will receive an error indicating they should retry the connection on the TLS port. Change-Id: Ica7083b0836f8d66cad8a4b4097613103fc91560
This commit is contained in:
parent
a2cc66a377
commit
e06890d101
@ -318,7 +318,6 @@ be told where to find them. This requires editing :file:`nova.conf` to set.
|
||||
vencrypt_client_cert=/etc/pki/nova-novncproxy/client-cert.pem
|
||||
vencrypt_ca_certs=/etc/pki/nova-novncproxy/ca-cert.pem
|
||||
|
||||
|
||||
.. _spice-console:
|
||||
|
||||
SPICE console
|
||||
@ -403,6 +402,10 @@ for SPICE consoles.
|
||||
- :oslo.config:option:`spice.playback_compression`
|
||||
- :oslo.config:option:`spice.streaming_mode`
|
||||
|
||||
As well as the following option to require that connections be protected by TLS:
|
||||
|
||||
- :oslo.config:option:`spice.require_secure`
|
||||
|
||||
.. _serial-console:
|
||||
|
||||
Serial console
|
||||
|
@ -194,6 +194,23 @@ Related options:
|
||||
* This option depends on the ``server_listen`` option.
|
||||
The proxy client must be able to access the address specified in
|
||||
``server_listen`` using the value of this option.
|
||||
"""),
|
||||
cfg.BoolOpt('require_secure',
|
||||
default=False,
|
||||
help="""
|
||||
Whether to require secure TLS connections to SPICE consoles.
|
||||
|
||||
If you're providing direct access to SPICE consoles instead of using the HTML5
|
||||
proxy, you may wish those connections to be encrypted. If so, set this value to
|
||||
True.
|
||||
|
||||
Note that use of secure consoles requires that you setup TLS certificates on
|
||||
each hypervisor.
|
||||
|
||||
Possible values:
|
||||
|
||||
* False: console traffic is not encrypted.
|
||||
* True: console traffic is required to be protected by TLS.
|
||||
"""),
|
||||
]
|
||||
|
||||
|
@ -1715,9 +1715,9 @@ class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest):
|
||||
obj.listen = "127.0.0.1"
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual(xml, """
|
||||
self.assertXmlEqual("""
|
||||
<graphics type="vnc" autoport="yes" keymap="en_US" listen="127.0.0.1"/>
|
||||
""")
|
||||
""", xml)
|
||||
|
||||
def test_config_graphics_spice(self):
|
||||
obj = config.LibvirtConfigGuestGraphics()
|
||||
@ -1726,6 +1726,18 @@ class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest):
|
||||
obj.keymap = "en_US"
|
||||
obj.listen = "127.0.0.1"
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual("""
|
||||
<graphics type="spice" autoport="no" keymap="en_US" listen="127.0.0.1"/>
|
||||
""", xml)
|
||||
|
||||
def test_config_graphics_spice_compression(self):
|
||||
obj = config.LibvirtConfigGuestGraphics()
|
||||
obj.type = "spice"
|
||||
obj.autoport = False
|
||||
obj.keymap = "en_US"
|
||||
obj.listen = "127.0.0.1"
|
||||
|
||||
obj.image_compression = "auto_glz"
|
||||
obj.jpeg_compression = "auto"
|
||||
obj.zlib_compression = "always"
|
||||
@ -1733,7 +1745,7 @@ class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest):
|
||||
obj.streaming_mode = "filter"
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual(xml, """
|
||||
self.assertXmlEqual("""
|
||||
<graphics type="spice" autoport="no" keymap="en_US" listen="127.0.0.1">
|
||||
<image compression="auto_glz"/>
|
||||
<jpeg compression="auto"/>
|
||||
@ -1741,7 +1753,64 @@ class LibvirtConfigGuestGraphicsTest(LibvirtConfigBaseTest):
|
||||
<playback compression="on"/>
|
||||
<streaming mode="filter"/>
|
||||
</graphics>
|
||||
""")
|
||||
""", xml)
|
||||
|
||||
def test_config_graphics_spice_secure(self):
|
||||
obj = config.LibvirtConfigGuestGraphics()
|
||||
obj.type = "spice"
|
||||
obj.autoport = False
|
||||
obj.keymap = "en_US"
|
||||
obj.listen = "127.0.0.1"
|
||||
|
||||
obj.secure = True
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual("""
|
||||
<graphics type="spice" autoport="no" keymap="en_US" listen="127.0.0.1">
|
||||
<channel name="main" mode="secure"/>
|
||||
<channel name="display" mode="secure"/>
|
||||
<channel name="inputs" mode="secure"/>
|
||||
<channel name="cursor" mode="secure"/>
|
||||
<channel name="playback" mode="secure"/>
|
||||
<channel name="record" mode="secure"/>
|
||||
<channel name="smartcard" mode="secure"/>
|
||||
<channel name="usbredir" mode="secure"/>
|
||||
</graphics>
|
||||
""", xml)
|
||||
|
||||
def test_config_graphics_spice_secure_compression(self):
|
||||
obj = config.LibvirtConfigGuestGraphics()
|
||||
obj.type = "spice"
|
||||
obj.autoport = False
|
||||
obj.keymap = "en_US"
|
||||
obj.listen = "127.0.0.1"
|
||||
|
||||
obj.secure = True
|
||||
|
||||
obj.image_compression = "auto_glz"
|
||||
obj.jpeg_compression = "auto"
|
||||
obj.zlib_compression = "always"
|
||||
obj.playback_compression = True
|
||||
obj.streaming_mode = "filter"
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual("""
|
||||
<graphics type="spice" autoport="no" keymap="en_US" listen="127.0.0.1">
|
||||
<image compression="auto_glz"/>
|
||||
<jpeg compression="auto"/>
|
||||
<zlib compression="always"/>
|
||||
<playback compression="on"/>
|
||||
<streaming mode="filter"/>
|
||||
<channel name="main" mode="secure"/>
|
||||
<channel name="display" mode="secure"/>
|
||||
<channel name="inputs" mode="secure"/>
|
||||
<channel name="cursor" mode="secure"/>
|
||||
<channel name="playback" mode="secure"/>
|
||||
<channel name="record" mode="secure"/>
|
||||
<channel name="smartcard" mode="secure"/>
|
||||
<channel name="usbredir" mode="secure"/>
|
||||
</graphics>
|
||||
""", xml)
|
||||
|
||||
|
||||
class LibvirtConfigGuestHostdev(LibvirtConfigBaseTest):
|
||||
|
@ -5897,6 +5897,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.assertIsNone(cfg.devices[3].zlib_compression)
|
||||
self.assertIsNone(cfg.devices[3].playback_compression)
|
||||
self.assertIsNone(cfg.devices[3].streaming_mode)
|
||||
self.assertFalse(cfg.devices[3].secure)
|
||||
|
||||
def test_get_guest_config_with_vnc_and_tablet(self):
|
||||
self.flags(enabled=True, group='vnc')
|
||||
@ -5932,6 +5933,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.assertIsNone(cfg.devices[3].zlib_compression)
|
||||
self.assertIsNone(cfg.devices[3].playback_compression)
|
||||
self.assertIsNone(cfg.devices[3].streaming_mode)
|
||||
self.assertFalse(cfg.devices[3].secure)
|
||||
self.assertEqual(cfg.devices[5].type, 'tablet')
|
||||
|
||||
def test_get_guest_config_with_spice_and_tablet(self):
|
||||
@ -5973,6 +5975,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.assertIsNone(cfg.devices[3].zlib_compression)
|
||||
self.assertIsNone(cfg.devices[3].playback_compression)
|
||||
self.assertIsNone(cfg.devices[3].streaming_mode)
|
||||
self.assertFalse(cfg.devices[3].secure)
|
||||
self.assertEqual(cfg.devices[5].type, 'tablet')
|
||||
|
||||
@mock.patch.object(host.Host, "_check_machine_type", new=mock.Mock())
|
||||
@ -6037,6 +6040,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.assertIsNone(cfg.devices[4].zlib_compression)
|
||||
self.assertIsNone(cfg.devices[4].playback_compression)
|
||||
self.assertIsNone(cfg.devices[4].streaming_mode)
|
||||
self.assertFalse(cfg.devices[4].secure)
|
||||
self.assertEqual(cfg.devices[5].type, video_type)
|
||||
|
||||
def test_get_guest_config_with_spice_compression(self):
|
||||
@ -6082,6 +6086,43 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.assertEqual(cfg.devices[3].zlib_compression, 'always')
|
||||
self.assertFalse(cfg.devices[3].playback_compression)
|
||||
self.assertEqual(cfg.devices[3].streaming_mode, 'all')
|
||||
self.assertFalse(cfg.devices[3].secure)
|
||||
|
||||
def test_get_guest_config_with_spice_secure(self):
|
||||
self.flags(enabled=False, group='vnc')
|
||||
self.flags(virt_type='kvm', group='libvirt')
|
||||
self.flags(enabled=True,
|
||||
agent_enabled=False,
|
||||
require_secure=True,
|
||||
server_listen='10.0.0.1',
|
||||
group='spice')
|
||||
self.flags(pointer_model='usbtablet')
|
||||
|
||||
cfg = self._get_guest_config_with_graphics()
|
||||
|
||||
self.assertEqual(len(cfg.devices), 9)
|
||||
self.assertIsInstance(cfg.devices[0],
|
||||
vconfig.LibvirtConfigGuestDisk)
|
||||
self.assertIsInstance(cfg.devices[1],
|
||||
vconfig.LibvirtConfigGuestDisk)
|
||||
self.assertIsInstance(cfg.devices[2],
|
||||
vconfig.LibvirtConfigGuestSerial)
|
||||
self.assertIsInstance(cfg.devices[3],
|
||||
vconfig.LibvirtConfigGuestGraphics)
|
||||
self.assertIsInstance(cfg.devices[4],
|
||||
vconfig.LibvirtConfigGuestVideo)
|
||||
self.assertIsInstance(cfg.devices[5],
|
||||
vconfig.LibvirtConfigGuestInput)
|
||||
self.assertIsInstance(cfg.devices[6],
|
||||
vconfig.LibvirtConfigGuestRng)
|
||||
self.assertIsInstance(cfg.devices[7],
|
||||
vconfig.LibvirtConfigGuestUSBHostController)
|
||||
self.assertIsInstance(cfg.devices[8],
|
||||
vconfig.LibvirtConfigMemoryBalloon)
|
||||
|
||||
self.assertEqual(cfg.devices[3].type, 'spice')
|
||||
self.assertEqual(cfg.devices[3].listen, '10.0.0.1')
|
||||
self.assertTrue(cfg.devices[3].secure)
|
||||
|
||||
@mock.patch.object(host.Host, 'get_guest')
|
||||
@mock.patch.object(libvirt_driver.LibvirtDriver,
|
||||
|
@ -2188,6 +2188,7 @@ class LibvirtConfigGuestGraphics(LibvirtConfigGuestDevice):
|
||||
self.autoport = True
|
||||
self.keymap = None
|
||||
self.listen = None
|
||||
self.secure = None
|
||||
|
||||
self.image_compression = None
|
||||
self.jpeg_compression = None
|
||||
@ -2222,6 +2223,11 @@ class LibvirtConfigGuestGraphics(LibvirtConfigGuestDevice):
|
||||
if self.streaming_mode is not None:
|
||||
dev.append(etree.Element(
|
||||
'streaming', mode=self.streaming_mode))
|
||||
if self.secure:
|
||||
for channel in ['main', 'display', 'inputs', 'cursor',
|
||||
'playback', 'record', 'smartcard', 'usbredir']:
|
||||
dev.append(etree.Element('channel', name=channel,
|
||||
mode='secure'))
|
||||
|
||||
return dev
|
||||
|
||||
|
@ -7692,6 +7692,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
graphics.zlib_compression = CONF.spice.zlib_compression
|
||||
graphics.playback_compression = CONF.spice.playback_compression
|
||||
graphics.streaming_mode = CONF.spice.streaming_mode
|
||||
graphics.secure = CONF.spice.require_secure
|
||||
guest.add_device(graphics)
|
||||
add_video_driver = True
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
This release adds a new config option require_secure to the spice
|
||||
configuration group. Defaulting to false to match the previous
|
||||
behavior, if set to true the SPICE consoles will require TLS
|
||||
protected connections. Unencrypted connections will be gracefully
|
||||
redirected to the TLS port via the SPICE protocol.
|
Loading…
Reference in New Issue
Block a user