Introduction

SSH connection refused on port 22 errors occur when clients cannot establish a TCP connection to the SSH daemon (sshd) on a remote server, resulting in immediate connection rejection. This differs from SSH connection timeouts (network unreachable) and authentication failures (wrong credentials). Connection refused indicates the TCP SYN packet reached the host but was rejected because no service is listening on port 22, a firewall actively rejected the connection, or network-level access controls blocked the attempt. Common causes include SSH daemon (sshd) not running or crashed, SSH daemon listening on non-standard port, firewall blocking port 22 (iptables, firewalld, ufw, Windows Firewall), cloud security group rules blocking SSH, network ACLs rejecting SSH traffic, TCP wrappers (/etc/hosts.deny) blocking access, SSH daemon misconfiguration preventing startup, port 22 in use by another process, host firewall rate limiting blocking repeated connections, and SSH daemon listening only on specific interfaces (not 0.0.0.0). The fix requires understanding SSH daemon configuration, Linux networking, firewall rules, cloud security groups, and diagnostic tools. This guide provides production-proven troubleshooting for SSH connection refused errors across Linux servers (RHEL/CentOS, Ubuntu/Debian, SUSE), Windows Server with OpenSSH, and cloud environments (AWS, Azure, GCP).

Symptoms

  • ssh: connect to host port 22: Connection refused
  • ssh: Could not resolve hostname (if DNS also failing)
  • ssh: Connection closed by remote host
  • PuTTY: "Server refused connection" or "Network error: Connection refused"
  • telnet hostname 22 returns connection refused
  • nc -zv hostname 22 shows connection refused
  • SSH works locally but not from remote hosts
  • SSH works from some networks but not others
  • Intermittent SSH connection failures
  • All ports appear filtered from external scan

Common Causes

  • SSH daemon (sshd) not running
  • SSH daemon listening on different port
  • Firewall blocking port 22 (local or network)
  • Cloud security group missing SSH rule
  • Network ACL blocking port 22
  • TCP wrappers (/etc/hosts.deny) blocking
  • SSH daemon bound to localhost only
  • Host firewall rate limiting (fail2ban, iptables)
  • Port 22 occupied by different service
  • SSH daemon configuration prevents startup

Step-by-Step Fix

### 1. Diagnose SSH connection

Test SSH connectivity:

```bash # Test SSH connection ssh -v user@hostname

# Verbose output shows connection stage: # debug1: Connecting to hostname [192.168.1.100] port 22. # debug1: connect to address 192.168.1.100 port 22: Connection refused

# Test with telnet telnet hostname 22 # Trying 192.168.1.100... # telnet: Unable to connect to remote host: Connection refused

# Test with netcat nc -zv hostname 22 # nc: connect to hostname port 22 (tcp) failed: Connection refused

# Test with nmap (from external host) nmap -p 22 hostname # PORT STATE SERVICE # 22/tcp filtered ssh # Or: # 22/tcp closed ssh ```

Check from multiple locations:

```bash # Test from different networks # - Same subnet (local network) # - Different VLAN # - External network (internet) # - Cloud console serial console

# Use online port checker # https://www.yougetsignal.com/tools/open-ports/

# Or from command line (if curl available) curl -v telnet://hostname:22 ```

### 2. Check SSH daemon status

Verify sshd is running:

```bash # Check service status (systemd) systemctl status sshd # Or on some systems: systemctl status ssh

# Expected output: # ● ssh.service - OpenBSD Secure Shell server # Loaded: loaded (/lib/systemd/system/ssh.service; enabled) # Active: active (running) since Mon 2024-01-15 10:00:00 UTC

# If not running, start it: sudo systemctl start sshd sudo systemctl enable sshd # Start on boot ```

Restart SSH daemon:

```bash # Restart sshd sudo systemctl restart sshd

# Check for errors during startup sudo journalctl -u sshd -n 50 --no-pager

# Common startup errors: # - Missing host keys # - Invalid configuration # - Port already in use # - Permission denied on key files ```

Check SSH daemon listening:

