When attempting an SSH connection, you encounter:

bash
$ ssh user@server.example.com
ssh_exchange_identification: read: Connection reset by peer

Or you might see:

bash
$ ssh user@server.example.com
ssh_exchange_identification: Connection closed by remote host

Or sometimes:

bash
banner exchange: Connection to 192.168.1.100 port 22: Connection timed out

These errors occur during the initial SSH protocol handshake, before authentication even begins. The server isn't sending the expected protocol identification string.

Understand the Protocol Handshake

When an SSH client connects, the server must immediately send an identification string like:

bash
SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1

If the server sends nothing, sends garbage, or disconnects before sending this string, you get an identification error.

Check if SSHD is Running

First, verify the SSH daemon is actually running:

bash
sudo systemctl status sshd

If it's not running:

bash
sudo systemctl start sshd
sudo systemctl enable sshd

Check the process is listening:

bash
sudo ss -tlnp | grep :22

Test Raw Connection

Use telnet or netcat to see what the server is sending:

bash
nc server.example.com 22

Or:

bash
telnet server.example.com 22

A healthy SSH server responds immediately with:

bash
SSH-2.0-OpenSSH_8.9p1

If you see nothing, the server isn't sending the banner. If you see HTML or another protocol, something else is listening on port 22.

Check for Port Redirection

Sometimes a web server or proxy is misconfigured on port 22:

bash
curl -v telnet://server.example.com:22

If you see HTTP responses, the wrong service is on port 22.

Check what's listening:

bash
sudo lsof -i :22
sudo netstat -tlnp | grep :22

Check TCP Wrappers

TCP wrappers can silently reject connections during the initial handshake. Check:

bash
cat /etc/hosts.deny

Look for lines like:

bash
ALL: ALL
sshd: ALL

If you find restrictive rules, check /etc/hosts.allow:

bash
cat /etc/hosts.allow

Add your IP:

bash
echo "sshd: $(who am i | awk '{print $NF}' | tr -d '()')" | sudo tee -a /etc/hosts.allow

Or temporarily disable hosts.deny for testing:

bash
sudo mv /etc/hosts.deny /etc/hosts.deny.bak

Check SSHD Configuration

On the server, verify the SSH daemon configuration:

bash
sudo sshd -T | grep -i banner

Check if a banner file is configured but missing:

bash
sudo grep -i banner /etc/ssh/sshd_config

If Banner points to a non-existent file, create it or comment out the line:

bash
sudo sed -i 's/^Banner/#Banner/' /etc/ssh/sshd_config
sudo systemctl restart sshd

Check MaxStartups

If many connections are being initiated simultaneously, the server might be rejecting new ones:

bash
sudo grep MaxStartups /etc/ssh/sshd_config

Default is usually 10:30:100. Increase it:

bash
echo "MaxStartups 100:30:200" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl restart sshd

Check for Load Balancer Issues

If connecting through a load balancer, it might not be properly forwarding SSH protocol. Test direct to backend:

bash
ssh user@192.168.1.10  # Direct backend IP

If this works but the load balancer doesn't, check the LB configuration. Load balancers need to proxy TCP, not HTTP:

bash
# HAProxy example for SSH
listen ssh
    bind *:22
    mode tcp
    option tcplog
    server backend1 192.168.1.10:22 check
    server backend2 192.168.1.11:22 check

Check Firewall Connection Tracking

Stateful firewalls can sometimes break the initial handshake. Check conntrack:

bash
sudo conntrack -L | grep :22

Or check iptables connection tracking:

bash
sudo cat /proc/net/nf_conntrack | grep dport=22

Temporarily disable connection tracking for SSH:

bash
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT

Check inetd or xinetd

Some systems use inetd/xinetd to manage SSH. Check if SSH is configured there:

bash
cat /etc/xinetd.d/sshd 2>/dev/null || cat /etc/inetd.conf | grep ssh

If found, make sure xinetd is running:

bash
sudo systemctl status xinetd

Check DNS Resolution

Slow DNS can delay the banner. Disable DNS lookups:

bash
echo "UseDNS no" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl restart sshd

Check GSSAPI Authentication

GSSAPI can cause delays during the handshake. Disable it:

bash
sudo sed -i 's/^GSSAPIAuthentication.*/GSSAPIAuthentication no/' /etc/ssh/sshd_config

Or add if not present:

bash
echo "GSSAPIAuthentication no" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl restart sshd

Check for SELinux Issues

On SELinux-enabled systems, check for denials:

bash
sudo ausearch -m avc -ts recent | grep sshd

Temporarily set to permissive to test:

bash
sudo setenforce 0

If this fixes it, create appropriate SELinux policies.

Check Verbose SSH Output

Run with maximum verbosity:

bash
ssh -vvv user@server.example.com 2>&1 | head -50

Look for:

bash
debug1: Remote protocol version 2.0, remote software version OpenSSH_8.9p1

If this line is missing or corrupted, the server isn't sending proper identification.

Test from Different Network

Connect from a different location to rule out local network issues:

bash
ssh user@server.example.com -o ProxyCommand="nc -X 5 -x proxy.example.com:1080 %h %p"

Or use a jump host:

bash
ssh -J jumphost user@server.example.com

Resolution Checklist

  1. 1.Verify SSHD is running: systemctl status sshd
  2. 2.Test raw connection: nc server 22
  3. 3.Check TCP wrappers: /etc/hosts.deny and /etc/hosts.allow
  4. 4.Verify MaxStartups configuration
  5. 5.Check for proxy/load balancer interference
  6. 6.Disable DNS lookups: UseDNS no
  7. 7.Check firewall connection tracking

Most identification string errors are caused by TCP wrappers blocking the connection or the wrong service listening on port 22. Start with a raw nc or telnet test to see what the server is actually sending.