服务器SSH连接超时怎么办?连接参数、路由与防火墙排查

SSH 连不上服务器,是运维最"要命"的一类故障——因为它是你进去排查别的故障的入口。SSH 一旦不通,网站挂了你也进不去看,整个排障就卡在第一步。而且 SSH 的"连不上"有好几种完全不同的表现,很容易混:有的是敲了命令半天没反应最后超时(Connection timed out),有的是直接拒绝(Connection refused),有的是连上了但卡在输密码或登录后卡半天,还有的是登录能进但几分钟不动就被踢出来。这几种背后的原因天差地别。

这篇文章把 SSH 问题分成三个阶段来看:连不上(网络、安全组、防火墙、端口)、连得上但很慢(DNS 反解、GSSAPI 认证、PAM、motd)、连得上但会断(心跳保活、MTU)。每个阶段给出对应的排查工具和配置。读完你应该能明确判断"我这次卡在哪一阶段",并用最短的路径解决,而不是一上来就乱改 sshd 配置。

一、先明确"超时"长什么样

SSH 报错的措辞直接对应不同的问题,先学会读它:

报错 含义 方向
Connection timed out 包发出去没有回应 网络不通、安全组丢弃、防火墙 DROP
Connection refused 主机可达但端口没开 sshd 没启动、端口错、防火墙 REJECT
No route to host 路由不可达 路由/网关/网段问题
Permission denied 认证失败 密钥/密码/用户问题(不是网络问题)
Connection reset by peer 连接被重置 中途被拦、sshd 异常、防护软件
连上后卡登录界面很久 认证阶段慢 DNS 反解、GSSAPI、PAM
登录后闲置一会儿断 会话保活失效 客户端/服务端心跳、NAT 超时

timed out 和 refused 的区别是关键:超时说明包被"静默丢弃"(防火墙 DROP 或网络不通),拒绝说明包到了、但没人监听。这两个的处理方向完全不同。

二、第一阶段:连不上

1. 分层确认连通性

# 先确认能不能到主机
ping -c 4 
# 再确认 SSH 端口是否开放(默认 22)
nc -zv -w 3  22
telnet  22

# 如果改了端口,测对应端口
nc -zv -w 3  2222

# 看在哪一跳断的
traceroute 
mtr -rw -c 20 

判读:

  • ping 不通、nc 也超时:可能主机宕了、网络不通、或 ICMP+端口都被丢弃。继续用 mtr 看断点。
  • ping 通、nc 22 超时:包到了主机但被丢弃,典型是防火墙 DROP 或云安全组没放行。
  • nc 22 返回 Connection refused:主机在,但 22 端口没监听(sshd 没起或端口改了)。

2. 检查 SSH 服务端(需要能通过别的方式进机器)

如果还能用云控制台的 VNC/救援终端进去,检查 sshd:

# 服务状态
systemctl status sshd            # RHEL/CentOS/Rocky
systemctl status ssh             # Debian/Ubuntu

# 是否在监听、监听哪个地址和端口
ss -lntp | grep -E ':22|ssh'
# 注意监听地址:127.0.0.1:22 只接受本机;0.0.0.0:22 接受所有

# 看监听端口配置
grep -E '^Port|^ListenAddress' /etc/ssh/sshd_config
sshd -T | grep -E '^port|^listenaddress'   # 打印最终生效配置

# 看日志
journalctl -u sshd -n 100 --no-pager
tail -n 100 /var/log/secure          # RHEL 系
tail -n 100 /var/log/auth.log        # Debian 系

3. 检查防火墙与安全组

这是"端口超时"第一大原因。注意要分两层查:云安全组和系统防火墙。

# 系统防火墙
# RHEL/CentOS/Rocky(firewalld)
sudo firewall-cmd --list-all
sudo firewall-cmd --list-ports
firewall-cmd --query-port=22/tcp
# 放行 SSH(若被误删)
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

# Debian/Ubuntu(ufw)
sudo ufw status verbose
sudo ufw allow 22/tcp

# 底层 iptables(有些环境直接用它)
sudo iptables -L -n -v | grep -E '22|ssh|DROP|REJECT'

# 云服务器还要去控制台确认安全组/网络 ACL 放行了 22 端口(或你改的端口)
# 入站规则要允许你的来源 IP 访问该端口

风险提示:远程改防火墙时,务必先留退路。用临时规则或 --timeout 先测试:

# firewalld 临时放行 60 秒,验证能连再永久保存
sudo firewall-cmd --add-port=22/tcp --timeout=60
# 确认新规则不会把自己挡在外面后,再 --permanent

如果把自己挡在外面了,只能通过云控制台的 VNC/串口进系统恢复。

4. 一个高频坑:改端口后忘了改防火墙

把 SSH 端口从 22 改成 2222 后,只改了 sshd_config 却没在防火墙/安全组放行 2222,是老手也会踩的坑。改端口务必同步处理防火墙,并保留一个已放行的旧端口会话不断开,确认新端口能连再关掉旧的。