```bash # Check if sshd is listening sudo ss -tlnp | grep :22 # Or: sudo netstat -tlnp | grep :22

# Expected output: # LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))

# Or with IPv6: # LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=1234,fd=4))

# If nothing on port 22, check all ports: sudo ss -tlnp | grep sshd

# May be listening on different port ```

Check SSH daemon configuration:

```bash # Check SSH port configuration sudo grep -i "^Port" /etc/ssh/sshd_config # Default: Port 22 # May be changed to non-standard port like 2222

# Check listen address sudo grep -i "^ListenAddress" /etc/ssh/sshd_config # Default: 0.0.0.0 (all interfaces) # If 127.0.0.1, only accepts local connections

# Validate configuration sudo sshd -t

# If no output, configuration is valid # If errors, fix them before restarting ```

### 3. Fix SSH daemon configuration

Restore SSH default configuration:

```bash # Backup current configuration sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Common working configuration: sudo cat > /etc/ssh/sshd_config << 'EOF' # Basic SSH Configuration Port 22 Protocol 2 ListenAddress 0.0.0.0

# Authentication PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication yes PermitEmptyPasswords no

# Security X11Forwarding no MaxAuthTries 3 ClientAliveInterval 300 ClientAliveCountMax 2

# Logging SyslogFacility AUTH LogLevel INFO

# Host keys HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_ecdsa_key HostKey /etc/ssh/ssh_host_ed25519_key EOF

# Test configuration sudo sshd -t

# Restart SSH daemon sudo systemctl restart sshd ```

Regenerate SSH host keys:

```bash # Check if host keys exist sudo ls -la /etc/ssh/ssh_host_*_key

# If missing, regenerate: # Debian/Ubuntu: sudo dpkg-reconfigure openssh-server

# RHEL/CentOS: sudo rm /etc/ssh/ssh_host_*_key* sudo systemctl restart sshd # Keys auto-generated on startup

# Or manually: sudo ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N "" sudo ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N "" sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""

# Set correct permissions sudo chmod 600 /etc/ssh/ssh_host_*_key sudo chmod 644 /etc/ssh/ssh_host_*_key.pub sudo chown root:root /etc/ssh/ssh_host_*_key*

# Restart sshd sudo systemctl restart sshd ```

Change SSH port (if 22 is blocked):

```bash # Edit sshd_config sudo sed -i 's/^Port 22/Port 2222/' /etc/ssh/sshd_config

# Or add new port (keeps both) echo "Port 2222" | sudo tee -a /etc/ssh/sshd_config

# Update firewall (see firewall section) # Restart sshd sudo systemctl restart sshd

# Verify listening sudo ss -tlnp | grep sshd # Should show both ports if both configured ```

### 4. Configure local firewall

Check iptables rules:

```bash # View current iptables rules sudo iptables -L -n -v --line-numbers

# Look for SSH rule (port 22) sudo iptables -L -n | grep 22

# If no rule allowing port 22, add one: sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT

# Save rules (Debian/Ubuntu) sudo iptables-save > /etc/iptables/rules.v4

# Or RHEL/CentOS sudo service iptables save # Or: sudo /usr/libexec/iptables/iptables.init save ```

Check firewalld (RHEL/CentOS/Fedora):

```bash # Check firewalld status sudo systemctl status firewalld

# List zones and services sudo firewall-cmd --list-all

# Check if SSH service is allowed sudo firewall-cmd --list-services | grep ssh

# Add SSH service sudo firewall-cmd --permanent --add-service=ssh sudo firewall-cmd --reload

# Or add specific port sudo firewall-cmd --permanent --add-port=22/tcp sudo firewall-cmd --reload

# Verify sudo firewall-cmd --list-services ```

Check ufw (Ubuntu/Debian):

```bash # Check ufw status sudo ufw status verbose

# If inactive, enable it sudo ufw enable

# Allow SSH sudo ufw allow ssh # Or: sudo ufw allow 22/tcp

# Reload ufw sudo ufw reload

# Verify sudo ufw status | grep 22 ```

Check Windows Firewall (Windows Server with OpenSSH):

