Обновление файла
This commit is contained in:
@@ -7,6 +7,12 @@
|
||||
- API-токен (заголовок `Authorization: PVEAPIToken=...`) — новые PVE ≥ 7.x
|
||||
- Cookie PVEAuthCookie — если заданы PVE_USERNAME + PVE_PASSWORD
|
||||
3. Проксирует бинарный VNC-поток между браузером клиента и Proxmox.
|
||||
|
||||
Особенности Proxmox:
|
||||
- PVE может вернуть HTTP redirect (302) на `https://...` вместо `wss://...`
|
||||
при WebSocket-handshake. Библиотека `websockets` не умеет следовать за
|
||||
такими redirect (https ≠ ws/wss), поэтому перехватываем redirect
|
||||
и переписываем scheme.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@@ -15,6 +21,8 @@ from typing import Optional, Tuple
|
||||
|
||||
import httpx
|
||||
import websockets
|
||||
from websockets.datastructures import Headers
|
||||
from websockets.http11 import Response
|
||||
from fastapi import APIRouter, Query, WebSocket, WebSocketDisconnect
|
||||
|
||||
from ..config import settings
|
||||
@@ -24,6 +32,23 @@ logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/console", tags=["console"])
|
||||
|
||||
|
||||
def _process_redirect(request, response: Response) -> Optional[str]:
|
||||
"""Перехватывает HTTP-redirect от Proxmox и переписывает https://→wss://.
|
||||
|
||||
Proxmox иногда возвращает Location со схемой https:// (хотя для WebSocket
|
||||
положено wss://). Библиотека websockets в этом случае падает с
|
||||
InvalidURI("scheme isn't ws or wss"). Возвращаем исправленный URL.
|
||||
"""
|
||||
location = response.headers.get("Location", "")
|
||||
if not location:
|
||||
return None
|
||||
if location.startswith("https://"):
|
||||
return "wss://" + location[len("https://"):]
|
||||
if location.startswith("http://"):
|
||||
return "ws://" + location[len("http://"):]
|
||||
return location
|
||||
|
||||
|
||||
async def _get_pve_auth_cookie() -> Optional[Tuple[str, str]]:
|
||||
"""Аутентифицируется в Proxmox по логину/паролю и возвращает (cookie_header, csrf_token).
|
||||
|
||||
@@ -76,14 +101,14 @@ async def console_ws(
|
||||
|
||||
# Определяем способ аутентификации.
|
||||
# Cookie-вариант приоритетнее, т.к. старые/строгие PVE не принимают API-токен на WS.
|
||||
headers: dict = {}
|
||||
headers: list = []
|
||||
auth = await _get_pve_auth_cookie()
|
||||
if auth:
|
||||
cookie_header, _ = auth
|
||||
headers["Cookie"] = cookie_header
|
||||
headers.append(("Cookie", cookie_header))
|
||||
auth_kind = "cookie"
|
||||
else:
|
||||
headers["Authorization"] = f"PVEAPIToken={settings.pve_token_name}={settings.pve_token_value}"
|
||||
headers.append(("Authorization", f"PVEAPIToken={settings.pve_token_name}={settings.pve_token_value}"))
|
||||
auth_kind = "api-token"
|
||||
|
||||
logger.info(
|
||||
@@ -94,9 +119,10 @@ async def console_ws(
|
||||
try:
|
||||
async with websockets.connect(
|
||||
upstream_url,
|
||||
extra_headers=headers,
|
||||
additional_headers=headers,
|
||||
subprotocols=["binary"],
|
||||
ssl=None if settings.pve_verify_ssl else False,
|
||||
process_redirect=_process_redirect,
|
||||
) as upstream:
|
||||
logger.info("console_ws: upstream connected")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user