Skip to content
This repository was archived by the owner on Dec 16, 2025. It is now read-only.

Commit fa83193

Browse files
committed
fix ssh terminal
1 parent e8526c5 commit fa83193

File tree

2 files changed

+23
-16
lines changed

2 files changed

+23
-16
lines changed

src/lattice/routes/terminal/routes.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,11 @@ async def terminal_connect(
8686
async with active_sessions_lock:
8787
# TTL cleanup
8888
try:
89-
expired = [sid for sid, data in active_sessions.items() if (now - data.get("created_at", now)) > SESSION_TTL_SECONDS]
89+
expired = [
90+
sid
91+
for sid, data in active_sessions.items()
92+
if (now - data.get("created_at", now)) > SESSION_TTL_SECONDS
93+
]
9094
for sid in expired:
9195
active_sessions.pop(sid, None)
9296
except Exception:
@@ -137,8 +141,8 @@ async def terminal_websocket(
137141

138142
# Validate the session using auth utils
139143
if not session_cookie:
140-
print(f"WebSocket connection attempt without session cookie. Session ID: {session_id}")
141144
await websocket.close(code=1008, reason="Authentication required")
145+
print("WebSocket closed: Missing wos_session cookie")
142146
return
143147

144148
# Bind user from cookie and compare with session owner
@@ -148,28 +152,26 @@ async def terminal_websocket(
148152
print(f"Error validating session cookie: {str(e)}")
149153
await websocket.close(code=1008, reason="Authentication failed")
150154
return
151-
155+
152156
if not user:
153-
print(f"WebSocket closed: Invalid or expired session cookie. Session ID: {session_id}")
154157
await websocket.close(code=1008, reason="Authentication failed")
158+
print("WebSocket closed: Authentication failed")
155159
return
156160

157161
# Accept the WebSocket connection
158162
await websocket.accept()
159-
print(f"WebSocket connection accepted for session {session_id}, user {user.get('id')}")
160163

161164
# Validate session ID
162165
if session_id not in active_sessions:
163-
print(f"WebSocket closed: Invalid session ID {session_id}. Available sessions: {list(active_sessions.keys())[:5]}")
164166
await websocket.close(code=1008, reason="Invalid session")
167+
print(f"WebSocket closed: Invalid session ID {session_id}")
165168
return
166169

167170
session_data = active_sessions[session_id]
168171
# TTL check
169172
try:
170173
now = asyncio.get_event_loop().time()
171174
if (now - session_data.get("created_at", now)) > SESSION_TTL_SECONDS:
172-
print(f"WebSocket closed: Session {session_id} expired")
173175
await websocket.close(code=1008, reason="Session expired")
174176
async with active_sessions_lock:
175177
active_sessions.pop(session_id, None)
@@ -180,19 +182,25 @@ async def terminal_websocket(
180182
params = session_data["params"]
181183

182184
# Enforce that the websocket user matches the session owner
183-
if session_data.get("user_id") != user.get("id") or session_data.get("organization_id") != user.get("organization_id"):
184-
print(f"WebSocket closed: User mismatch. Session user: {session_data.get('user_id')}, WebSocket user: {user.get('id')}")
185+
if session_data.get("user_id") != user.get("id") or session_data.get(
186+
"organization_id"
187+
) != user.get("organization_id"):
188+
print("WebSocket closed: User mismatch.")
185189
await websocket.close(code=1008, reason="Unauthorized for session")
186190
return
187191

188192
ssh_cmd = ["ssh", params["cluster_name"]]
189-
190-
print(f"Starting SSH connection to {params['cluster_name']} for session {session_id}")
193+
194+
print(
195+
f"Starting SSH connection to {params['cluster_name']} for session {session_id}"
196+
)
191197

192198
# Schedule forced disconnect after TTL from session creation
193199
ttl_task = None
194200
try:
195-
created_at = float(session_data.get("created_at", asyncio.get_event_loop().time()))
201+
created_at = float(
202+
session_data.get("created_at", asyncio.get_event_loop().time())
203+
)
196204
now = asyncio.get_event_loop().time()
197205
remain = max(0.0, (SESSION_TTL_SECONDS - (now - created_at)))
198206

@@ -221,7 +229,7 @@ async def _ttl_watchdog(delay: float):
221229
await websocket.send_text(error_msg)
222230
await websocket.close(code=1011, reason="PTY creation failed")
223231
return
224-
232+
225233
try:
226234
process = await asyncio.create_subprocess_exec(
227235
*ssh_cmd,
@@ -241,7 +249,7 @@ async def _ttl_watchdog(delay: float):
241249
await websocket.send_text(error_msg)
242250
await websocket.close(code=1011, reason="SSH process failed")
243251
return
244-
252+
245253
os.close(slave_fd) # We don't need the slave fd in this process
246254
session_data["process"] = process
247255
session_data["master_fd"] = master_fd

src/lattice/routes/terminal/terminal.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,10 @@
6262
// Construct WebSocket URL dynamically based on current location
6363
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
6464
const wsUrl = `${protocol}//${window.location.host}/api/v1/terminal/ws/{{ session_id }}`;
65-
term.writeln(`Connecting to ${wsUrl}...\r\n`);
6665
const socket = new WebSocket(wsUrl);
6766

6867
socket.onopen = () => {
69-
term.writeln("\r\nConnected to {{ cluster_name }}");
68+
term.writeln("\r\nConnected to instance");
7069
term.onData((data) => {
7170
if (socket.readyState === WebSocket.OPEN) {
7271
socket.send(btoa(data)); // Encode input as Base64

0 commit comments

Comments
 (0)