Skip to content

fix: close exec channels to prevent SSH MaxSessions exhaustion - #14

Merged
chouzz merged 1 commit into
mainfrom
fix/ssh-channel-leak
Aug 19, 2026
Merged

fix: close exec channels to prevent SSH MaxSessions exhaustion#14
chouzz merged 1 commit into
mainfrom
fix/ssh-channel-leak

Conversation

@chouzz

@chouzz chouzz commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Problem

Repeated execute_command calls over the same SSH connection eventually fail with an SSH error (e.g. ChannelException: Administratively prohibited), after which the connection is unusable. This is sshd's MaxSessions protection kicking in.

Root cause

Each exec_command() call opens a new channel on the shared transport, but the channel was never closed after the command finished. Open sessions accumulate (default limit is 10) until the server rejects new channels.

Fix (src/remoteshell_mcp/ssh_client.py)

  1. Close the exec channel in a finally block after reading stdout/stderr/exit code, releasing the server-side session slot immediately
  2. Enable transport keepalive (default 30s, configurable via keepalive_interval) so idle connections survive firewall/sshd idle timeouts
  3. Reconnect-and-retry once when opening the channel fails (stale transport, exhausted sessions) — safe because the command has not started executing remotely

Tests

New tests/test_ssh_client.py:

  • Channel is closed after every execution
  • 20 consecutive commands succeed against a simulated MaxSessions=2 server (previously failed at command 3)
  • Keepalive is configured on connect
  • One reconnect+retry on SSHException; gives up cleanly after a second failure

All 12 tests pass.

Note: server-side alternative is raising MaxSessions in sshd_config, but closing channels client-side is the proper fix.

Repeated execute_command calls opened a new channel per command but
never closed it, so sessions accumulated until the server's MaxSessions
limit rejected new channels (ChannelException: Administratively
prohibited) and the connection became unusable.

- Close the exec channel in a finally block after reading output
- Enable transport keepalive (default 30s) to survive idle timeouts
- Retry once on channel-open failure by reconnecting first (safe: the
  command has not started executing on the remote yet)
- Add tests simulating a MaxSessions-limited server
@chouzz
chouzz merged commit 60424ad into main Aug 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant