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

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 logging
import websockets
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Query
from fastapi import APIRouter, Query, WebSocket, WebSocketDisconnect
from ..config import settings
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/console", tags=["console"])
@@ -18,13 +33,12 @@ async def console_ws(
ticket: str = Query(...),
):
"""
Проксирует бинарный VNC-поток между браузером клиента и websocket-эндпоинтом
Proxmox (nodes/{node}/(qemu|lxc)/{vmid}/vncwebsocket). Тикет и порт берутся
из ответа /instances/{id}/console (см. instances.py -> pve.get_vnc_ticket).
Проксирует бинарный VNC-поток между браузером клиента и WebSocket-эндпоинтом
Proxmox (nodes/{node}/(qemu|lxc)/{vmid}/vncwebsocket).
Примечание: в зависимости от версии Proxmox и способа аутентификации
(API-токен vs cookie-тикет) может понадобиться донастройка заголовков —
см. README, раздел "Консоль VNC".
Если Proxmox отказывает в аутентификации — см. README, раздел «Консоль VNC».
Возможные варианты: обновить PVE, либо задать PVE_USERNAME/PVE_PASSWORD в .env
для cookie-авторизации через POST /access/ticket.
"""
await websocket.accept()
@@ -39,6 +53,11 @@ async def console_ws(
"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:
async with websockets.connect(
upstream_url,
@@ -46,6 +65,7 @@ async def console_ws(
subprotocols=["binary"],
ssl=None if settings.pve_verify_ssl else False,
) as upstream:
logger.info("console_ws: upstream connected")
async def client_to_upstream():
try:
@@ -53,7 +73,9 @@ async def console_ws(
data = await websocket.receive_bytes()
await upstream.send(data)
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():
try:
@@ -61,9 +83,21 @@ async def console_ws(
if isinstance(message, str):
message = message.encode()
await websocket.send_bytes(message)
except websockets.ConnectionClosed:
pass
except websockets.ConnectionClosed as exc:
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())
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
logger.exception("console_ws: ошибка подключения к Proxmox")
await websocket.close(code=1011, reason=str(exc)[:120])