Repository navigation
Fix crash when a container connects to an exposed host port with nothing listening - #1617
Merged
Merged
Conversation
…ing listening Forward SSH remote connections with ssh2 directly instead of ssh-remote-port-forward, closing the SSH channel when the host connection fails rather than leaving the socket error unhandled. Closes #1607
cristianrgreco
marked this pull request as ready for review
October 25, 2026 09:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TestContainers.exposeHostPortscrashed the test process with an uncaughtECONNREFUSEDwhen a container connected tohost.testcontainers.internal:<port>and nothing was listening on that host port. The container side of the connection also hung until its own timeout.The cause was in
ssh-remote-port-forward@1.0.4. For each forwarded connection it opens a host socket withnet.connectbut attaches noerrorlistener, and it never closes the SSH channel when that socket fails.That library is tiny and hasn't been touched since 2021, so this PR drops it and does the forwarding in
port-forwarder.tswithssh2directly.ssh2was already in the tree throughdocker-modem. The new code keeps the old behaviour:readyforwardInis used to expose a portreadyTimeout: 100_000is still passed when reusing a port forwarderIt also adds two error handlers:
Two smaller changes come with it:
ssh2assock. This lets usunref()it without reaching intossh2's private_sock, which the old library did.ready(for example, failing to reach the sshd container) now rejectexposeHostPorts. They used to be emitted on a client with noerrorlistener, which also crashed the process.Dependencies:
ssh-remote-port-forwardis replaced byssh2@^1.15.0(the lockfile keeps the1.15.0already installed).@types/ssh2@^1.15.6is added as a devDependency. It replaces the 0.5.x typings that the old library pulled in; those described the pre-1.0ssh2API.Verification
Regression test added to
port-forwarder.test.ts: it exposes a host port with no server behind it, then curls it from a container and expects exit code 52 (empty reply).Red, on the unfixed code:
Green, with the fix (
port-forwarder.test.tsandport-forwarder-reuse.test.ts):A plain Node script that calls
exposeHostPortsagainst the built package still exits as soon as it is done, so the unref'd SSH socket doesn't keep the process alive.npm run format,npm run lint,npm run knipandnpm run check-compilesall pass.Not breaking
No public API changes. The new runtime dependency
ssh2is CommonJS and was already installed throughdockerode→docker-modem.@types/ssh2is a devDependency, and the port forwarder's types aren't reachable fromindex.d.ts.Closes #1607