1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-10 12:16:49 +00:00

feat(console): the VNC console uses the panel's own address

The console pages connect to /novncd/ on the address, scheme and port the
page itself was loaded from; nginx proxies it to novncd, which listens on
the loopback only. There is nothing to configure: the console works on
HTTPS (wss), behind another proxy and on any published port.

Before, the page connected to WS_PUBLIC_HOST/PORT/PATH, port 6080 by
default: straight to novncd, past nginx and without TLS, so an HTTPS panel
and a Docker container published on another port had no console until the
public port was set by hand, and novncd listened on every interface.

Gone: WS_PUBLIC_PORT, WS_PUBLIC_HOST, WS_PUBLIC_PATH and WS_CERT, the
installer's two public console questions and its 6080 firewall rule, the
Docker image's WS_PUBLIC_PORT and its init script lines, the manual
install's firewall rule for 6080 and nginx's unused /websockify location.
WS_HOST defaults to 127.0.0.1. A VM with a QEMU websocket port is still
reached on that port of the panel's host name (left for a separate
decision).
This commit is contained in:
catborise 2026-10-26 19:51:35 +03:00
parent 82af555d45
commit 68b104dfe4
14 changed files with 43 additions and 164 deletions

View file

@ -89,8 +89,6 @@ class ConsoleViewsTestCase(TestCase):
self.assertEqual(
unsigned, f"{self.compute.id}:{self.instance.uuid}:{self.admin_user.id}"
)
self.assertEqual(response.context["ws_path"], "novncd/")
self.assertEqual(response.context["ws_port"], 6080)
self.assertEqual(response.context["console_passwd"], "vncsecret")
@patch("console.views.wvmInstance")
@ -106,7 +104,7 @@ class ConsoleViewsTestCase(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "console-vnc-full.html")
self.assertEqual(response.context["ws_port"], 5900)
self.assertEqual(response.context["console_websocket_port"], 5900)
@patch("console.views.wvmInstance")
def test_the_lite_console_switches_scaling_from_its_menu(self, mock_wvm):
@ -247,7 +245,7 @@ class ConsoleViewsTestCase(TestCase):
self.assertNotContains(response, "attacker.example")
for name in ("host", "port", "path"):
self.assertNotContains(response, f"readQueryVariable('{name}'")
self.assertContains(response, "const host = 'testserver';")
self.assertContains(response, "const host = window.location.hostname;")
@patch("console.views.wvmInstance")
def test_the_page_sets_no_token_cookie(self, mock_wvm):
@ -278,60 +276,18 @@ class ConsoleViewsTestCase(TestCase):
self.assertTemplateNotUsed(response, "console-vnc-lite.html")
@patch("console.views.wvmInstance")
@override_settings(WS_PUBLIC_PATH="/novncd/")
def test_console_ws_path_normalization_leading_slash(self, mock_wvm):
mock_conn = MagicMock()
mock_conn.get_console_type.return_value = "vnc"
mock_wvm.return_value = mock_conn
def test_both_pages_connect_to_novncd_behind_the_panels_own_address(self, mock_wvm):
# nginx proxies /novncd/ on the panel's own scheme, host and port: no
# console address to configure, HTTPS gives wss
mock_wvm.return_value.get_console_type.return_value = "vnc"
mock_wvm.return_value.get_console_websocket_port.return_value = None
self.client.force_login(self.admin_user)
url = reverse("console") + f"?token={self.token}"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["ws_path"], "novncd/")
@patch("console.views.wvmInstance")
@override_settings(WS_PUBLIC_PATH="novncd/")
def test_console_ws_path_normalization_clean(self, mock_wvm):
mock_conn = MagicMock()
mock_conn.get_console_type.return_value = "vnc"
mock_wvm.return_value = mock_conn
self.client.force_login(self.admin_user)
url = reverse("console") + f"?token={self.token}"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["ws_path"], "novncd/")
@patch("console.views.wvmInstance")
@override_settings(WS_PUBLIC_PATH="/")
def test_console_ws_path_root_slash(self, mock_wvm):
mock_conn = MagicMock()
mock_conn.get_console_type.return_value = "vnc"
mock_wvm.return_value = mock_conn
self.client.force_login(self.admin_user)
url = reverse("console") + f"?token={self.token}"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["ws_path"], "")
@override_settings(ALLOWED_HOSTS=["panel.example.com"])
@patch("console.views.wvmInstance")
def test_console_ws_host_strip_port(self, mock_wvm):
mock_conn = MagicMock()
mock_conn.get_console_type.return_value = "vnc"
mock_wvm.return_value = mock_conn
self.client.force_login(self.admin_user)
url = reverse("console") + f"?token={self.token}"
response = self.client.get(url, HTTP_HOST="panel.example.com:8080")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["ws_host"], "panel.example.com")
lite = self.client.get(reverse("console") + f"?token={self.token}&view=lite")
self.assertContains(lite, "const host = window.location.hostname;")
self.assertContains(lite, "const path = 'novncd/';")
full = self.client.get(reverse("console") + f"?token={self.token}&view=full")
self.assertContains(full, "host: window.location.hostname,")
self.assertContains(full, "encrypt: https,")
@patch("console.views.wvmInstance")
def test_console_xss_protection_query_parameters(self, mock_wvm):