Обновление файла

This commit is contained in:
2026-07-24 23:06:48 +03:00
parent 93038d4490
commit 1edd7cd555
+44 -10
View File
@@ -1,10 +1,25 @@
"""WebSocket-прокси для VNC-консоли.
Берёт у Proxmox одноразовый тикет через `vncproxy` (это работает с API-токеном),
затем проксирует бинарный поток от браузера к WebSocket-эндпоинту Proxmox.
В зависимости от версии Proxmox WebSocket-эндпоинт может требовать:
1. Заголовок `Authorization: PVEAPIToken=...` (новые версии PVE ≥ 7.x)
2. Cookie `PVEAuthCookie` (старые версии или строгие настройки)
В консоль пишется подробный лог, чтобы было видно, на каком этапе отваливается.
"""
import asyncio import asyncio
import logging
import websockets import websockets
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Query from fastapi import APIRouter, Query, WebSocket, WebSocketDisconnect
from ..config import settings from ..config import settings
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/console", tags=["console"]) router = APIRouter(prefix="/console", tags=["console"])
@@ -18,13 +33,12 @@ async def console_ws(
ticket: str = Query(...), ticket: str = Query(...),
): ):
""" """
Проксирует бинарный VNC-поток между браузером клиента и websocket-эндпоинтом Проксирует бинарный VNC-поток между браузером клиента и WebSocket-эндпоинтом
Proxmox (nodes/{node}/(qemu|lxc)/{vmid}/vncwebsocket). Тикет и порт берутся Proxmox (nodes/{node}/(qemu|lxc)/{vmid}/vncwebsocket).
из ответа /instances/{id}/console (см. instances.py -> pve.get_vnc_ticket).
Примечание: в зависимости от версии Proxmox и способа аутентификации Если Proxmox отказывает в аутентификации — см. README, раздел «Консоль VNC».
(API-токен vs cookie-тикет) может понадобиться донастройка заголовков — Возможные варианты: обновить PVE, либо задать PVE_USERNAME/PVE_PASSWORD в .env
см. README, раздел "Консоль VNC". для cookie-авторизации через POST /access/ticket.
""" """
await websocket.accept() await websocket.accept()
@@ -39,6 +53,11 @@ async def console_ws(
"Authorization": f"PVEAPIToken={settings.pve_token_name}={settings.pve_token_value}" "Authorization": f"PVEAPIToken={settings.pve_token_name}={settings.pve_token_value}"
} }
logger.info(
"console_ws: подключение к %s (node=%s, vmid=%s, guest=%s, port=%s)",
pve_host, node, vmid, guest_type, port,
)
try: try:
async with websockets.connect( async with websockets.connect(
upstream_url, upstream_url,
@@ -46,6 +65,7 @@ async def console_ws(
subprotocols=["binary"], subprotocols=["binary"],
ssl=None if settings.pve_verify_ssl else False, ssl=None if settings.pve_verify_ssl else False,
) as upstream: ) as upstream:
logger.info("console_ws: upstream connected")
async def client_to_upstream(): async def client_to_upstream():
try: try:
@@ -53,7 +73,9 @@ async def console_ws(
data = await websocket.receive_bytes() data = await websocket.receive_bytes()
await upstream.send(data) await upstream.send(data)
except WebSocketDisconnect: except WebSocketDisconnect:
pass logger.info("console_ws: клиент отключился")
except Exception as exc:
logger.warning("console_ws: client_to_upstream error: %s", exc)
async def upstream_to_client(): async def upstream_to_client():
try: try:
@@ -61,9 +83,21 @@ async def console_ws(
if isinstance(message, str): if isinstance(message, str):
message = message.encode() message = message.encode()
await websocket.send_bytes(message) await websocket.send_bytes(message)
except websockets.ConnectionClosed: except websockets.ConnectionClosed as exc:
pass logger.info("console_ws: upstream closed: %s", exc)
except Exception as exc:
logger.warning("console_ws: upstream_to_client error: %s", exc)
await asyncio.gather(client_to_upstream(), upstream_to_client()) await asyncio.gather(client_to_upstream(), upstream_to_client())
except websockets.exceptions.InvalidStatus as exc:
# Proxmox отверг соединение — скорее всего из-за аутентификации.
logger.error(
"console_ws: Proxmox отклонил подключение (status=%s). "
"Возможно, требуется cookie PVEAuthCookie вместо API-токена — "
"задайте PVE_USERNAME и PVE_PASSWORD в backend/.env",
exc.response.status_code if exc.response else "?",
)
await websocket.close(code=1011, reason=f"Proxmox auth failed: {exc.response.status_code if exc.response else exc}")
except Exception as exc: # noqa: BLE001 except Exception as exc: # noqa: BLE001
logger.exception("console_ws: ошибка подключения к Proxmox")
await websocket.close(code=1011, reason=str(exc)[:120]) await websocket.close(code=1011, reason=str(exc)[:120])