Improve socket.io TLS alignment and logging

This commit is contained in:
2025-10-18 04:36:30 -06:00
parent 60a8cfcdc0
commit 10c43c431e

View File

@@ -839,39 +839,41 @@ class AgentHttpClient:
headers[_AGENT_CONTEXT_HEADER] = context_label headers[_AGENT_CONTEXT_HEADER] = context_label
return headers return headers
def configure_socketio(self, client: "socketio.AsyncClient") -> None: def configure_socketio(self, client: "socketio.AsyncClient") -> Dict[str, Any]:
"""Align the Socket.IO engine's TLS verification with the REST client.""" """Align Socket.IO TLS settings with the REST session and return connect kwargs."""
connect_kwargs: Dict[str, Any] = {}
try: try:
verify = getattr(self.session, "verify", True) verify = getattr(self.session, "verify", True)
engine = getattr(client, "eio", None) context = None
if engine is None:
return
# python-engineio accepts either a boolean, a path to a CA bundle,
# or an ``ssl.SSLContext`` for TLS verification. When we have a
# pinned certificate bundle on disk, prefer constructing a context
# that trusts that bundle, while also passing the explicit path so
# the engine falls back to the same trust store if it rebuilds the
# context internally. This ensures SYSTEM services that rely on
# machine-scoped certificates can negotiate WebSocket TLS without
# tripping over default CA roots.
if isinstance(verify, str) and os.path.isfile(verify): if isinstance(verify, str) and os.path.isfile(verify):
context = None
try: try:
context = ssl.create_default_context(cafile=verify) context = ssl.create_default_context(cafile=verify)
context.check_hostname = False context.check_hostname = False
except Exception: except Exception:
context = None context = None
engine.ssl_context = context if context is not None:
engine.ssl_verify = verify connect_kwargs["ssl_context"] = context
elif verify is False:
engine.ssl_context = None # The AsyncClient honours ``ssl_verify`` / ``ssl_context`` parameters
engine.ssl_verify = False # passed to ``connect``. For compatibility with older engineio builds
else: # we also reflect the values onto the underlying engine instance when
engine.ssl_context = None # available so reconnect attempts use the same trust material.
engine.ssl_verify = True connect_kwargs["ssl_verify"] = verify
engine = getattr(client, "eio", None)
if engine is not None:
try:
engine.ssl_context = connect_kwargs.get("ssl_context")
except Exception:
pass
try:
engine.ssl_verify = verify
except Exception:
pass
except Exception: except Exception:
pass connect_kwargs.setdefault("ssl_verify", True)
return connect_kwargs
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Enrollment & token management # Enrollment & token management
@@ -2615,7 +2617,11 @@ async def connect_loop():
while True: while True:
try: try:
client.ensure_authenticated() client.ensure_authenticated()
client.configure_socketio(sio) connect_kwargs = client.configure_socketio(sio) or {}
try:
setattr(sio, "connection_error", None)
except Exception:
pass
url = client.websocket_base_url() url = client.websocket_base_url()
print(f"[INFO] Connecting Agent to {url}...") print(f"[INFO] Connecting Agent to {url}...")
_log_agent(f'Connecting to {url}...') _log_agent(f'Connecting to {url}...')
@@ -2623,10 +2629,17 @@ async def connect_loop():
url, url,
transports=['websocket'], transports=['websocket'],
headers=client.auth_headers(), headers=client.auth_headers(),
**connect_kwargs,
) )
break break
except Exception as e: except Exception as e:
detail = _describe_exception(e) detail = _describe_exception(e)
try:
conn_err = getattr(sio, "connection_error", None)
except Exception:
conn_err = None
if conn_err:
detail = f"{detail}; connection_error={conn_err!r}"
print(f"[WebSocket] Server unavailable: {detail}. Retrying in {retry}s...") print(f"[WebSocket] Server unavailable: {detail}. Retrying in {retry}s...")
_log_agent(f'Server unavailable: {detail}', fname='agent.error.log') _log_agent(f'Server unavailable: {detail}', fname='agent.error.log')
await asyncio.sleep(retry) await asyncio.sleep(retry)