三、第二阶段:连得上但很慢

现象是:nc 22 通,ssh 命令也能建立连接,但输密码前或登录后要等十几秒甚至更久。根源通常是服务端在连接建立时做了反向 DNS 查询或 GSSAPI 认证,而这些查询超时了。

1. 关闭 DNS 反解(UseDNS)

sshd 默认(或某些版本默认开启)会对客户端 IP 做反向解析,如果 DNS 不好使,就会卡到超时(常见几十秒)。

# 编辑 /etc/ssh/sshd_config
# UseDNS no
# 检查并修改
sudo sed -i 's/^#\?UseDNS.*/UseDNS no/' /etc/ssh/sshd_config
sudo sshd -t                # 校验语法
sudo systemctl reload sshd  # RHEL 系;Debian 系是 ssh

2. 关闭 GSSAPI 认证

GSSAPI(Kerberos)认证在没配 Kerberos 的环境下会尝试并超时。

# /etc/ssh/sshd_config 中
# GSSAPIAuthentication no
# 客户端也在 /etc/ssh/ssh_config 里关
# GSSAPIAuthentication no
sudo sed -i 's/^#\?GSSAPIAuthentication.*/GSSAPIAuthentication no/' /etc/ssh/sshd_config
sudo systemctl reload sshd

3. PAM 与 motd 拖慢登录

登录后卡顿,可能是 PAM 模块(如 pam_systemd、pam_unix 的 NSS 查询)或显示 motd 时执行了慢命令。

# 看 PAM 配置
cat /etc/pam.d/sshd
# 检查 NSS 是否配置了会超时的后端
grep -E '^passwd|^group|^hosts' /etc/nsswitch.conf

# 看登录脚本里有没有慢命令
cat /etc/profile /etc/bashrc
ls /etc/profile.d/
cat /etc/update-motd.d/* 2>/dev/null     # Debian/Ubuntu 的 motd 脚本

# 用 strace 跟踪 sshd 登录过程,看卡在哪(在高负载或调试时慎用)
sudo strace -f -p $(pgrep -f 'sshd.*') 2>&1 | head -50

注意:strace 附加进程会有性能开销,生产环境尽量在测试环境做,或只在必要时短时间使用。

4. 客户端侧调优

# 客户端 ~/.ssh/config,避免每次卡顿
cat >> ~/.ssh/config <<'EOF'
Host myserver
    HostName 203.0.113.10
    User root
    GSSAPIAuthentication no
    # 跳过不必要的认证方式,加速连接
    PreferredAuthentications publickey,password
EOF
chmod 600 ~/.ssh/config

四、第三阶段:连得上但会断

现象是能登录、能操作,但闲置一会儿(几分钟到十几分钟)后会话就"卡死"或断开,ssh 提示 Connection to ... closed 或 Write failed: Broken pipe。原因是空闲连接被中间的 NAT、防火墙、负载均衡回收了,或者双方心跳没开。

服务端保活

# /etc/ssh/sshd_config
# ClientAliveInterval 60      # 每 60 秒发一次心跳
# ClientAliveCountMax 3       # 连续 3 次无响应才断开(即最多约 180 秒)
sudo sed -i 's/^#\?ClientAliveInterval.*/ClientAliveInterval 60/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?ClientAliveCountMax.*/ClientAliveCountMax 3/' /etc/ssh/sshd_config
sudo systemctl reload sshd

客户端保活

# 客户端 ~/.ssh/config 或 /etc/ssh/ssh_config
# ServerAliveInterval 60      # 每 60 秒向服务器发心跳
# ServerAliveCountMax 3
cat >> ~/.ssh/config <<'EOF'
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    TCPKeepAlive yes
EOF

两端都配更保险。原则:客户端心跳间隔要小于中间设备(NAT/防火墙)的空闲超时时间(很多 NAT 默认 5–10 分钟)。

MTU 问题

如果断在传大文件、跑大命令时,或者某些操作后连接直接卡死,要怀疑 MTU。中间链路的 MTU 不匹配会导致大包被丢弃,TCP 又没正确分片,表现为"小操作正常、大传输卡死"。

# 探测路径 MTU(Don't Fragment)
ping -M do -s 1472      # 1472 + 28 = 1500,通了说明 MTU 1500
ping -M do -s 1400      # 逐步减小,找到能通的最大值
# 若 1472 不通、1400 通,说明路径 MTU 小于 1500,需要调低

# 临时降低本机 MTU
sudo ip link set dev eth0 mtu 1400

云环境(VXLAN、隧道)常见 MTU 是 1450 或更小。如果怀疑 MTU,可以在服务端/客户端把网卡 MTU 调低测试,确认后再固化。

五、用 ssh -vvv 定位卡点

ssh -vvv 会打印详细的连接和认证过程,是判断"卡在哪一步"的利器。