```powershell # Check firewall rules Get-NetFirewallRule -DisplayName "*OpenSSH*"

# Allow SSH New-NetFirewallRule -Name 'OpenSSH-Inbound-TCP' -DisplayName 'OpenSSH Server (inbound)' -Description 'Allows inbound TCP traffic to OpenSSH server' -Enabled True -Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow -Profile Any

# Or enable via netsh netsh advfirewall firewall add rule name="OpenSSH Server" dir=in action=allow protocol=TCP localport=22 ```

### 5. Configure cloud security groups

AWS EC2 security group:

```bash # Using AWS CLI # Get instance security group INSTANCE_ID="i-0abc123def456" SG_ID=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID \ --query 'Reservations[0].Instances[0].SecurityGroups[0].GroupId' \ --output text)

# Add SSH rule aws ec2 authorize-security-group-ingress \ --group-id $SG_ID \ --protocol tcp \ --port 22 \ --cidr 0.0.0.0/0

# Or restrict to specific IP aws ec2 authorize-security-group-ingress \ --group-id $SG_ID \ --protocol tcp \ --port 22 \ --cidr YOUR_IP/32

# Verify rules aws ec2 describe-security-groups --group-ids $SG_ID \ --query 'SecurityGroups[0].IpPermissions' ```

Azure Network Security Group:

```bash # Using Azure CLI RESOURCE_GROUP="myResourceGroup" NSG_NAME="myNSG"

# Add SSH rule az network nsg rule create \ --resource-group $RESOURCE_GROUP \ --nsg-name $NSG_NAME \ --name AllowSSH \ --protocol Tcp \ --priority 1000 \ --destination-port-range 22 \ --access Allow \ --direction Inbound \ --source-address-prefix '*' \ --description "Allow SSH access"

# Verify rules az network nsg rule list \ --resource-group $RESOURCE_GROUP \ --nsg-name $NSG_NAME \ --output table ```

GCP Firewall Rule:

```bash # Using gcloud PROJECT_ID="my-project" NETWORK="default"

# Create firewall rule gcloud compute firewall-rules create allow-ssh \ --project $PROJECT_ID \ --network $NETWORK \ --allow tcp:22 \ --source-ranges 0.0.0.0/0 \ --description "Allow SSH access"

# Or restrict to specific IP range gcloud compute firewall-rules create allow-ssh-restricted \ --project $PROJECT_ID \ --network $NETWORK \ --allow tcp:22 \ --source-ranges YOUR_IP/32 \ --description "Allow SSH from specific IP"

# Verify gcloud compute firewall-rules list --filter="name:ssh" ```

### 6. Check network ACLs

AWS Network ACL:

```bash # Check NACL associations aws ec2 describe-network-acls \ --filters "Name=association.subnet-id,Values=subnet-abc123"

# Check NACL rules aws ec2 describe-network-acls \ --network-acl-ids nacl-123456

# Add inbound rule for SSH (if missing) aws ec2 create-network-acl-entry \ --network-acl-id nacl-123456 \ --rule-number 100 \ --protocol tcp \ --port-range From=22,To=22 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --egress false

# Verify aws ec2 describe-network-acls --network-acl-ids nacl-123456 ```

Check for network firewalls:

```bash # From external host, test port with different tools # Some firewalls respond differently to different probes

# TCP SYN scan nmap -sS -p 22 hostname

# TCP connect scan nmap -sT -p 22 hostname

# ACK scan (bypasses stateless firewalls) nmap -sA -p 22 hostname

# If SYN blocked but ACK gets response: # Stateful firewall is filtering

# Check intermediate hops traceroute -T -p 22 hostname mtr -c 100 hostname ```

### 7. Check TCP wrappers

Check hosts.allow and hosts.deny:

```bash # Check TCP wrappers configuration cat /etc/hosts.allow cat /etc/hosts.deny

# If hosts.deny contains: # ALL: ALL # And hosts.allow doesn't have SSH exception, access is blocked

# Add SSH exception to hosts.allow echo "sshd: 192.168.1.0/24" >> /etc/hosts.allow # Or allow all (not recommended for production) echo "sshd: ALL" >> /etc/hosts.allow

# Test with tcpdchk sudo tcpdchk sudo tcpdmatch sshd user@hostname ```

