Skip to content

Interactive mode ignores SSH keepalive settings, causing long-running sessions to disconnect #168

@inureyes

Description

@inureyes

Problem / Background

When users run long-running interactive sessions (such as tmux) via bssh's interactive mode, the connection silently drops after a period of inactivity. This is not a tmux issue — it is caused by two bugs in bssh's interactive mode implementation:

  1. Interactive mode ignores keepalive configuration — CLI options and SSH config file settings are not passed through.
  2. PTY session has no idle detection or health check mechanism — a dead connection causes the terminal to freeze indefinitely.

Root Cause Analysis

Root Cause 1: Interactive mode ignores keepalive configuration

The CLI options --server-alive-interval and --server-alive-count-max work correctly in exec mode but are completely ignored in interactive mode.

  • In exec mode (src/app/dispatcher.rs:461-467), SshConnectionConfig is properly built with keepalive settings and passed via Client::connect_with_ssh_config().
  • In interactive mode (src/commands/interactive/connection.rs:64), the code uses plain Client::connect() which does not accept SshConnectionConfig, so keepalive is hardcoded to defaults (60s interval, 3 max).
  • The InteractiveCommand struct in src/commands/interactive/types.rs does not have an ssh_connection_config field at all.
  • SSH config file (~/.ssh/config) settings for ServerAliveInterval and ServerAliveCountMax are also ignored in interactive mode.

Root Cause 2: PTY session has no idle detection or health check

The PTY session main loop in src/pty/session/session_manager.rs:305 uses tokio::select! with three branches:

  1. channel.wait() — waits for remote data
  2. msg_rx.recv() — waits for local input
  3. cancel_rx.changed() — waits for termination signal

There is no timeout branch and no periodic health check. When the SSH connection silently dies (e.g., firewall drops an idle connection), channel.wait() blocks forever and the terminal appears frozen to the user.

Disconnection scenario

  1. User connects via bssh interactive mode and runs tmux
  2. User is idle for some time
  3. Default keepalive: 60s interval x 3 max = 180s detection window
  4. If network/firewall idle timeout is shorter, or russh keepalive packets are not properly delivered, connection drops
  5. PTY session cannot detect the dead connection and hangs indefinitely on channel.wait()

Keepalive support comparison

Feature Exec Mode Interactive Mode
Keepalive applied Yes No (hardcoded 60s default)
CLI options honored Yes No (ignored)
SSH config honored Yes No (ignored)
Idle detection Limited None
Connection health check None None

Proposed Solution

Fix 1: Pass SshConnectionConfig to interactive mode connections

  • Add ssh_connection_config field to InteractiveCommand struct (src/commands/interactive/types.rs)
  • Change src/commands/interactive/connection.rs:64 to use Client::connect_with_ssh_config() instead of Client::connect()
  • Build keepalive config in dispatcher for interactive mode the same way as exec mode (src/app/dispatcher.rs)

Fix 2: Add health check timer to PTY session loop

  • Add a periodic timeout branch in the tokio::select! loop in src/pty/session/session_manager.rs:305
  • Detect dead connections and notify the user instead of freezing
  • Consider implementing a reconnection or graceful termination mechanism

Fix 3: Propagate keepalive settings to jump host connections in interactive mode

  • Interactive mode also does not pass SshConnectionConfig to JumpHostChain, so jump host connections in interactive mode use hardcoded defaults too

Acceptance Criteria

  • InteractiveCommand struct includes ssh_connection_config field
  • Interactive mode connections use Client::connect_with_ssh_config() with user-specified keepalive settings
  • CLI options --server-alive-interval and --server-alive-count-max are honored in interactive mode
  • SSH config file settings (ServerAliveInterval, ServerAliveCountMax) are honored in interactive mode
  • PTY session loop includes a periodic health check / timeout branch
  • Dead connections are detected and reported to the user instead of silently freezing
  • Jump host connections in interactive mode also receive keepalive configuration
  • Existing exec mode behavior remains unchanged (regression test)

Affected Files

  • src/commands/interactive/connection.rs — connection establishment
  • src/commands/interactive/types.rsInteractiveCommand struct
  • src/app/dispatcher.rs — config building for interactive mode
  • src/pty/session/session_manager.rs — PTY session loop
  • src/ssh/tokio_client/connection.rsSshConnectionConfig

Additional Context

This is a bug that affects all users of interactive mode with long-running sessions. The current workaround is limited — users cannot adjust keepalive settings to match their network environment when using interactive mode.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions