When attempting an SSH connection, you encounter:
$ ssh user@server.example.com
ssh_exchange_identification: read: Connection reset by peerOr you might see:
$ ssh user@server.example.com
ssh_exchange_identification: Connection closed by remote hostOr sometimes:
banner exchange: Connection to 192.168.1.100 port 22: Connection timed outThese 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:
SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1If 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:
sudo systemctl status sshdIf it's not running:
sudo systemctl start sshd
sudo systemctl enable sshdCheck the process is listening:
sudo ss -tlnp | grep :22Test Raw Connection
Use telnet or netcat to see what the server is sending:
nc server.example.com 22Or:
telnet server.example.com 22A healthy SSH server responds immediately with:
SSH-2.0-OpenSSH_8.9p1If 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:
curl -v telnet://server.example.com:22If you see HTTP responses, the wrong service is on port 22.
Check what's listening:
sudo lsof -i :22
sudo netstat -tlnp | grep :22Check TCP Wrappers
TCP wrappers can silently reject connections during the initial handshake. Check:
cat /etc/hosts.denyLook for lines like:
ALL: ALL
sshd: ALLIf you find restrictive rules, check /etc/hosts.allow:
cat /etc/hosts.allowAdd your IP:
echo "sshd: $(who am i | awk '{print $NF}' | tr -d '()')" | sudo tee -a /etc/hosts.allowOr temporarily disable hosts.deny for testing:
sudo mv /etc/hosts.deny /etc/hosts.deny.bakCheck SSHD Configuration
On the server, verify the SSH daemon configuration:
sudo sshd -T | grep -i bannerCheck if a banner file is configured but missing:
sudo grep -i banner /etc/ssh/sshd_configIf Banner points to a non-existent file, create it or comment out the line:
sudo sed -i 's/^Banner/#Banner/' /etc/ssh/sshd_config
sudo systemctl restart sshdCheck MaxStartups
If many connections are being initiated simultaneously, the server might be rejecting new ones:
sudo grep MaxStartups /etc/ssh/sshd_configDefault is usually 10:30:100. Increase it:
echo "MaxStartups 100:30:200" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl restart sshdCheck for Load Balancer Issues
If connecting through a load balancer, it might not be properly forwarding SSH protocol. Test direct to backend:
ssh user@192.168.1.10 # Direct backend IPIf this works but the load balancer doesn't, check the LB configuration. Load balancers need to proxy TCP, not HTTP:
# 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 checkCheck Firewall Connection Tracking
Stateful firewalls can sometimes break the initial handshake. Check conntrack:
sudo conntrack -L | grep :22Or check iptables connection tracking:
sudo cat /proc/net/nf_conntrack | grep dport=22Temporarily disable connection tracking for SSH:
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPTCheck inetd or xinetd
Some systems use inetd/xinetd to manage SSH. Check if SSH is configured there:
cat /etc/xinetd.d/sshd 2>/dev/null || cat /etc/inetd.conf | grep sshIf found, make sure xinetd is running:
sudo systemctl status xinetdCheck DNS Resolution
Slow DNS can delay the banner. Disable DNS lookups:
echo "UseDNS no" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl restart sshdCheck GSSAPI Authentication
GSSAPI can cause delays during the handshake. Disable it:
sudo sed -i 's/^GSSAPIAuthentication.*/GSSAPIAuthentication no/' /etc/ssh/sshd_configOr add if not present:
echo "GSSAPIAuthentication no" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl restart sshdCheck for SELinux Issues
On SELinux-enabled systems, check for denials:
sudo ausearch -m avc -ts recent | grep sshdTemporarily set to permissive to test:
sudo setenforce 0If this fixes it, create appropriate SELinux policies.
Check Verbose SSH Output
Run with maximum verbosity:
ssh -vvv user@server.example.com 2>&1 | head -50Look for:
debug1: Remote protocol version 2.0, remote software version OpenSSH_8.9p1If 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:
ssh user@server.example.com -o ProxyCommand="nc -X 5 -x proxy.example.com:1080 %h %p"Or use a jump host:
ssh -J jumphost user@server.example.comResolution Checklist
- 1.Verify SSHD is running:
systemctl status sshd - 2.Test raw connection:
nc server 22 - 3.Check TCP wrappers:
/etc/hosts.denyand/etc/hosts.allow - 4.Verify MaxStartups configuration
- 5.Check for proxy/load balancer interference
- 6.Disable DNS lookups:
UseDNS no - 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.