sshd "Unable to negotiate with …: no matching … found": What It Means
Published
The client and the server had no key exchange method, host key type, cipher or MAC in common, so sshd logged what the client offered and ended the connection before any account was named.
What it looks like
Oct 18 03:51:11 web1 sshd-session[48211]: Unable to negotiate with 203.0.113.7 port 54321: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1 [preauth]
- 1Time — When it was logged; this format has no year or time zone
- 2Host — The server’s host name
- 3Program — sshd-session, one process per connection, and its process ID: a connection’s lines share it
- 4Source address — The client’s IP address
- 5Source port — The client’s source port; with the address it identifies one connection
- 6Client offer — What the client offered; nothing in it was acceptable
- 7[preauth] — Logged before the user authenticated
Example lines as /var/log/auth.log shows them (/var/log/secure on RHEL and Fedora):
Oct 18 03:51:11 web1 sshd-session[48211]: Unable to negotiate with 203.0.113.7 port 54321: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1 [preauth] Oct 18 04:02:19 web1 sshd-session[48344]: Unable to negotiate with 198.51.100.22 port 40118: no matching host key type found. Their offer: ssh-rsa,ssh-dss [preauth] Oct 18 04:19:50 web1 sshd-session[48502]: Unable to negotiate with 192.0.2.44 port 51902: no matching cipher found. Their offer: aes128-cbc,3des-cbc,blowfish-cbc [preauth] Oct 18 05:03:27 web1 sshd-session[48711]: Unable to negotiate with 198.51.100.9 port 33110: no matching MAC found. Their offer: hmac-md5,hmac-sha1-96 [preauth]
On our own server, the 30 days to 25 October 2026
Unable to negotiate with …: …. Their offer: …
321
lines · about 11 a day · 160 source addresses
Last seen 25 October 2026
LogTriage's production API server (Hetzner, Germany): port 22 open to the internet, key-only since 6 October 2026 (password and keyboard-interactive authentication off); nginx in front of the API on ports 80 and 443. Counted weekly from the sshd journal; no address ever leaves the server. More in the monthly attack report; which free blocklists already list these attackers is measured in the IP blocklist report, and you can check the addresses in your own log.
Where the line comes from
Format strings as written in the source of OpenSSH 10.2p1 — the versions this server runs. Each links to the exact line.
-
Unable to negotiate with %s: %s. Their offer: %smessage · packet.c:2046 (OpenSSH) -
no matching key exchange method founderror text · ssherr.c:95 (OpenSSH) -
no matching host key type founderror text · ssherr.c:97 (OpenSSH) -
no matching cipher founderror text · ssherr.c:89 (OpenSSH) -
no matching MAC founderror text · ssherr.c:91 (OpenSSH)
What it means
sshd writes this line from the error handler in its packet layer, through logdie(): it logs the message and the connection ends there. Three values are filled in:
- The remote identifier — the client’s IP address and source port, the same identifier that other pre-authentication lines for that connection use. The
[preauth]tag marks the message as coming from the pre-authentication process, before any account exists. - The error text, from
ssh_err(). For this message it is one ofno matching key exchange method found,no matching host key type found,no matching cipher foundorno matching MAC found; the same table also hasno matching compression method foundfor the compression case. That text is the only part that tells you which component failed. - The client’s offer — the proposal list for the component that failed. It is the client’s list, never the server’s.
SSH negotiates each of these components separately during the KEXINIT exchange: both sides send a list, and the connection can only continue if the lists intersect for key exchange, host key signature algorithm, cipher and MAC. If any one has no overlap, sshd stops. On the server the four lists come from KexAlgorithms, HostKeyAlgorithms, Ciphers and MACs.
The gap that produces most of these lines is between supported and default. sshd_config(5) lists 3des-cbc, aes128-cbc and friends as supported ciphers, and hmac-md5, hmac-sha1 as supported MACs, but the default Ciphers set is [email protected], [email protected],[email protected], aes128-ctr,aes192-ctr,aes256-ctr, and the default KexAlgorithms set contains only the ML-KEM/sntrup hybrids, curve25519 and the three ECDH methods — no diffie-hellman-* at all. Likewise ssh-rsa and ssh-dss are absent from the default HostKeyAlgorithms, which starts at rsa-sha2-512,rsa-sha2-256 for RSA.
Is it an attack?
Almost never on its own, but the shape matters.
| Pattern | Reading |
|---|---|
Many single connections, many sources, ancient offers such as diffie-hellman-group1-sha1 | Internet-wide scanners and old bot clients — noise |
| One source, several connections in seconds, a different offer each time | Algorithm enumeration — a scanner mapping what your server accepts |
| One source, repeating on a schedule, same offer every time | Your own monitoring, backup or CI client that was never updated |
| A burst from internal addresses right after a change | A hardening change broke legitimate clients — an availability problem |
The same source later produces Accepted | Investigate the session; the negotiation failure itself is irrelevant |
Nothing in this line indicates stolen or guessed credentials: the connection never reached authentication, which is also why these keep appearing on a key-only server where PasswordAuthentication is off.
How to investigate
Split the lines by failing component first, then by source and offer.
# Last 24 hours (Debian/Ubuntu unit is ssh; RHEL/Fedora use -u sshd)
sudo journalctl -u ssh --since "24 hours ago" | grep "Unable to negotiate"
# Which component failed, and how often (/var/log/auth.log; RHEL/Fedora /var/log/secure)
sudo grep -oP 'Unable to negotiate with \S+ port \d+: \Kno matching .+ found' \
/var/log/auth.log | sort | uniq -c | sort -rn
# Busiest sources
sudo grep -oP 'Unable to negotiate with \K\S+' /var/log/auth.log \
| sort | uniq -c | sort -rn | head -20
# What those clients actually offered
sudo grep -oP 'Their offer: \K[^ ]+' /var/log/auth.log | sort | uniq -c | sort -rn | head -20
# The whole connection, by PID
sudo grep 'sshd-session\[48211\]' /var/log/auth.log
# What this server will accept right now
sudo sshd -T | grep -E '^(kexalgorithms|hostkeyalgorithms|ciphers|macs) '
# Everything this build of OpenSSH can do, per sshd_config(5)
ssh -Q KexAlgorithms; ssh -Q HostKeyAlgorithms; ssh -Q cipher; ssh -Q mac
Compare the offer column with the effective list: the intersection is empty, and the missing entry tells you whether the client is behind on key exchange, host keys, cipher or MAC. Private-address sources belong to you and need fixing; public ones are usually scanners. For an exported auth.log, the browser log analyzer groups the lines by source, and connections that die even earlier show up as kex_exchange_identification or Connection closed lines instead.
How to stop it
- Decide whose connections these are. If every source is external and each appears once, there is nothing to fix — the modern defaults are doing their job and the clients are obsolete.
- Fix the client, not the server. Upgrading the SSH client, library or appliance firmware removes the cause permanently.
- Only widen the server’s lists as a last resort. The manual describes prefixing a list with
+to append to the default set,-to remove from it and^to move entries to the head; appending a legacy cipher or host key algorithm applies to every client that connects, so treat it as a temporary, documented exception. - Cut the scanner volume at the network edge by restricting port 22 to known ranges or putting it behind a VPN or bastion. That removes these lines faster than any sshd setting.
- Do not expect penalties to suppress them.
PerSourcePenaltiesis enabled by default, but the documented categories cover crashes, unsuccessful authentication attempts, administratively refused connections, clients that disconnect without attempting authentication and clients that exceedLoginGraceTime— a failed algorithm negotiation is not one of the listed conditions. - Keep
LogLevelat the defaultINFO. These lines are cheap evidence that a legitimate client broke, and raising the bar on logging hides that.
sudo sshd -t && sudo systemctl reload ssh
Validate the configuration before reloading, and keep a working session open while you test a new one.
Lines you will see next to it
- kex_exchange_identification(struct ssh *ssh, int timeout_ms, sshd never finished the opening banner exchange with the client — the other end went away (or sent something unreadable) before sending its identification string, so the connection ended before key exchange and before any authentication.
- Connection closed by … The client at the other end of the SSH connection went away — closed it cleanly, or reset it — and sshd logged the address, port and (if the client had already named an account) the user it was trying before it quit.
- …Connection … sshd dropped the connection during the opening protocol banner exchange because what the client sent could not be parsed — almost always something that is not an SSH client at all.
Detect it
Frequently Asked Questions
- Does this line mean a login failed?
- No account was involved at all. Algorithm negotiation happens before authentication, and sshd ends the connection at the point of failure, so no user name, password or key was ever presented on that connection. Treat it as a connectivity or client-compatibility event, not a credential event.
- Why did these lines start appearing after an OpenSSH upgrade or a hardening change?
- The algorithms a server supports and the ones it offers by default are different lists, and the default set narrows over releases. A client that only speaks older algorithms — for example a host key type of ssh-rsa or ssh-dss, or a CBC cipher — now finds no overlap. The "Their offer" part of the line tells you exactly what that client proposed.
- A backup job or appliance of ours cannot connect any more. What do I change?
- Read the error text first, because it names the component that failed — key exchange, host key type, cipher or MAC — then compare the client's offer in the line with the server's effective list. Upgrading or reconfiguring the client is the right fix. sshd_config(5) does document adding algorithms to the default set with a leading '+' character, but re-enabling legacy algorithms weakens every session on the host, so prefer putting the old device behind a jump host.
- Should I alert on "Unable to negotiate" lines?
- Not one by one — on an internet-facing host most come from scanners and ancient clients, and none of them represent a successful or even attempted authentication. Alert instead on sudden volume from internal addresses, which usually means your own tooling broke after a configuration change, and investigate any source that later produces an Accepted line.
Run your own auth.log through it
Paste or drop /var/log/auth.log (or journalctl -u ssh output) into the free browser analyzer: brute force, password spraying, a successful login after failures and invalid-user sweeps are flagged with the evidence lines. Nothing is uploaded.