Обновление файла
This commit is contained in:
@@ -7,11 +7,12 @@
|
||||
- Cookie PVEAuthCookie — если заданы PVE_USERNAME + PVE_PASSWORD
|
||||
3. Проксирует бинарный VNC-поток между браузером клиента и Proxmox.
|
||||
|
||||
Используется aiohttp вместо websockets, потому что:
|
||||
- Proxmox 8.x+ делает 302 Redirect с Location со схемой https:// вместо wss://,
|
||||
а websockets на uvloop не умеет правильно следовать за такими редиректами
|
||||
(raise InvalidURI "scheme isn't ws or wss").
|
||||
- aiohttp корректно обрабатывает HTTP→WS-редиректы с заменой scheme.
|
||||
Особенности Proxmox 8.x+:
|
||||
- При WebSocket-handshake на vncwebsocket Proxmox отвечает 302 Redirect
|
||||
на тот же URL, но со схемой https:// вместо wss://. aiohttp при
|
||||
перенаправлении теряет Cookie-заголовок (или не может следовать за
|
||||
редиректом без TLS-валидации). Поэтому делаем редирект вручную:
|
||||
перехватываем ответ, переписываем scheme, открываем WS сами.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@@ -76,7 +77,6 @@ async def console_ws(
|
||||
guest_path = "qemu" if guest_type == "vm" else "lxc"
|
||||
pve_host_only = settings.pve_host.replace("http://", "").replace("https://", "")
|
||||
|
||||
# Экранируем спецсимволы в тикете/порте (vncticket может содержать # / % :).
|
||||
safe_ticket = quote(ticket, safe="")
|
||||
safe_port = quote(str(port), safe="")
|
||||
upstream_url = (
|
||||
@@ -84,8 +84,6 @@ async def console_ws(
|
||||
f"?port={safe_port}&vncticket={safe_ticket}"
|
||||
)
|
||||
|
||||
# Определяем способ аутентификации: cookie приоритетнее (некоторые PVE
|
||||
# не принимают API-токен на WebSocket).
|
||||
headers: dict = {}
|
||||
auth = await _get_pve_auth_cookie()
|
||||
if auth:
|
||||
@@ -106,9 +104,38 @@ async def console_ws(
|
||||
upstream: Optional[aiohttp.ClientWebSocketResponse] = None
|
||||
session: Optional[ClientSession] = None
|
||||
try:
|
||||
# aiohttp сам следует за HTTP-редиректами и при этом корректно
|
||||
# меняет схему с https на wss (что websockets на uvloop не делает).
|
||||
session = ClientSession()
|
||||
# Отключаем автоматический redirect, чтобы самим обработать https→wss
|
||||
# с сохранением Cookie-заголовка.
|
||||
resp = await session.get(
|
||||
upstream_url,
|
||||
headers=headers,
|
||||
allow_redirects=False,
|
||||
ssl=False if not settings.pve_verify_ssl else None,
|
||||
)
|
||||
if resp.status in (301, 302, 303, 307, 308):
|
||||
location = resp.headers.get("Location", "")
|
||||
logger.info("console_ws: redirect to %s", location)
|
||||
if location.startswith("https://"):
|
||||
upstream_url = "wss://" + location[len("https://"):]
|
||||
elif location.startswith("http://"):
|
||||
upstream_url = "ws://" + location[len("http://"):]
|
||||
else:
|
||||
upstream_url = location
|
||||
await resp.release()
|
||||
elif resp.status == 101:
|
||||
# Proxmox уже сделал WS-handshake (редкий случай без редиректа).
|
||||
await resp.release()
|
||||
else:
|
||||
await resp.release()
|
||||
raise aiohttp.ClientResponseError(
|
||||
request_info=resp.request_info,
|
||||
history=resp.history,
|
||||
status=resp.status,
|
||||
message=f"unexpected status {resp.status}",
|
||||
headers=resp.headers,
|
||||
)
|
||||
|
||||
upstream = await session.ws_connect(
|
||||
upstream_url,
|
||||
headers=headers,
|
||||
|
||||
Reference in New Issue
Block a user