ssh -vvv user@203.0.113.10

输出很长,看关键节点:

  • 卡在 Connecting to 203.0.113.10 port 22:TCP 连接阶段,网络/防火墙问题。
  • 卡在 debug1: SSH2_MSG_KEXINIT sent 之后无输出:密钥交换阶段,可能被拦或 MTU 问题。
  • 卡在认证前(Authentications that can continue 之前很久):多半是 UseDNS/GSSAPI。
  • 认证通过后卡住:PAM、motd、登录脚本。

配合工具一起用:

# 排查网络层
mtr -rw -c 20 
traceroute 
nc -zv -w 3  22

# 抓包(看 TCP 握手到了哪一步)
sudo tcpdump -i any -nn port 22 -c 50
# 看到 SYN 发出但没有 SYN-ACK,多半被墙(DROP)
# 看到 SYN-ACK 后又有 RST,可能被 REJECT 或 sshd 拒绝

抓包是终极手段:能明确区分"没收到回应(DROP/网络不通)"和"收到拒绝(REJECT/无监听)"。

六、配置调优清单

现象 调整项 位置
连接超时 安全组 + 防火墙放行端口 云控制台 / firewalld / ufw
连接被拒 启动 sshd、确认端口 systemctl、sshd_config
登录慢(DNS) UseDNS no /etc/ssh/sshd_config
登录慢(GSSAPI) GSSAPIAuthentication no 服务端和客户端
会话超时断开 ClientAliveInterval 服务端
客户端侧保活 ServerAliveInterval ~/.ssh/config
大传输卡死 调 MTU ip link set mtu
单 IP 频繁被扫 MaxAuthTries、fail2ban sshd / fail2ban

补充一个安全相关建议:SSH 是暴力破解重灾区,建议禁用密码登录、改用密钥,并配合 fail2ban 或云平台的高危端口防护。改 sshd_config 前务必先确认新配置能登录,改完先 sshd -t 校验,再 reopen 一个新会话验证,最后 reload,不要一下子 restart 把自己锁外面。

# 改配置的标准安全流程
sudo sshd -t                                   # 1. 先校验语法
# 2. 保留当前会话不要断,另开一个新终端测试
ssh -p  user@server
# 3. 新会话能进,再重载
sudo systemctl reload sshd

风险提示:sshd_config 改错是最经典的"把自己锁在服务器外面"。修改前备份 cp /etc/ssh/sshd_config{,.bak},全程保留一个已登录的会话不断开,用云控制台作为最后退路。

常见问题

Q:Connection timed out 和 Connection refused 有什么区别,怎么处理?
timed out 是包被静默丢弃,查网络和安全组/防火墙(DROP 规则);refused 是端口没监听,查 sshd 是否启动、端口是否改过。前者用 mtr/tcpdump,后者用 ss -lntp 和 sshd -T。

Q:能连上但每次登录要等十几秒,最可能是什么?
优先关 UseDNS no 和 GSSAPIAuthentication no,这两项是最常见的"登录慢"。其次是 PAM/NSS 查询和登录脚本里的慢命令。

Q:登录后不动几分钟就断,怎么解决?
配置保活。服务端 ClientAliveInterval 60 + ClientAliveCountMax 3,客户端 ServerAliveInterval 60。间隔要小于中间 NAT/防火墙的空闲超时。

Q:改了 SSH 端口就连不上了,怎么办?
先确认防火墙和安全组放行了新端口,ss -lntp 确认 sshd 监听在新端口,sshd -T | grep port 确认配置生效。若被锁在外面,用云控制台的 VNC/串口进去改回。

Q:大文件传输(scp/rsync)中途卡死,怎么排查?
多半是 MTU 问题。用 ping -M do -s 1472 探测路径 MTU,不通就逐步减小,找到合适的值后调低网卡 MTU(云环境常见 1450)。开启 tcp_mtu_probing 也可作为辅助。

总结

SSH 问题先分三阶段:连不上、连得上但慢、连得上但断。不同阶段原因和处理完全不同。

连不上:先 ping + nc -zv 分清是超时还是拒绝,超时查网络/安全组/防火墙(注意云安全组 + 系统防火墙两层),拒绝查 sshd 是否启动、端口是否改过。用 mtr、tcpdump 定位。

连得上但慢:罪魁通常是 UseDNS 反解和 GSSAPI 认证,在 sshd_config 里关掉(UseDNS no、GSSAPIAuthentication no),其次是 PAM/NSS 和登录脚本。

连得上但断:配置两端心跳保活(服务端 ClientAliveInterval,客户端 ServerAliveInterval),怀疑大包卡死就查 MTU。

排查工具首推 ssh -vvv,配合 nc、mtr、tcpdump。改任何 sshd_config 前,先备份、先校验(sshd -t)、保留活动会话、留好云控制台退路,避免把自己锁在服务器外面。