FreeBSD密码登录报错timeout
线上WebShell在执行密码登录FreeBSD 13.0 64位时,报错超时进而开始调研,这里分析下
排查
错误的直接原因是SSH登录超时,查看SSH2模块及具体调用逻辑发现有两种可能性会造成超时
- 有执行键盘交互登录时,用户一直未输入会提示为超时,如果本身网络不通报错会是
ECONNRESET
- SSH握手服务端一直没有响应会触发超时
- 有执行键盘交互登录时,用户一直未输入会提示为超时,如果本身网络不通报错会是
按照当前客户端登录逻辑,虽然客户端有多种登录方式轮流尝试的方式,当前逻辑里开启了键盘交互登录,但能够触发的前提也是公钥登录失败
测试环境保证与线上程序一致的情况下测试发现并复现,而生产环境稳定复现。本地终端比如iTerm2直接登录并不复现该问题。
尝试在生产服务器上直接使用ssh命令
具体计时测试发现,生产服务器上测试直接命令行执行ssh连接,在
20s+
后,报错ssh_exchange_identification: read: Connection reset by peer
。对照上述默认超时时间,也就可以说通了。- 生产环境
- 测试环境
基于此基本确定与环境不同有关
分析
了解了下当前生产环境与测试环境有一点明显不同是测试环境机器本身直连外网,而生产并不是,靠着这个明显差异作为切入点,最后咨询系统/网络专家总算搞清楚了问题点,
在生产环境/测试环境抓包ssh握手过程发现,生产环境在TCP握手过程中,发送ACK时没有带时间戳,而预发环境有携带
FreeBSD 是严格按 rfc1323 的 paws 机制设计的, syn 和 syn+ack 两个步骤都含 timestamp option ,就会启用 paws 机制。第三个 ack 包不含 timestamp,就直接丢了
FreeBSD系统默认开启了check时间戳机制,而别的系统比如Ubuntu/CentOS之并没开启,或者没有该设置
采用以下命令可看到相关配置
1
sudo sysctl -a | grep net|grep rfc
FreeBSD12.3中net.inet.tcp.tolerate_missing_ts 默认是1, 而13中默认是0
解决方案
FreeBSD端修改
1
2
3
4
5
6
7
8# 临时修改方案
sysctl -w net.inet.tcp.tolerate_missing_ts=1
# 永久修改
sysctl.conf
net.inet.tcp.tolerate_missing_ts=1
/etc/rc.d/sysctl reload即设置后忽视对于时间戳的验证
客户端即当前WebShell所在客户端侧修改
net.ipv4.tcp_wan_timestamps=1
,当然也需确保net.ipv4.tcp_timestamps=1
即非公网也携带时间戳
最终我们选择方案1,方案2被否是因为当前服务是以容器部署,并没有该设定,需要修改母机,同时改动网络,风险大。
测试如上方案,发现问题解决。
补充
命令行脚本
如上提到的终端ssh连接可以用来调试登录认证过程,这里贴下脚本
1 | ssh -p 22 -v root@ip |
抓包工具
- tcpdump
- 分析工具使用wireshark
SSH服务端登录验证配置
针对键盘交互登录方式会不会尝试除了客户端本身控制之外,也依赖于服务端是否开启。不同系统下对于登录方式默认支持有所区别,Ubuntu/CentOS系统下针对keyboard-interactive方式默认是关闭而FreeBSD是开启
配置文档见:https://www.freebsd.org/cgi/man.cgi?query=ssh_config
1 | ChallengeResponseAuthentication yes |
写在最后
到此问题解决。针对此除了本身SSH握手在时间戳上会造成握手失败的情况外,也需要注意SSH登录本身会尝试多种登录方式。