diff --git a/console/novncd b/console/novncd index 93ff728a..7ae4299d 100755 --- a/console/novncd +++ b/console/novncd @@ -4,7 +4,6 @@ # through an SSH tunnel or a local Unix socket. import argparse -import ipaddress import logging import os import socket @@ -23,6 +22,7 @@ from websockify import ProxyRequestHandler, WebSocketProxy # noqa: E402 from console.sshtunnels import open_tunnel, tunnel_target # noqa: E402 from vrtManager.connection import CONN_SOCKET, CONN_SSH # noqa: E402 +from vrtManager.util import is_loopback # noqa: E402 from webvirtcloud.settings import WS_HOST, WS_PORT # noqa: E402 @@ -77,14 +77,6 @@ def get_connection_infos(token): WILDCARD = (None, "", "0.0.0.0", "::") -def is_loopback(address): - """127.0.0.0/8, ::1 or localhost: reachable only on the compute itself.""" - try: - return ipaddress.ip_address(address).is_loopback - except ValueError: - return address == "localhost" - - class ConsoleClient: """What a console websocket is joined to; ConsoleProxyHandler is the websockify handler that runs it.""" diff --git a/console/tests/test_views.py b/console/tests/test_views.py index f76cce0c..10c08dec 100644 --- a/console/tests/test_views.py +++ b/console/tests/test_views.py @@ -270,12 +270,18 @@ class ConsoleViewsTestCase(TestCase): @patch("console.views.wvmInstance") def test_a_tcp_hosts_loopback_console_says_why_it_cannot_open(self, mock_wvm): + # the running VM's listener decides, as for novncd, not its next-start settings Compute.objects.filter(pk=self.compute.pk).update(type=1) - mock_wvm.return_value.get_console_type.return_value = "vnc" - mock_wvm.return_value.console_listens_locally.return_value = True + conn = mock_wvm.return_value + conn.get_console_type.return_value = "vnc" + conn.get_console_socket.return_value = None + conn.console_listens_locally.return_value = False # next start: all interfaces self.client.force_login(self.admin_user) - response = self.client.get(self.url) - self.assertContains(response, "This console listens only on its host") + for live, refused in (("127.0.0.1", True), ("::1", True), ("0.0.0.0", False), ("192.0.2.40", False)): + with self.subTest(live=live): + conn.get_console_listener_addr.return_value = live + response = self.client.get(self.url) + self.assertEqual("This console listens only on its host" in response.content.decode(), refused) @patch("console.views.wvmInstance") def test_both_pages_connect_to_novncd_behind_the_panels_own_address(self, mock_wvm): diff --git a/console/views.py b/console/views.py index f1c40e4c..007bf093 100644 --- a/console/views.py +++ b/console/views.py @@ -11,6 +11,7 @@ from instances.utils import can_open_console from libvirt import libvirtError from vrtManager.connection import CONN_TCP, CONN_TLS from vrtManager.instance import wvmInstance +from vrtManager.util import is_loopback def to_bool(val, default=False): @@ -57,8 +58,10 @@ def console(request, compute_id, uuid): console_passwd = conn.get_console_passwd() running = conn.instance.isActive() # novncd reaches a console that listens on its host's loopback only - # through SSH (or on this host) - unreachable = instance.compute.type in (CONN_TCP, CONN_TLS) and conn.console_listens_locally() + # through SSH (or on this host); the running VM's listener, as novncd sees it + unreachable = instance.compute.type in (CONN_TCP, CONN_TLS) and bool( + conn.get_console_socket() or is_loopback(conn.get_console_listener_addr()) + ) except (libvirtError, ValueError, IndexError, Instance.DoesNotExist): running = unreachable = False console_type = None diff --git a/vrtManager/util.py b/vrtManager/util.py index 30d7b6d2..db87625b 100644 --- a/vrtManager/util.py +++ b/vrtManager/util.py @@ -1,3 +1,4 @@ +import ipaddress import random import secrets import re @@ -224,3 +225,11 @@ def vol_driver_type(vol_format, pool_type=None): if pool_type == "disk" or vol_format in (None, "", "none", "unknown", "iso"): return "raw" return vol_format + + +def is_loopback(address): + """127.0.0.0/8, ::1 or localhost: reachable only on that host itself.""" + try: + return ipaddress.ip_address(address).is_loopback + except ValueError: + return address == "localhost"