Git & CI/CD Advanced

Troubleshooting Git SSH Connection Closed by Foreign Host (Port 22, Fatal Protocol) on CentOS Stream / Rocky Linux

Resolve 'Git SSH connection closed by foreign host port 22 fatal protocol' errors on CentOS Stream / Rocky Linux. Fix firewalls, SSH keys, and server configurations.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve 'Git SSH connection closed by foreign host port 22 fatal protocol' errors on CentOS Stream / Rocky Linux. Fix firewalls, SSH keys, and server configurations.

When interacting with a Git repository over SSH, encountering the error "ssh_exchange_identification: Connection closed by foreign host" is a common roadblock for developers and system administrators. This issue indicates that the remote server (the "foreign host") is abruptly terminating the SSH connection before the protocol exchange can even complete. This guide provides a comprehensive, highly technical approach to diagnose and resolve this frustrating problem specifically on CentOS Stream and Rocky Linux environments, covering both client-side and, more critically, server-side troubleshooting.

Symptom & Error Signature

Users will typically observe this error when attempting Git operations such as git clone, git push, git fetch, or even a direct ssh connection to the Git server:

$ git clone ssh://[email protected]/repo.git
Cloning into 'repo'...
ssh_exchange_identification: Connection closed by foreign host
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

A more verbose SSH client debug output (using ssh -vvv) often provides further clues, highlighting the immediate closure:

$ ssh -vvv [email protected]
OpenSSH_8.7p1, OpenSSL 1.1.1l  24 Aug 2021
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 57: include /etc/ssh/ssh_config.d/*.conf
debug1: Connecting to yourgitserver.com [XXX.XXX.XXX.XXX] port 22.
debug1: Connection established.
debug1: identity file /home/user/.ssh/id_rsa type 0
debug1: identity file /home/user/.ssh/id_rsa-cert type -1
debug1: local host fullpath: /home/user/.ssh/id_rsa
debug1: Remote host closed connection
ssh_exchange_identification: Connection closed by foreign host

In some rare cases, you might observe a protocol version difference which is a strong indicator that something other than sshd is responding on port 22, or sshd is crashing immediately:

debug1: Remote host closed connection
debug1: protocol version difference: client has 2, server has 0
ssh_exchange_identification: Connection closed by foreign host

Root Cause Analysis

The "Connection closed by foreign host" error at the ssh_exchange_identification phase almost always points to an issue on the remote server that prevents the SSH daemon (sshd) from initiating a proper handshake. Common root causes on CentOS Stream / Rocky Linux include:

  1. Firewall Blockage: The most frequent culprit. The remote server's firewall (firewalld or iptables) is blocking inbound traffic on port 22, preventing the SSH client from establishing a full TCP connection. While telnet might show the port as open, the firewall could be dropping subsequent packets after the initial SYN/ACK.
  2. SSH Server (sshd) Service Inactivity or Crash: The sshd service on the remote server is not running, has crashed, or is in an unstable state, leading to an immediate connection termination.
  3. SELinux Policy Violation: Security-Enhanced Linux (SELinux) on the remote server might be preventing sshd from reading necessary files (like user SSH keys in ~/.ssh/authorized_keys), writing to logs, or performing other critical operations, causing it to prematurely close connections.
  4. Incorrect SSH Server Configuration (sshd_config): Misconfigurations in /etc/ssh/sshd_config (e.g., MaxAuthTries too low, LoginGraceTime too short, AllowUsers/DenyUsers restrictions, or an invalid KexAlgorithms directive) could lead to rapid connection closures.
  5. Corrupted or Incorrect SSH Host Keys: While less common for an immediate closure, issues with the server's host keys could prevent a secure session setup.
  6. TCP Wrappers (hosts.allow/hosts.deny): The remote server's /etc/hosts.deny configuration might be explicitly blocking the client's IP address or subnet.
  7. Resource Exhaustion: Less likely for this specific error signature, but if the remote server is severely low on memory, CPU, or file descriptors, sshd might fail to handle new connections.
  8. Network-Level Intermediaries: Proxies, load balancers, or other network devices between the client and server could be configured to drop SSH connections under certain conditions.

Step-by-Step Resolution

This troubleshooting guide assumes you have console access or an alternative SSH access (e.g., via a rescue system or another port) to the problematic remote CentOS Stream / Rocky Linux server.

1. Verify Basic Network Connectivity (Client & Server)

First, ensure fundamental network connectivity.

On the Client Machine:

# Check basic IP reachability
ping yourgitserver.com

# Check if port 22 is open and listening (a successful connection means the port is open)
telnet yourgitserver.com 22
# Expected output on success:
# Trying XXX.XXX.XXX.XXX...
# Connected to yourgitserver.com.
# Escape character is '^]'.
# SSH-2.0-OpenSSH_8.x  <-- This line indicates sshd is responding!

If telnet fails to connect or hangs, it's a strong indicator of a network-level or firewall issue.

2. Check Remote Server SSH Daemon Status

Access your remote CentOS Stream / Rocky Linux server via console or an alternative method.

# Check if the sshd service is active and running
systemctl status sshd

# Example of a healthy output:
# ● sshd.service - OpenSSH server daemon
#    Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
#    Active: active (running) since Tue 2026-10-27 10:00:00 UTC; 1min ago
#      Docs: man:sshd(8)
#            man:sshd_config(5)
#  Main PID: 1234 (sshd)
#     Tasks: 1 (limit: 11394)
#    Memory: 4.8M
#    CGroup: /system.slice/sshd.service
#            └─1234 /usr/sbin/sshd -D -oCiphers=...

# If it's not active (dead), attempt to start it:
> [!IMPORTANT]
> If `sshd` fails to start or immediately stops, check `journalctl -u sshd` and `/var/log/secure` for critical errors before blindly restarting.

systemctl start sshd
systemctl enable sshd # Ensure it starts on boot

# If it's running but connections still fail, try restarting it:
systemctl restart sshd

3. Inspect Remote Server Firewall (firewalld)

firewalld is the default firewall management tool on CentOS Stream / Rocky Linux. Incorrect rules are a common cause of SSH connection issues.

# List all active firewall rules for all zones
firewall-cmd --list-all --zone=public
# Look for 'services: ssh' or 'ports: 22/tcp'

# If ssh service is missing, add it permanently:
> [!IMPORTANT]
> This command adds the SSH service to the `public` zone, allowing incoming connections on port 22.

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload # Apply changes without dropping existing connections

# Verify the rule is active after reload
firewall-cmd --list-all --zone=public

If you are troubleshooting over SSH, do NOT restart firewalld with systemctl restart firewalld unless you are absolutely sure of your rules, as it will drop all existing connections. Use firewall-cmd --reload instead.

For temporary testing, you can stop the firewall, but this is highly insecure for production systems:

sudo systemctl stop firewalld
# Test SSH connection
sudo systemctl start firewalld # Re-enable immediately!

4. Review SSH Server Configuration (/etc/ssh/sshd_config)

Misconfigurations in sshd_config can lead to connections being dropped.

sudo vi /etc/ssh/sshd_config

Examine these directives:

  • Port 22: Ensure this is set to 22 if you're connecting on the default port. If it's a non-standard port, ensure your client is connecting to the correct one.
  • ListenAddress: If defined, ensure it's listening on the correct IP address/interface.
  • AllowUsers, DenyUsers, AllowGroups, DenyGroups: Verify that the git user (or whichever user you're connecting as) is not being explicitly denied or is allowed.
  • PermitRootLogin: If you are attempting to log in as root, ensure this is set to yes, prohibit-password, or forced-commands-only. (Generally, PermitRootLogin no is recommended for security).
  • Protocol 2: Ensure this is set to 2. Obsolete Protocol 1 can cause issues.
  • LoginGraceTime: The time in seconds a user has to authenticate. If too low, connections might be terminated prematurely. Default is typically 2m.
  • MaxAuthTries: The maximum number of authentication attempts permitted per connection. Exceeding this can close the connection. Default is usually 6.

After making any changes to sshd_config, you must restart the SSH service for them to take effect:

sudo systemctl restart sshd

5. Verify SSH Key Permissions and authorized_keys (Remote Server)

Incorrect file or directory permissions for the ~/.ssh directory or ~/.ssh/authorized_keys file are common causes of SSH authentication failures, which can sometimes manifest as early connection closure.

# Navigate to the user's home directory (e.g., /home/gituser)
cd /home/gituser # Or the home directory of the user you're connecting as

# Check permissions for the .ssh directory
ls -ld .ssh
# Expected: drwx------ (700) owner:group

# Check permissions for authorized_keys file
ls -l .ssh/authorized_keys
# Expected: -rw------- (600) owner:group

# If permissions are incorrect, fix them:
chmod 700 .ssh
chmod 600 .ssh/authorized_keys

# Ensure the ownership is correct for the user
chown -R gituser:gituser .ssh

Ensure the public key within ~/.ssh/authorized_keys is correct, on a single line, and doesn't contain extra whitespace or characters.

6. Examine SELinux Contexts (Remote Server)

SELinux can prevent sshd from accessing user directories or files if contexts are incorrect.

# Check SELinux status
sestatus
# Expected: enforcing

# List SELinux contexts for .ssh and authorized_keys
ls -Zd ~/.ssh ~/.ssh/authorized_keys
# Expected for .ssh: unconfined_u:object_r:ssh_home_t:s0 .ssh
# Expected for authorized_keys: unconfined_u:object_r:ssh_authorized_keys_t:s0 .ssh/authorized_keys

# If contexts are incorrect, restore them to default:
> [!IMPORTANT]
> This command will recursively restore the default SELinux contexts for files and directories under `~/.ssh`.

sudo restorecon -Rv ~/.ssh

For temporary troubleshooting, you can disable SELinux. This is NOT recommended for production environments as it severely compromises system security.

sudo setenforce 0 # Temporarily set to permissive mode
# Test SSH connection
sudo setenforce 1 # Re-enable enforcing mode immediately!

To disable permanently (only for extreme cases), edit /etc/selinux/config and set SELINUX=disabled, then reboot.

7. Check TCP Wrappers (Optional, Remote Server)

TCP Wrappers (controlled by /etc/hosts.allow and /etc/hosts.deny) can block connections.

cat /etc/hosts.allow
cat /etc/hosts.deny

Look for sshd: ALL or sshd: <client_IP> entries. Ensure your client's IP is not being denied. If /etc/hosts.deny has sshd: ALL, you must explicitly allow your IP in /etc/hosts.allow (e.g., sshd: YOUR_CLIENT_IP).

8. Analyze SSH Server Logs for Clues (Remote Server)

SSH server logs (journalctl) provide critical debugging information.

# View the last few entries for the sshd service
journalctl -u sshd -e

# Filter logs for authentication errors or specific IP addresses
journalctl -u sshd | grep 'Authentication refused'
journalctl -u sshd | grep 'Failed password'
journalctl -u sshd | grep 'YOUR_CLIENT_IP'

If the logs are not verbose enough, you can temporarily increase the LogLevel in /etc/ssh/sshd_config to DEBUG or VERBOSE_DEBUG for more detailed output. Remember to restart sshd after changing LogLevel and revert it to a less verbose setting (INFO or VERBOSE) once troubleshooting is complete.

9. Client-Side SSH Configuration and Agent

While the error points to the foreign host, ensure your client-side setup isn't contributing to the issue, especially regarding key management.

# Check if your SSH agent is running and has keys loaded
ssh-add -l

# If your key is not loaded, add it:
ssh-add ~/.ssh/id_rsa_yourkey # Replace with your actual private key path

# Inspect ~/.ssh/config for specific host entries that might override defaults
cat ~/.ssh/config

Ensure no problematic HostName, User, Port, or IdentityFile directives are incorrectly configured for yourgitserver.com.

By systematically working through these steps on both your client and especially your remote CentOS Stream / Rocky Linux Git server, you should be able to pinpoint and resolve the "Connection closed by foreign host" error. Remember that security configurations (firewalls, SELinux) are common culprits and require careful verification.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.