Skip to content

Commit 47a3647

Browse files
committed
Fix: Logs access was not authenticated correctly
1 parent e8e23ed commit 47a3647

File tree

1 file changed

+38
-11
lines changed

1 file changed

+38
-11
lines changed

src/aleph/vm/orchestrator/views/operator.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,8 @@ def get_signed_operation(request: web.Request) -> SignedOperation:
164164
raise web.HTTPBadRequest(reason="Invalid X-SignedOperation format") from error
165165

166166

167-
async def authenticate_jwk(request: web.Request) -> str:
168-
signed_pubkey = get_signed_pubkey(request)
169-
signed_operation = get_signed_operation(request)
170-
167+
def verify_signed_operation(signed_operation: SignedOperation, signed_pubkey: SignedPubKeyHeader) -> str:
168+
"""Verify that the operation is signed by the ephemeral key authorized by the wallet."""
171169
if signed_pubkey.content.json_web_key.verify(
172170
data=signed_operation.payload,
173171
signature=signed_operation.signature,
@@ -176,7 +174,21 @@ async def authenticate_jwk(request: web.Request) -> str:
176174
logger.debug("Signature verified")
177175
return signed_pubkey.content.address
178176
else:
179-
raise web.HTTPUnauthorized(reason="Signature could not verified") from None
177+
raise web.HTTPUnauthorized(reason="Signature could not verified")
178+
179+
180+
async def authenticate_jwk(request: web.Request) -> str:
181+
"""Authenticate a request using the X-SignedPubKey and X-SignedOperation headers."""
182+
signed_pubkey = get_signed_pubkey(request)
183+
signed_operation = get_signed_operation(request)
184+
return verify_signed_operation(signed_operation, signed_pubkey)
185+
186+
187+
async def authenicate_websocket_message(message) -> str:
188+
"""Authenticate a websocket message since JS cannot configure headers on WebSockets."""
189+
signed_pubkey = SignedPubKeyHeader.parse_obj(message["X-SignedPubKey"])
190+
signed_operation = SignedOperation.parse_obj(message["X-SignedOperation"])
191+
return verify_signed_operation(signed_operation, signed_pubkey)
180192

181193

182194
def require_jwk_authentication(
@@ -219,24 +231,39 @@ def get_execution_or_404(ref: ItemHash, pool: VmPool) -> VmExecution:
219231
raise web.HTTPNotFound(body=f"No virtual machine with ref {ref}")
220232

221233

222-
@require_jwk_authentication
223-
async def stream_logs(request: web.Request, authenticated_sender: str) -> web.StreamResponse:
234+
async def stream_logs(request: web.Request) -> web.StreamResponse:
235+
"""Stream the logs of a VM.
236+
237+
The authentication method is slightly different because browsers do not
238+
allow Javascript to set headers in WebSocket requests.
239+
"""
224240
vm_hash = get_itemhash_or_400(request.match_info)
225241
pool: VmPool = request.app["vm_pool"]
226242
execution = get_execution_or_404(vm_hash, pool=pool)
227243

228244
if execution.vm is None:
229245
raise web.HTTPBadRequest(body=f"VM {vm_hash} is not running")
230246

231-
if execution.message.address != authenticated_sender:
232-
logger.debug(f"Unauthorized sender {authenticated_sender} for {vm_hash}")
233-
return web.Response(status=401, body="Unauthorized sender")
234-
235247
queue: asyncio.Queue = asyncio.Queue(maxsize=1000)
236248
try:
237249
ws = web.WebSocketResponse()
238250
await ws.prepare(request)
251+
239252
try:
253+
# Authentication
254+
first_message = await ws.receive_json()
255+
credentials = first_message["auth"]
256+
authenticated_sender = await authenicate_websocket_message(credentials)
257+
258+
if execution.message.address != authenticated_sender:
259+
logger.debug(f"Denied request to access logs by {authenticated_sender} on {vm_hash}")
260+
return web.Response(status=401, body="Unauthorized sender")
261+
else:
262+
logger.debug(f"Accepted request to access logs by {authenticated_sender} on {vm_hash}")
263+
await ws.send_json({"status": "failed", "reason": "unauthorized sender"})
264+
265+
await ws.send_json({"status": "connected"})
266+
240267
# Limit the number of queues per VM
241268
if len(execution.vm.fvm.log_queues) > 20:
242269
logger.warning("Too many log queues, dropping the oldest one")

0 commit comments

Comments
 (0)