必须同时配置TMOUT和ClientAliveInterval+ClientAliveCountMax,缺一不可;TMOUT只监控键盘输入空闲,无法感知网络中断或客户端假死,而ClientAlive机制通过加密心跳探测连接真实性,二者分别解决“人没操作”和“网断进程挂”两类问题。

只配 TMOUT 或只改 ClientAliveInterval,空闲断连大概率失效。必须两者都配,且各自生效路径不同——一个管“人没敲键盘”,一个管“网线断了但进程还挂着”。
为什么单设 TMOUT 会漏掉大量假活跃连接
TMOUT 是 Bash 自己的计时器,只在你有输入时才倒数;一旦运行 vim、tail -f、ssh 到另一台机器,或者笔记本合盖休眠,它就完全停摆。此时 sshd 进程仍认为连接正常,不会释放资源。
- 验证方式:登录后执行
unset TMOUT,再静默等待(比如拔网线),观察是否真被踢出——若没踢,说明TMOUT不起作用 - 图形终端(GNOME Terminal、Konsole)默认不加载
/etc/profile,TMOUT直接无效 - 写进
~/.bashrc也不行:SSH 登录默认走~/.bash_profile,不读.bashrc - 必须用
export TMOUT=1800,仅TMOUT=1800不导出,子 shell 和新登录都不继承
怎么配 ClientAliveInterval 和 ClientAliveCountMax 才不误杀
这两个参数在 /etc/ssh/sshd_config 里协同工作,控制服务端主动探测连接真实性的频率和容忍度。总超时 = ClientAliveInterval × ClientAliveCountMax。
- 别设
ClientAliveCountMax 0:这会让 sshd 在第一次心跳无响应时立刻断连,Wi-Fi 切换、短暂丢包都会触发误杀 - 推荐组合:
ClientAliveInterval 300(5 分钟发一次) +ClientAliveCountMax 3(最多丢 3 次) → 实际容忍 15 分钟无响应 - 必须取消注释:检查配置行前面不能有
#,也不能写成ClientAliveInterval=300(等号是错的) - 改完必须
sudo systemctl restart sshd:reload 有时不重载心跳参数,只 restart 才保险
如何验证配置真正在跑,不是纸上谈兵
别只跑 sshd -t 或 echo $TMOUT ——那只是语法对、变量有值,不代表机制在线。
- Shell 层验证:新 SSH 登录后,先
unset TMOUT,然后什么都不做,等满设定时间(如 15 分钟),看是否被踢出;若没踢,说明ClientAlive*没生效 - 网络层验证:另一台机器连上,执行
sudo ss -tnp | grep :22确认 ESTABLISHED 连接存在;然后关掉客户端或拔网线,等超过ClientAliveInterval × ClientAliveCountMax时间,再查连接是否消失 - 注意干扰项:
systemd-logind的IdleAction(比如设了IdleAction=lock)可能抢在 sshd 前干掉会话,需检查loginctl show-user $USER | grep IdleAction
权限、路径、兼容性这些细节最容易翻车
配置看着全对,却在某些账号下失效,问题常出在这些地方:
-
~/.ssh/config权限必须是600,否则 OpenSSH 直接忽略整个文件 - CentOS 7.9 等老系统中,
/etc/ssh/sshd_config可能被云厂商镜像覆盖,改完要确认没被重置 - 非 SSH 登录(比如本地 tty、串口 console)不受
ClientAlive*影响,只靠TMOUT - 如果用了跳板机或
ProxyCommand,客户端的ServerAliveInterval无法穿透到最终目标,得在跳板机上也配
真正难搞的是中间设备——防火墙、NAT 网关、企业级状态检测设备,它们根本不认 SSH 心跳包,只看 TCP 数据流。这时候光调 ClientAliveInterval 没用,得协调网络侧调大连接跟踪超时,或者加应用层保活(比如后台跑 while :; do date >/dev/null; sleep 30; done)。


