Check fail2ban:

```bash # Check fail2ban status sudo systemctl status fail2ban

# Check if IP is banned sudo fail2ban-client status sshd

# Unban IP sudo fail2ban-client set sshd unbanip YOUR_IP

# Or restart fail2ban sudo systemctl restart fail2ban

# Check fail2ban logs sudo tail -f /var/log/fail2ban.log ```

### 8. Debug SSH daemon issues

Run sshd in debug mode:

```bash # Stop running sshd sudo systemctl stop sshd

# Run in foreground with debug output sudo /usr/sbin/sshd -d -d -d -p 2222

# -d: Debug mode (can use up to 3 times for more verbosity) # -p 2222: Use different port to avoid conflict

# Attempt connection from another terminal ssh -p 2222 -v user@hostname

# Debug output shows: # - Configuration loading # - Host key loading # - Authentication attempts # - Connection errors ```

Check SELinux (RHEL/CentOS):

```bash # Check SELinux status getenforce

# If Enforcing, check for SSH denials sudo ausearch -m avc -ts recent | grep ssh

# Check SSH port context sudo semanage port -l | grep ssh

# Should show: # ssh_port_t tcp 22

# If using non-standard port, add it: sudo semanage port -a -t ssh_port_t -p tcp 2222

# Restore context sudo restorecon -Rv /etc/ssh/ ```

Check AppArmor (Ubuntu/Debian):

```bash # Check AppArmor status sudo aa-status

# Check for SSH profile sudo aa-status | grep ssh

# If in complain mode, may allow but log # If in enforce mode with issues, may block

# Set to complain mode for debugging sudo aa-complain /usr/sbin/sshd

# Or disable profile sudo aa-disable /usr/sbin/sshd ```

### 9. Check for port conflicts

Check if another process has port 22:

```bash # Check what's using port 22 sudo ss -tlnp | grep :22 sudo lsof -i :22

# If different process, stop it: sudo kill <PID>

# Or reconfigure that service to use different port ```

Check for socket activation:

```bash # Some systems use socket activation for sshd systemctl list-sockets | grep ssh

# If socket is active but service failed: systemctl status ssh.socket systemctl status ssh.service

# Restart both sudo systemctl restart ssh.socket sudo systemctl restart ssh.service ```

### 10. Test and verify SSH access

Test SSH connection:

```bash # Basic connection test ssh user@hostname

# With specific port ssh -p 2222 user@hostname

# Verbose output for debugging ssh -vvv user@hostname

# Test key exchange only ssh -o PreferredAuthentications=none user@hostname # Should get: Permission denied (publickey) # This means SSH daemon is responding

# Test from multiple clients # - Different OS # - Different networks # - Different SSH clients ```

Verify SSH configuration:

```bash # Final verification checklist:

# 1. sshd running systemctl is-active sshd

# 2. Listening on correct port ss -tlnp | grep sshd

# 3. Firewall allows port 22 sudo iptables -L -n | grep 22

# 4. Security group allows SSH # (Check cloud console)

# 5. Can connect locally ssh localhost

# 6. Can connect from remote # (Test from another host)

# 7. Logs show successful connections sudo tail -f /var/log/auth.log # Debian/Ubuntu sudo tail -f /var/log/secure # RHEL/CentOS ```

Prevention

  • Document SSH port and access requirements
  • Configure firewall rules via infrastructure-as-code
  • Use bastion hosts for production access
  • Implement SSH key-based authentication only
  • Regular audit of security group and firewall rules
  • Monitor SSH access logs for unauthorized attempts
  • Use fail2ban or similar for brute-force protection
  • Consider port knocking for sensitive systems
  • **Connection refused**: No service listening or firewall rejected
  • **Connection timed out**: Network unreachable or firewall dropped
  • **Permission denied (publickey)**: SSH daemon responding, auth failed
  • **Host key verification failed**: Host key changed (possible MITM)
  • **Network is unreachable**: No route to destination (routing issue)