Skip to content

fix(ssh): report text a server sends before its version line - #486

Open
100milliongold wants to merge 1 commit into
k0sproject:mainfrom
100milliongold:fix/ssh-pre-version-text
Open

100milliongold wants to merge 1 commit into
k0sproject:mainfrom
100milliongold:fix/ssh-pre-version-text

Conversation

@100milliongold

Copy link
Copy Markdown

Fixes #485

When a handshake fails before the server's version line arrives, the error now ends with what the server sent in its place:

ssh dial: ssh: handshake failed: EOF (server sent "Not allowed at this time" before any SSH version line)

That is how OpenSSH 9.8 and later say a connection was dropped by PerSourcePenalties or MaxStartups. x/crypto skips pre-version lines, so until now the error looked like a network fault.

Choices a reviewer may want to know about:

  • Only failures before any version line get the text. Once a version line has arrived, lines the server sent ahead of it are a legitimate notice and say nothing about a later failure.
  • The text is added after classifyHandshakeError, so a server cannot make an ordinary handshake failure look like ErrAuthFailed by sending ssh: unable to authenticate.
  • At most 255 bytes are kept, from the end, because the refusal is the last line sshd sends. %q keeps control characters away from the terminal.
  • It covers the direct, bastion and ProxyCommand handshakes, including the ProxyCommand branch that ends on the connect deadline.
  • It is best effort. sshd closes without reading the client's version string, so its close can turn into a reset, and a kernel may discard the text if the reset arrives before the client reads it. On macOS loopback the text was read every time; I have not checked Linux.

go test ./..., go test -race ./protocol/ssh/ and golangci-lint v2.13.1 pass. Each new integration test fails when its call site's annotate is removed.

This PR was written by an AI agent (Claude Code) at the request of @100milliongold. I cannot be re-invoked on my own, so @100milliongold owns the follow-up on review comments and CI.

OpenSSH 9.8 and later refuse a connection under PerSourcePenalties or
MaxStartups by writing "Not allowed at this time" where the version
string would go and hanging up. x/crypto skips pre-version lines, so the
handshake error said only EOF or "connection reset by peer" and read like
a network fault.

Keep up to 255 bytes of what arrives before the version line and add it
to the error when the handshake fails before any version line. The text
is added after classification, so it cannot turn a failure into
ErrAuthFailed.

Signed-off-by: Jea-Eok-Kim <gadian88@gmail.com>
@100milliongold
100milliongold force-pushed the fix/ssh-pre-version-text branch from a922c96 to 08ae617 Compare September 30, 2026 01:19
@kke
kke requested a balanced review from Copilot October 5, 2026 11:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Long individual lines retain their beginning rather than the intended diagnostic tail.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Improves SSH handshake diagnostics by preserving server text sent before the SSH version line.

Changes:

  • Adds a bounded, concurrency-safe pre-version recorder.
  • Integrates diagnostics into direct, bastion, and ProxyCommand connections.
  • Adds unit and integration coverage for diagnostics and error classification.
File Description
protocol/​ssh/​preversion.go Implements pre-version text recording and annotation.
protocol/​ssh/​preversion_test.go Tests recording, bounds, quoting, and version handling.
protocol/​ssh/​connection.go Integrates recording into direct and bastion handshakes.
protocol/​ssh/​connection_test.go Tests direct and bastion diagnostics.
protocol/​ssh/​proxy.go Integrates recording into ProxyCommand handshakes.
protocol/​ssh/​proxy_test.go Tests ProxyCommand failure and timeout diagnostics.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +68 to +70
if len(r.line) < maxPreVersionText {
r.line = append(r.line, char)
}
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.

ssh: handshake errors drop the text sshd sends before its version line

2 participants