Fix compute authentication for websocket endpoints

This commit is contained in:
grossmj
2023-10-22 12:44:39 +10:00
parent ffc49ea954
commit 44c066ac68
15 changed files with 699 additions and 210 deletions

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import logging
from fastapi import Request, Query, Depends, HTTPException, WebSocket, status
from fastapi.security import OAuth2PasswordBearer
@ -26,6 +26,7 @@ from gns3server.db.repositories.rbac import RbacRepository
from gns3server.services import auth_service
from .database import get_repository
log = logging.getLogger(__name__)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v3/access/users/login", auto_error=False)
@ -108,7 +109,9 @@ async def get_current_active_user_from_websocket(
return user
except HTTPException as e:
websocket_error = {"action": "log.error", "event": {"message": f"Could not authenticate while connecting to "
f"WebSocket: {e.detail}"}}
err_msg = f"Could not authenticate while connecting to controller WebSocket: {e.detail}"
websocket_error = {"action": "log.error", "event": {"message": err_msg}}
await websocket.send_json(websocket_error)
await websocket.close(code=1008)
log.error(err_msg)
return await websocket.close(code=1008)

View File

@ -29,6 +29,7 @@ from typing import List, Callable
from uuid import UUID
from gns3server.controller import Controller
from gns3server.config import Config
from gns3server.controller.node import Node
from gns3server.controller.project import Project
from gns3server.utils import force_unix_path
@ -510,16 +511,22 @@ async def post_file(file_path: str, request: Request, node: Node = Depends(dep_n
# FIXME: response with correct status code (from compute)
@router.websocket("/{node_id}/console/ws", dependencies=[Depends(has_privilege_on_websocket("Node.Console"))])
async def ws_console(websocket: WebSocket, node: Node = Depends(dep_node)) -> None:
@router.websocket("/{node_id}/console/ws")
async def ws_console(
websocket: WebSocket,
current_user: schemas.User = Depends(has_privilege_on_websocket("Node.Console")),
node: Node = Depends(dep_node)
) -> None:
"""
WebSocket console.
Required privilege: Node.Console
"""
if current_user is None:
return
compute = node.compute
await websocket.accept()
log.info(
f"New client {websocket.client.host}:{websocket.client.port} has connected to controller console WebSocket"
)
@ -557,9 +564,20 @@ async def ws_console(websocket: WebSocket, node: Node = Depends(dep_node)) -> No
try:
# receive WebSocket data from compute console WebSocket and forward to client.
async with HTTPClient.get_client().ws_connect(ws_console_compute_url) as ws_console_compute:
asyncio.ensure_future(ws_receive(ws_console_compute))
async for msg in ws_console_compute:
log.info(f"Forwarding console WebSocket to '{ws_console_compute_url}'")
server_config = Config.instance().settings.Server
user = server_config.compute_username
password = server_config.compute_password
if not user:
raise ControllerForbiddenError("Compute username is not set")
user = user.strip()
if user and password:
auth = aiohttp.BasicAuth(user, password.get_secret_value(), "utf-8")
else:
auth = aiohttp.BasicAuth(user, "")
async with HTTPClient.get_client().ws_connect(ws_console_compute_url, auth=auth) as ws:
asyncio.ensure_future(ws_receive(ws))
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
await websocket.send_text(msg.data)
elif msg.type == aiohttp.WSMsgType.BINARY: