先问一句:你现在的状态,到底是“ping 不通 CentOS 7 主机”,还是“ping 得通但 SSH 连不上 22 端口”?这两个问题的排查路径完全不一样,但很多人在提问时报错信息只写了“centos7的22端口无法访问”,这就把故障范围拉得很大。我刚处理过一台 CentOS 7.9 的虚拟机,客户机同网段能 ping 通,但ssh root@一直卡在连接超时,最后定位到是 firewalld 默认 zone 被改成了 drop。这种场景太典型了,值得把整套排查思路完整过一遍。
这篇文章按真实排障顺序展开:网络层判断、端口监听、防火墙、sshd 服务、SELinux、资源问题、客户端干扰以及虚拟机特殊场景。不管你是刚装完 CentOS 7 连不上,还是服务器跑着跑着突然 22 端口进不去,都可以按这个路线逐层定位。
1. 22端口连不上的故障全景与排查路线
1.1 先分清三种截然不同的故障现象
“无法访问”这个词概括了至少三种完全不同的现象,我建议你先把现象确认清楚,后面才不会白忙活。
第一种是连接被拒绝,客户端秒弹Connection refused。说明网络路径是通的,但目标机器的 22 端口没有进程在监听,或者防火墙直接回了 RST。第二种是连接超时,客户端卡在那里直到Connection timed out。说明 SYN 包发出去之后石沉大海,通常是被防火墙 DROP 了,也可能是路由不可达。第三种是认证失败,Permission denied或者密码错,这已经说明 22 端口通了,只是卡在身份验证。
这三种现象对应的排查方向完全不同。遇到过很多新手,明明看到的是Connection refused,还在拼命查防火墙和网络,结果实际是 sshd 服务压根没起来。所以,第一步永远是确认现象,而不是凭经验乱猜。
1.2 一条主线解决90%的问题
对于 CentOS 7 上 22 端口无法访问,核心排查主线就四个字:通、听、挡、验。
通,指网络层是否可达,包括 IP、网关、路由和 ARP。听,指 sshd 有没有监听在 0.0.0.0:22 或者正确的网卡地址上。挡,指 firewalld、iptables、SELinux 这些安全机制有没有把 22 端口拦下来。验,指用客户端工具交叉验证,确认是服务器问题还是客户端这边自己的环境问题。
我在实际排障中习惯按这张表逐步推进。
| 排查层级 | 核心问题 | 典型命令 | 快速判断 |
|---|---|---|---|
| 网络层 | IP和路由通不通 | ping、ip route | ping 不通优先查网络配置 |
| 传输层 | 22端口有没有监听 | ss -tlnp、telnet | 无监听则查 sshd |
| 安全层 | 防火墙/SELinux 拦没拦 | firewall-cmd、getenforce | 规则缺失则放行 |
| 应用层 | sshd 配置和日志 | sshd -t、/var/log/secure | 配置错误则修正 |
| 客户端 | 软件、代理、known_hosts | ssh -vvv、换机器测试 | 客户端问题则替换工具 |
这篇文章后面所有内容,都是围绕这条主线展开的。
2. 先分清三层问题:网络通不通,端口听没听,防火墙挡没挡
2.1 第一板斧:ping 和基础连通性
先看最基本的。在客户端机器上执行:
ping 192.168.31.10如果 ping 不通,有三种可能。第一种,目标机器根本不在这个网段,或者 IP 配置错了。第二种,中间有防火墙禁 ping,但注意:很多云主机和服务器禁 ICMP 不等于禁 TCP,所以 ping 不通不一定说明 SSH 也不通。第三种,提示“无法访问目标主机”这类报错时,通常是本机 ARP 缓存里找不到目标 MAC 地址,说明目标主机不在当前链路内,或者网关有问题。
接下来看服务端自己:
ip addr ip routeip addr确认网卡有没有拿到 IP,状态是 UP 还是 DOWN。ip route确认默认路由是否存在。如果发现default via这一行消失了,跨网段访问基本没戏,因为回包找不到路。我见过一台 CentOS 7 重启之后默认路由丢了,外部 ping 不通,SSH 自然也进不去,重启 network 服务才恢复。
ping 通是基础,但很多人忽略了一个关键:ping 通只代表 ICMP 可达,不能代表 22 端口 TCP 可达。所以下一步必须直接测端口。
2.2 第二板斧:telnet/nc/nmap 探测22端口
端口探测最直观的方式是 telnet:
telnet 192.168.31.10 22如果出现Connected to 192.168.31.10并显示SSH-2.0-OpenSSH_7.4之类的 banner,说明 22 端口已经通了,问题可能出在认证环节或客户端自身。如果出现Connection refused,说明端口没监听或被 REJECT。如果一直卡住直到超时,说明数据包被丢弃。
在不方便装 telnet 的机器上,可以用 nc:
nc -vz -w 3 192.168.31.10 22-v显示详细信息,-z只扫描不发送数据,-w 3设置 3 秒超时。有 nmap 的话更简单:
nmap -p 22 192.168.31.10nmap 会明确显示open、closed、filtered三种状态。其中filtered基本就是防火墙把包丢了,这个信息和Connection timed out相互印证。
我实测过一个很典型的案例:同网段 ping 通,telnet超时,服务端ss -tlnp显示 sshd 正常监听 0.0.0.0:22,最后发现是 firewalld 默认 zone 被设置成了drop。所有入站连接都被静默丢弃,表现就是“看起来一切都正常,但就是连不上”。
2.3 第三板斧:在服务端抓包确认是被丢还是没回包
如果客户端探测超时,下一步我建议直接在 CentOS 7 服务器上抓包,这一步能区分“防火墙丢弃”和“回程路由不通”。
tcpdump -i ens33 tcp port 22 -nn观察几秒钟。如果只看到SYN发进来,但服务器没有回SYN-ACK,那基本可以确定是防火墙拦了,因为协议栈本身没有回包。如果看到服务器回了SYN-ACK,但客户端依然超时,那就是回程路由或客户端侧的问题。
抓包这个操作看起来多余,但在复杂网络里真的能救命。有一次我排查一台双网卡服务器,发现抓包能看到请求进来,但服务器从错误网卡回包,客户端自然收不到。这个问题如果不抓包,光靠看防火墙规则,永远定位不到。
3. 网卡与IP配置:那些一眼看不出来的老坑
3.1 ifcfg-xxx 和 ONBOOT=no 这个经典问题
CentOS 7 的网络配置文件和 CentOS 6 相比变化不大,但坑一点都不少。最常见的是/etc/sysconfig/network-scripts/ifcfg-ens33里ONBOOT=no,导致开机后网卡没有自动启动,IP 没配上去,外部自然无法访问。
修改方式很简单:
vi /etc/sysconfig/network-scripts/ifcfg-ens33确保以下几项正确:
TYPE=Ethernet BOOTPROTO=static ONBOOT=yes IPADDR=192.168.31.10 NETMASK=255.255.255.0 GATEWAY=192.168.31.1 DNS1=223.5.5.5改完执行:
systemctl restart network或者用 NetworkManager 的方式:
nmcli con reload nmcli con up ens33这里有个配置差异:CentOS 7 同时存在 network 和 NetworkManager 两套管理工具。如果你改了配置文件但发现不生效,检查一下 NetworkManager 是不是接管了这块网卡。我见过用户在/etc/sysconfig/network-scripts/下改了 IP,结果 NM 自动覆盖了配置,最后两条命令很快解决:
systemctl disable NetworkManager systemctl enable network systemctl restart network另外提醒一句,很多从 CentOS 6 时代过来的老文档还在教改eth0,但 CentOS 7 默认用ens33或ens192这类网卡命名。如果写的网卡名不对,文档改得再对也等于白改。可以用ip addr先确认实际网卡名。
3.2 路由表与多网卡的干扰
单网卡配置不当导致问题,相对好查。真正头疼的是多网卡服务器。
有一次我在一台双网卡 CentOS 7 上部署服务,外网网卡和内网网卡都配置了默认网关。重启后系统把默认路由指向了内网网卡,结果从外网 SSH 22 端口进来的包,回包却从内网网卡发出去了。客户端那边看到的全是连接超时。
排查命令:
ip route正常情况下输出里应该只有一条default via。如果出现了多条默认路由,或者默认网关指向了错误的网卡,处理方式通常是把不需要的 GATEWAY 注释掉,或者用策略路由(ip rule)控制源地址路由。
对于只有一张网卡的读者,这里也不可掉以轻心。CentOS 7 里 NetworkManager 有时候会动态修改路由表,尤其是虚拟机环境里 DHCP 和静态配置混用的时候。配置完 IP 之后,再执行一次ip route确认默认路由存在且指向正确,这是成本最低的保险。
4. firewalld 是 22 端口访问失败的头号嫌疑人
4.1 先确认防火墙到底开没开
CentOS 7 默认使用 firewalld,但在很多装机教程和自定义镜像里,可能同时存在 iptables 服务。所以排查时要先确认到底是谁在拦。
systemctl status firewalld firewall-cmd --state如果输出running,那防火墙就是主要嫌疑人。接着查看当前 zone 和已有规则:
firewall-cmd --get-default-zone firewall-cmd --list-all--get-default-zone输出通常是public,但如果被改成了drop,那所有入站流量都会被直接丢弃,SSH 无论如何都连不上。--list-all里能看到当前 zone 放行了哪些服务和端口,如果services: ssh不在列表里,22 端口默认就被挡在外面。
这里有个关键概念:firewalld 的 zone 机制类似“不同信任级别的区域”。public区域默认只放行少量服务,drop区域默认丢弃所有入站请求,trusted区域默认接受所有流量。CentOS 7 安装完成后默认是public,本来应当放行 ssh,但很多人在配置过程中误改了默认 zone,或者执行过一些第三方脚本导致 zone 变成了 drop。检查这一步一定要做。
4.2 正确放行22端口的命令
如果防火墙开着但规则里没有 ssh,执行以下命令放行:
firewall-cmd --permanent --add-port=22/tcp firewall-cmd --reload这里加不加--permanent是新手最容易踩的坑。--permanent表示写入永久配置,重启后依然生效;不带这个参数的话,规则只对当前运行环境生效,重启 firewalld 或重启机器后就没了。
如果你想指定来源 IP,而不是对所有 IP 开放,可以用 rich rule:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.31.0/24" port protocol="tcp" port="22" accept' firewall-cmd --reload执行完以后,一定要复查:
firewall-cmd --list-all确认ports: 22/tcp或对应的 rich rule 已经出现。如果复查发现规则不在,多半是刚才的命令没加--permanent,重启后丢了。
4.3 “重启失效”和 DOCKER 链的坑
说一个很典型的现场故事。有人执行了firewall-cmd --add-port=22/tcp,当时测试 SSH 可以连接,很高兴。结果第二天重启机器后又连不上了,还非常困惑“我明明放行了”。
原因就是上面说的:--add-port没有加--permanent,规则只存在于运行时配置里。重启后 firewalld 加载永久配置,临时规则自然消失。正确的操作流程是:先加--permanent,再--reload,最后用--list-all复核。
另一个更隐蔽的坑和 Docker 有关。CentOS 7 上装了 Docker 之后,Docker 会在 iptables 的 FORWARD 链中插入 DOCKER 规则。如果firewalld和 Docker 的启动顺序不对,或者做了防火墙操作后没有重新初始化 Docker 链,可能会导致整个网络转发异常,表现之一就是 22 端口访问失败。
排查方法:
iptables -L -n | grep 22 iptables -L FORWARD -n看到 DOCKER 链的规则覆盖了 FORWARD 行为,但又不确定是不是它导致的,可以临时验证:
systemctl stop firewalld然后立刻用客户端测试 22 端口。如果能连接了,说明一定是 firewalld 的规则问题。确认后再把防火墙启动回来,用最小化放行规则代替“一关了之”。
5. sshd 服务本身与 SELinux 的隐形坑
5.1 检查 sshd 是否在听、监听在哪
当网络层和防火墙都排除后,就要看 sshd 自己了。先检查服务状态:
systemctl status sshd如果显示active (running),再看监听地址:
ss -tlnp | grep :22正常情况应该看到类似下面的输出:
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))注意0.0.0.0:22表示监听在所有 IPv4 地址上。如果看到的是127.0.0.1:22,那问题就大了:sshd 只监听在回环地址上,外部流量根本无法到达。这种情况通常是因为/etc/ssh/sshd_config里配置了ListenAddress 127.0.0.1,或者系统里其他配置文件覆盖了默认监听行为。
解决方法,把配置改为:
ListenAddress 0.0.0.0然后重启 sshd。这是“端口通但连不上”的经典原因之一,排查时通过ss -tlnp一眼就能看出来。
5.2 sshd_config 改错导致远程回不来
另一种情况是服务端 sshd 没有在运行,或者启动失败。看状态和日志:
systemctl status sshd journalctl -u sshd -n 50 tail -n 50 /var/log/secure如果有Failed to start OpenSSH server daemon之类的报错,大概率是配置文件写错了。最稳的操作是执行配置语法检查:
sshd -t如果语法有误,这里会直接报错。这个命令在远程维护场景下简直是救命绳。我自己的习惯是:每次改完/etc/ssh/sshd_config,先sshd -t通过,再systemctl restart sshd。千万别直接 restart,一旦语法错误,服务起不来,远程会话直接断开,如果手头没有其他访问通道,就只能去机房或者靠 IPMI 了。
另外,配置里几个常见项值得留意:
PermitRootLogin:如果设为no,root 用户无法登录。PasswordAuthentication:如果设为no,密码登录会被拒绝。AllowUsers/DenyUsers:指定用户白名单或黑名单,如果配错了会把你自己挡在门外。UseDNS:如果设为yes,客户端连接时服务端会做反向 DNS 解析,表现为连接建立很慢但最终能连上。对多数场景建议设置为no。
排障时结合/var/log/secure最有效。看到Failed password是认证失败,看到Connection closed by authenticating user说明认证流程被中断,看到error: maximum authentication attempts exceeded说明客户端尝试认证次数太多被服务端断开。
5.3 SELinux 拦截了 SSH 流量
很多人在排查 22 端口时会把 SELinux 直接忽略,但它确实会拦截 SSH,而且表现形式很迷惑:防火墙开了,sshd 也在监听,可外部就是连不上。
先看状态:
getenforce输出Enforcing说明 SELinux 正在强制模式。为了快速定位,可以临时关掉验证:
setenforce 0这时候如果客户端能连上了,那基本可以确定是 SELinux 拦截。但注意,setenforce 0只对当前运行环境生效,重启后恢复 Enforcing,不能作为长期解决方案。
长期方案是用 semanage 命令放行对应端口。比如你把 sshd 改到了 9022 端口,SELinux 默认只允许 22,就需要执行:
semanage port -a -t ssh_port_t -p tcp 9022查看当前 SELinux 允许的 SSH 端口:
semanage port -l | grep ssh如果看到ssh_port_t tcp 22只有 22,而你实际监听的是其他端口,那 SELinux 一定会拦。新增放行后重启 sshd 即可。
有人会问,为什么只改了端口就要动 SELinux?因为 SELinux 的端口类型标签和进程策略是绑定的,sshd 进程被标记为ssh_t,它只允许绑定到带有ssh_port_t标签的端口。你改了配置但没通知 SELinux,它当然继续按旧策略拦截。这个逻辑理解透了,以后遇到类似问题就不会一头雾水。
6. 资源耗尽、客户端干扰与虚拟机场景
6.1 磁盘满、日志刷爆、连接数耗尽
有些 22 端口“无法访问”其实和设备资源有关,这类问题表现得比较隐蔽,因为看起来配置文件都对。
第一个常见场景是根分区磁盘写满。用df -h看/的使用率,如果 100%,sshd 在写入 utmp、读取 authorized_keys、创建 session 文件时都会失败。表现可能是客户端输入密码后卡死,或者连接直接被拒。处理方式就是清理磁盘,通常先找/var/log下的大文件,再用journalctl --vacuum-size=200M收缩日志。
第二个场景是/var/log/secure被刷爆。如果服务器暴露在公网,被扫描器暴力破解,日志文件可能涨到几个 GB。日志写不进去时,sshd 的认证流程可能异常缓慢甚至无响应。处理方式是删掉旧日志并重启 rsyslog,同时建议启用 fail2ban 或修改 SSH 端口降低被扫概率。
第三个场景是文件描述符耗尽或进程数达到上限。查看:
ulimit -n cat /proc/sys/fs/file-nr如果已分配文件描述符接近系统上限,sshd 无法 accept 新连接。表现是连接立即被拒或极慢。这种问题通常需要通过调大fs.file-max和进程的ulimit来解决,但更重要的是先找到为什么会有这么多文件描述符被占。曾经遇到过 coredump 文件堆积把inode耗尽的案例,df -i一看发现 inode 满,删除大量临时文件后才恢复。
6.2 客户端侧也有“假故障”
有一种情况很气人:服务器完全正常,问题出在客户端自己身上。
最常见的是 known_hosts 冲突。换过服务器系统或者恢复过快照后,客户端的~/.ssh/known_hosts里还存着旧的指纹,连接时会报:
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!这时候很多人会不知所措,实际上删除对应的 known_hosts 条目即可:
ssh-keygen -R 192.168.31.10另外一个隐蔽因素是客户端开了全局代理。我用 Windows 上某些 SSH 工具连接内网 Linux 服务器时,只要系统代理开着,连接就失败,关了代理立刻恢复正常。这个原因在文档里很少被提及,但真实场景遇到概率不小。
多角度验证也很有用。如果服务器连不上,拿另一台电脑、手机上的 Termius、或者换个网络环境试一试。如果其他设备能连,只有你这台机器不能连,那问题的范围基本锁定在客户端,不必再折腾服务器。
6.3 VMware 虚拟机网络模式的影响
很多新手是在 VMware 里装的 CentOS 7,然后发现 22 端口连不上。这时候要先看虚拟机的网络模式,因为不同的模式决定了谁能访问这台虚拟机。
VMware 三种网络模式的区别整理如下。
| 模式 | 虚拟机 IP 特征 | 宿主机能否访问 | 局域网其他机器能否访问 |
|---|---|---|---|
| NAT | 192.168.x.x,由 VMnet8 分配 | 能 | 默认不能,需端口转发 |
| 桥接 | 与宿主机同网段 | 能 | 能,前提是 IP 配置正确 |
| 仅主机 | 192.168.x.x,由 VMnet1 分配 | 能 | 不能 |
最常见的坑是选 NAT 模式,然后局域网里另一台电脑想 SSH 连这台虚拟机,怎么都连不上。这不是 CentOS 7 的问题,而是 NAT 模式本身就不允许外部主动访问。解决方式有两种,要么改用桥接模式,要么在 VMware 的 NAT 设置里添加端口转发,把宿主机的某个端口转发到虚拟机的 22 端口。
此外,虚拟机内部网卡可能没有“已连接”。编辑虚拟机设置,确认网络适配器那里已勾选“已连接”和“启动时连接”。我遇到过一台虚拟机关机后再次开机,网卡连接状态丢失,导致ip addr里根本没有 IP 地址,这类问题不到虚拟机设置里看一眼很难发现。
7. 排障速查表与几条实战体会
7.1 常用排障命令速查表
把多年积累的排查命令整理成一张表,遇到问题可以按顺序来。
| 现象 | 可能原因 | 先执行什么 |
|---|---|---|
| ping 报“无法访问目标主机” | ARP 不通、目标不在链路内 | ip neigh、检查网段 |
| ping 超时 | ICMP 被禁或路由问题 | 用nc -vz IP 22验证 TCP |
| telnet 显示 Connection refused | sshd 没监听或防火墙 REJECT | systemctl status sshd |
| telnet 超时 | 防火墙 DROP 或回程路由错误 | firewall-cmd --list-all、tcpdump |
| 防火墙规则里有 22 端口仍连不上 | SELinux 拦截或 sshd 监听错误地址 | getenforce、ss -tlnp |
| 密码正确但认证失败 | 磁盘满、authorized_keys 权限错、UseDNS | df -h、tail /var/log/secure |
| 换网络环境能连,原网络不行 | 客户端代理、IP 冲突、宿主机防火墙 | 关闭代理、换 IP 测试 |
这张表不能覆盖所有情况,但能覆盖八成以上“22端口无法访问”的问题。剩下的就需要结合日志和抓包深入分析了。
7.2 三条实战体会
第一个体会是:先用nc或telnet测端口,别急着改服务器。命令结果直接告诉你问题是“拒绝”还是“超时”,方向完全不一样。拒绝查服务和防火墙 REJECT,超时查防火墙 DROP 和路由。
第二个体会,也是踩过坑才明白的:不要为了方便直接永久关闭防火墙。见过太多人systemctl disable firewalld然后服务器被扫描爆破,SSH 被塞满垃圾日志。正确做法是只放行必要端口,能用 rich rule 限制来源 IP 就尽量限制。22 端口是登录入口,安全等级本来就该比普通服务高。
第三个体会和配置安全相关:改 sshd 配置前先备份,改完先sshd -t自检,再重启服务。我唯一一次被锁在云主机外面就是深夜手滑把 Port 写错,重启后 sshd 直接起不来。从那以后所有涉及 SSH 的改动,都必须先跑语法检查。这个习惯救了我很多次,也希望这次分享能帮到你。
CentOS 7 已经服役多年,这类端口访问问题本质都不难,难的是被各种环境变量干扰。希望这篇排障记录能让你下次面对 22 端口问题时,少走几步弯路。