我第一台云服务器的第一课,就是 SSH 连不上。
当时我的反应,跟大多数人一样:重启。重启完还是连不上,然后开始在网上乱搜,改sshd_config、重装 ssh、甚至重装了系统——折腾三个小时,最后发现是防火墙把 22 端口关了。
重启这个动作最坑的地方在于:它会把现场擦掉。你之后再想查「刚才到底为什么连不上」,日志已经翻篇了。
这篇我按「分层」来讲。SSH 连不上只有四种可能,按这四层一层层往下走,最多十分钟能定位。
〇、先花两分钟:SSH 连接的四层结构
把 SSH 想成打电话:
| 层 | 打电话的对应 | 出问题的表现 |
|---|---|---|
| 1. 网络层 | 号码拨得通吗 | Connection timed out |
| 2. 端口层 | 对方有这条线吗 | Connection refused |
| 3. 服务层 | 有人接吗 | 连上就断、卡在登录前 |
| 4. 认证层 | 能确认身份吗 | Permission denied、Host key verification failed |
注意报错信息的区别——它其实在告诉你卡在第几层:
Connection timed out:包发出去了,没人应答→ 第 1 或第 2 层(防火墙拦了、机器没开、IP 错了);Connection refused:有人应答,但明确拒绝→ 机器活着,端口上没有服务在听(服务没起、端口改了);能连上但登不进去 → 第 4 层(认证)。
这一层区分能省掉一半时间:refused是「服务的问题」,timed out是「网络或被拦的问题」。
一、第 1~3 步:网络层(先证明机器活着)
第 1 步:ping 一下
$ ping -c 3 192.168.1.50 3 packets transmitted, 3 received, 0% packet loss
怎么读:有回包说明机器活着、网络通。没回包别急着下结论——很多云服务器和防火墙默认丢弃 ICMP,ping 不通但 SSH 是好的。所以 ping 只当辅助证据。
第 2 步:确认 IP 和端口没记错
这一步听着傻,但真的有人(包括我)连了半天发现连的是另一个环境的机器。确认:
# 本机 IP(在服务器上执行) $ ip -br addr ens33 UP 192.168.1.50/24
第 3 步:换一条路径试
从另一台机器试,或者用手机热点试。如果能从别的地方连上,问题就在你这条网络路径上(本地网络、公司出口、VPN 分流),不在服务器。
$ nc -vz 192.168.1.50 22 Connection to 192.168.1.50 22 port [tcp/ssh] succeeded!
怎么读:succeeded说明端口是开着的,可以直接跳到第 9 步去查认证。Connection refused说明机器活着但没服务在听——直接看第 4 步。
(nc是 netcat,Ubuntu 上可能需要sudo apt install netcat-openbsd。不方便装的话,第 5 步的ss命令也能起到类似作用。)
二、第 4~6 步:端口层(服务在不在)
第 4 步:服务起了吗?注意 Ubuntu 上它叫ssh不叫sshd
$ systemctl status ssh ● ssh.service - OpenBSD Secure Shell server Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled) Active: active (running) since Sat 2026-09-26 09:12:03 CST; 1 day 3h ago
怎么读:Active: active (running)才算正常。
⚠️ 这一步有个大坑:Ubuntu / Debian 系的服务名叫ssh,RHEL / CentOS 系叫sshd。在 Ubuntu 上敲systemctl status sshd会告诉你「找不到这个单元」,很容易被误判成「服务没装」。
更绕的是,桌面版上你搜ssh也搜不到它——因为服务端压根没装,能搜出来的全是别的东西:
$ systemctl list-unit-files | grep -iE 'ssh|sshd' sssd-ssh.service indirect enabled sssd-ssh.socket enabled enabled ssh-access.target static -
怎么读:sssd-ssh是「SSSD(企业账号认证服务)的一个组件」,ssh-access.target是个目标单元——三个都不是 SSH 服务端。搜不到ssh.service,就是没装。
服务没起就拉起来:
sudo systemctl start ssh && sudo systemctl enable ssh
(enable是设开机自启——服务「起过」和「会自动起」是两件事,这个坑在Linux避坑 02里详细讲过。)
第 5 步:服务端到底装了没有
这是最容易被忽略的一种情况,而且在 Ubuntu 桌面版上是默认状态。
Ubuntu 桌面版默认不安装 OpenSSH 服务端(只有客户端)。也就是说:
你能从这台机器
ssh去连别人(客户端装了);但别人连不进来——因为压根没有服务端。
$ dpkg -l | grep -E 'openssh|ssh' ii libssh2-1t64:amd64 1.11.1-1ubuntu0.26.04.4 amd64 SSH2 client-side library ii openssh-client 1:10.2p1-2ubuntu3.6 amd64 secure shell (SSH) client $ ss -tlnp | grep :22 (无输出)
怎么读(这是我在自己那台 26.04.1 上跑的真实结果):
dpkg -l里只有openssh-client(客户端),没有openssh-server(服务端)→ 你能连出去,别人连不进来;ss -tlnp里没有任何:22→ 没有程序在监听 22 端口。
两条合起来,就是「服务端不存在」的铁证。
(顺便留意第一行那个libssh2-1t64——它是客户端用的库,名字里有 ssh,但不是服务端,别被带跑。)
要装:
sudo apt install openssh-server
第 6 步:服务在听,但只听本机
有些配置(尤其容器镜像、加固过的系统)会把监听地址限制成127.0.0.1——只接受本机连接,外面永远连不上。
$ ss -tlnp | grep :22 LISTEN 0 128 127.0.0.1:22 0.0.0.0:* users:(("sshd",pid=812,fd=3))怎么读:关键看:22前面那一段地址。
| 显示 | 含义 |
|---|---|
0.0.0.0:22 | 所有网卡都听——外部可以连(正确状态) |
[::]:22 | 同上,走 IPv6 |
127.0.0.1:22 | 只听本机——外部连不上,问题就在这 |
对应的配置在/etc/ssh/sshd_config里的ListenAddress。
对照一下就知道「地址」这一列有多关键——这是我机器上全部在监听的端口,一个 22 都没有:
$ ss -tlnp LISTEN 0 4096 127.0.0.1:631 0.0.0.0:* ← 打印服务,只听本机 LISTEN 0 50 0.0.0.0:445 0.0.0.0:* ← Samba,所有网卡都听 LISTEN 0 50 0.0.0.0:139 0.0.0.0:* ← Samba,同上 LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* ← systemd-resolved,只听本机
怎么读:0.0.0.0:445= 外面连得进来;127.0.0.1:631= 只有本机能连。同样是「在监听」,地址不同,含义完全相反——这就是第 6 步要教你的判断。
三、第 7~8 步:防火墙(最常见的元凶)
第 7 步:本机防火墙
$ sudo ufw status 状态:不活动
怎么读:不活动= ufw 没在拦(此时去看看 nftables / iptables 有没有别的规则);活动就要看规则里有没有放行 22:
⚠️ 这里有个特别容易误判的地方,我在自己机器上就撞到了:
$ systemctl is-active ufw active $ systemctl show -p ActiveState -p SubState -p Type -p RemainAfterExit ufw.service ActiveState=active SubState=exited Type=oneshot RemainAfterExit=yes
看到active就以为防火墙开着?不是。把后一条命令的四行连起来读就懂了:Type=oneshot(跑一遍就退)+RemainAfterExit=yes(跑完别退,停在 active 上)+SubState=exited(进程其实已经结束了)——这个active只表示「启动脚本执行过」,跟「防火墙有没有启用」是两件事。
那真正的开关在哪?在这个文件里:
$ cat /etc/ufw/ufw.conf ENABLED=no LOGLEVEL=low
ENABLED=no—— 这才是权威答案,和sudo ufw status报的「不活动」完全一致。
判断防火墙,永远以sudo ufw status的输出为准,别看systemctl is-active。
顺便说下verbose版长什么样——没启用时它只回一句(我机器上就是这样):
$ sudo ufw status verbose 状态:不活动
启用之后才会列出规则表,格式如下(这台机器上没启用,所以下面是格式参考):
状态:活动 日志记录:on (low) 默认:deny (incoming), allow (outgoing), disabled (routed) 新配置文件:skip To Action From -- ------ ---- 22/tcp ALLOW Anywhere
RHEL 系用firewall-cmd --list-all,看ports或services里有没有ssh。
⚠️第一句提醒:如果你正通过 SSH 连着服务器,在敲sudo ufw enable之前,先确认 22 端口已经放行:
sudo ufw allow 22/tcp # 先放行 sudo ufw enable # 再启用
顺序反了,你会当场把自己锁在门外——这个教训值得单独记一条。
第 8 步:云安全组 / 机房设备
如果你用的是云服务器,安全组是最常见的拦路虎,而且在服务器里怎么查都查不出来——因为规则在云平台的控制台上。
排查方式:打开云控制台,看这台实例绑定的安全组入方向规则里有没有放行 22(很多默认策略只放 80/443,或者只允许特定来源 IP)。
不要把「服务器内部一切正常」等同于「网络是通的」——云安全组、机房 ACL、公司出口设备,都在你看不见的地方。
四、第 9~12 步:认证层(能连上,但登不进去)
到这里连接已经建立,问题在身份认证。
第 9 步:看服务端的拒绝日志
$ sudo journalctl -u ssh -n 30 Sep 27 11:02:31 host sshd[2311]: Failed password for alice from 192.168.1.9 port 51234 ssh2 Sep 27 11:02:33 host sshd[2311]: Connection closed by authenticating user alice 192.168.1.9 port 51234 [preauth]
怎么读:Failed password= 密码不对;Connection closed by authenticating user ... [preauth]= 认证阶段被断开(多半是密钥或配置问题);如果什么都没有,说明请求根本没到达服务(回到第 7、8 步)。
日志位置:Ubuntu 在 journald(上面这条命令),也能在/var/log/auth.log里找到;RHEL 系在/var/log/secure。
第 10 步:密钥登录失败?九成是权限问题
SSH 对密钥文件的权限极其挑剔——只要它觉得别人有可能读到你的私钥,就直接拒绝使用。
服务端的检查清单(下面是我机器上的真实状态):
$ ls -ld ~/.ssh ~/.ssh/authorized_keys drwx------ 2 rainbow rainbow 4096 Sep 16 15:24 /home/rainbow/.ssh -rw------- 1 rainbow rainbow 0 Sep 16 15:24 /home/rainbow/.ssh/authorized_keys
怎么读:drwx------(700)和-rw-------(600)是对的,权限没问题。但注意那个0——文件大小是 0,里面一个公钥都没写。这种「文件在、内容空」的状态最容易被忽略:登录当然失败,而日志里只会干巴巴地说一句「认证失败」。
怎么读:.ssh目录必须是700(drwx------),authorized_keys必须是600(-rw-------),而且属主必须是你自己。多一个组读权限,SSH 都可能不认。
一条命令修好:
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys && chown -R $USER:$USER ~/.ssh
还有一条隐蔽的:家目录本身不能是 777。/home/alice权限过于宽松时,SSH 同样会拒绝——因为它认为别人可以替换你的.ssh。
第 11 步:服务端配置在"故意"拦你
/etc/ssh/sshd_config里几个常见开关:
| 配置项 | 效果 |
|---|---|
PermitRootLogin no | root 不允许直接登录(默认值,很多人不知道) |
PasswordAuthentication no | 禁用密码,只认密钥 |
AllowUsers alice | 白名单,只有列出的用户能登 |
DenyUsers | 黑名单 |
MaxAuthTries 3 | 认证失败 3 次就断开 |
Port 2222 | 改了端口,客户端要用-p 2222 |
改完配置一定要做两件事:
sudo sshd -t # 先检查语法(错了它会告诉你哪一行) sudo systemctl reload ssh # 再让服务重读配置
sshd -t这步千万别省。配置写错了直接 reload 服务,结果服务起不来——而你正通过 SSH 连着,下一次连接就再也进不去了。
第 12 步:主机密钥变了(Host key verification failed)
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
这不是故障,是安全机制在工作:你以前连过这台机器,它当时的「指纹」记录在~/.ssh/known_hosts里;现在指纹变了,客户端就报警——因为它无法区分「服务器重装了」和「有人在中间冒充服务器」。
服务器重装过、或者你换了台机器复用同一个 IP,都会触发这个警告。
确认是自己的机器变了,清掉旧记录即可:
ssh-keygen -R 192.168.1.50
反过来说,如果你没重装过系统却看到这个警告,别急着清——先确认机器是不是被人动过。
顺便提一个 2026 年的新情况:新版 OpenSSH 已经不再支持 DSA 密钥(我机器上是OpenSSH_10.2p1),所以那些老教程里ssh-keygen -t dsa的做法现在会直接被拒。现在该用的是ed25519:
ssh-keygen -t ed25519 -C "备注信息"
五、客户端自查:三条命令看清"我这里发生了什么"
如果服务器那边一切正常,用客户端的详细模式看:
$ ssh -v alice@192.168.1.50 debug1: Connecting to 192.168.1.50 [192.168.1.50] port 22. debug1: Connection established. debug1: Authentications that can continue: publickey,password
怎么读:-v会把每一步都打出来。
卡在
Connecting to ...不动 → 网络层(第 1~3 步);Connection established之后卡住 → 服务层;Authentications that can continue:这行很有用——它列出服务端允许的认证方式。如果里面有publickey但你只有密码,就知道该配密钥了。
嫌不够详细可以叠-vvv。
速查表(文末收藏版)
先分清报错 → timed out=网络/防火墙 refused=端口没服务 机器活着吗 → ping -c 3 IP 端口通吗 → nc -vz IP 22 服务状态(Ubuntu) → systemctl status ssh ← 服务名是 ssh,不是 sshd 服务状态(RHEL 系) → systemctl status sshd 服务端装了吗 → dpkg -l | grep -E 'openssh|ssh' (桌面版默认只有 -client) 别用这个判断防火墙 → systemctl is-active ufw ← active 不代表防火墙开着(它是 oneshot) 防火墙真正的开关 → /etc/ufw/ufw.conf 里的 ENABLED= 谁在听 22 → ss -tlnp | grep :22 (看是 0.0.0.0 还是 127.0.0.1) 本机防火墙 → sudo ufw status verbose (RHEL:firewall-cmd --list-all) 启用防火墙前先放行 → sudo ufw allow 22/tcp && sudo ufw enable 云服务器 → 控制台看安全组入方向规则 服务端拒绝日志 → sudo journalctl -u ssh -n 30 密钥权限 → chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys 配置语法检查 → sudo sshd -t 重读配置(不断连接) → sudo systemctl reload ssh 主机密钥变了 → ssh-keygen -R IP 客户端详细排查 → ssh -v 用户名@IP 生成新密钥(用这个) → ssh-keygen -t ed25519 -C "备注"