别再把 SSH 当成一个神秘的登录框,它其实很简单:客户端连上服务器,先核验服务器身份,再证明用户身份。大多数故障信息都会直接告诉你,问题出在哪一步。
🎙️ 发布并录制于: · 更新于 ·
私钥只留在你的电脑上。公钥可以复制到各台服务器。登录时,服务器会让你证明自己持有私钥,但私钥本身不会上传。服务器上的 ~/.ssh/authorized_keys 每多一行,就代表允许一个公钥访问这个账户。
# 生成一对现代密钥;请设置密码短语。
ssh-keygen -t ed25519 -a 100 -C "laptop-2026"
~/.ssh/id_ed25519 私钥——绝不要发给别人
~/.ssh/id_ed25519.pub 公钥——把它安装到服务器上
# 安全地查看公钥:
cat ~/.ssh/id_ed25519.pub一个 SSH 目标包含四项信息:用户、主机、端口和身份密钥。默认值会藏起其中三项,平时很方便,用错时却很难察觉。连接失败后,给原命令加一个 -v。按顺序看网络连接、主机密钥检查和身份验证。
ssh [email protected]
ssh -p 2222 [email protected]
# 诊断视图;在 -v 信息不够之前,-vvv 通常只会增加干扰。
ssh -v -p 2222 [email protected]
# 值得留意的输出:
debug1: Connecting to server.example.com [203.0.113.10] port 2222.
debug1: Server host key: ssh-ed25519 SHA256:...
debug1: Offering public key: /home/alice/.ssh/id_ed25519远程用户名一定要写准确。你电脑上的用户名,不能证明服务器账户也叫这个名字。云主机镜像常用 ubuntu、ec2-user,也可能要求其他预先创建的账户。
看到这条原始报错,说明网络和 SSH 服务都正常,失败的是身份验证。此时不要再查防火墙。先确认远程用户名,再看客户端提交了哪把密钥。然后强制使用目标密钥,并确认配套公钥装在服务器的那个账户下。
[email protected]: Permission denied (publickey).
# 1. 详细输出中有没有 "Offering public key"?
ssh -v [email protected]
# 2. 强制使用目标身份密钥,并忽略意外出现的其他密钥:
ssh -o IdentitiesOnly=yes -i ~/.ssh/work_ed25519 [email protected]
# 3. 比对指纹,不要比文件名:
ssh-keygen -lf ~/.ssh/work_ed25519.pub
# 在服务器控制台中:
ssh-keygen -lf /home/alice/.ssh/authorized_keysauthorized_keys 是否缺少对应记录。接着查权限,最后才查服务器策略。不要随手重新生成密钥,那会破坏线索,还可能把一把错密钥变成两把。如果其他用户也能读取私钥,OpenSSH 会拒绝使用它。如果错误的人能写入目录或文件,服务器也可能忽略 authorized_keys。先修正所有者,再修正权限模式。文件属于 root 时,单用 chmod 解决不了问题。
WARNING: UNPROTECTED PRIVATE KEY FILE!
Permissions 0644 for 'id_ed25519' are too open.
# 客户端:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
# 服务器端,以目标账户为准执行:
chown -R alice:alice /home/alice/.ssh
chmod 700 /home/alice/.ssh
chmod 600 /home/alice/.ssh/authorized_keys在 Windows 上,用文件的“安全”设置或 icacls,移除无关用户继承到的访问权限。把 Unix 权限数字照搬进 PowerShell,不是跨平台解决办法。真正的规则只有一条:私钥只能由所有者读取。
主机密钥用来识别服务器。密钥变化,可能是正常重装、IP 被重新分配,也可能有人正在拦截连接。这条警告无法替你判断,所以必须自行核验。不要一看到它就删除记录后重连。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
Offending ED25519 key in /home/alice/.ssh/known_hosts:12
Host key verification failed.
# 1. 通过可信渠道核验新指纹:
# 云控制台、服务商元数据,或管理员。
# 2. 只删除这个主机的旧记录:
ssh-keygen -R server.example.com
ssh-keygen -R "[server.example.com]:2222"
# 3. 重新连接,并比对屏幕显示的指纹。StrictHostKeyChecking=no 不是解决办法。它只是把碍眼的警告换成悄无声息的冒充。核验只花一分钟;跳过这一步,就等于放弃 SSH 判断凭据究竟交给了哪台机器的能力。这两类故障都发生在身份验证之前。Connection refused 是目标很快给出的答复:主机能到达,但该端口没有服务接收连接,或者防火墙主动拒绝了连接。连接超时则表示回复被丢弃,或路由不通。不要把两者都含糊地叫作“SSH 挂了”。
ssh: connect to host server.example.com port 22: Connection refused
→ 检查端口、sshd 是否监听,以及服务状态。
ss -ltn | grep ':22'
systemctl status sshd # 有些系统把服务命名为 ssh
ssh: connect to host server.example.com port 22: Connection timed out
→ 检查 DNS/IP、VPN、路由、安全组和防火墙允许列表。
ssh -v -o ConnectTimeout=8 [email protected]请从实际使用的客户端网络,测试真正的主机名和端口。在服务器本机测试成功,几乎不能证明入站防火墙没有问题。等 TCP 连接成功后,再回头检查密钥和用户名。
命令行适合临时试验,长期使用的主机应写进 ~/.ssh/config。给机器起个好记的别名,并固定用户、端口和身份密钥。如果配置继承的结果出乎意料,就用 ssh -G alias 查看最终生效的完整配置。
Host reports
HostName reports.internal.example.com
User alice
Port 2222
IdentityFile ~/.ssh/work_ed25519
IdentitiesOnly yes
Host *.internal.example.com
ServerAliveInterval 30
ServerAliveCountMax 3
# 现在可以这样用:
ssh reports
scp results.csv reports:/srv/import/
ssh -G reports | less密钥代理保存已经解锁的签名能力,让你不必每次连接都输入密码短语。它不会复制私钥。如果客户端提交了错误的身份,就检查代理载入了哪些密钥。访问私有网络时,请使用跳板机,不要登录堡垒机后把私钥存到那里。
# 查看代理中的身份,并载入本地密钥:
ssh-add -l
ssh-add ~/.ssh/work_ed25519
The agent has no identities. # 载入一把密钥,或用 -i 指定
# 通过堡垒机访问内部主机:
ssh -J [email protected] [email protected]
# 对应的配置:
Host private-app
HostName 10.0.4.12
User app
ProxyJump [email protected]默认不要启用代理转发。只要会话还开着,被入侵的远程主机就可能要求你的转发代理代为签名。ProxyJump 可以转发连接,却不会把代理访问权交给堡垒机,因此更适合作为默认选择。
本地转发会在你的电脑上打开一个端口,再通过 SSH 把流量送到服务器一侧可访问的目标。-L 8080:db.internal:5432 可以这样读:本机监听 8080,然后从远程一侧连接 db.internal 的 5432 端口。只需要隧道、不需要 shell 时,请加 -N。
# 本机 15432 → gateway 能访问的数据库
ssh -N -L 15432:db.internal:5432 gateway
psql -h 127.0.0.1 -p 15432 appdb
# 本机 8080 → 绑定在远程 localhost:3000 的服务
ssh -N -L 8080:127.0.0.1:3000 app-server
# 在浏览器中打开 http://127.0.0.1:8080
# 转发无法建立时立即失败:
ssh -N -o ExitOnForwardFailure=yes -L 8080:127.0.0.1:3000 app-server0.0.0.0。后者可能把内部数据库或管理面板暴露给整个本地网络。隧道只提供私密传输,不会自动提供访问控制。改动任何东西之前,先判断故障处在哪个阶段。运行一次详细模式,再按下面的顺序检查,通常比胡乱猜二十次更快。
□ DNS 是否解析到了预期 IP?
□ 端口是否正确、可以到达,而且确实在监听?
□ 是拒绝连接,也就是主机可达但端口关闭,还是超时,也就是数据被丢弃或没有路由?
□ 是否核验了服务器主机密钥的指纹?
□ 远程用户名是否完全正确?
□ `ssh -v` 显示正在提交哪把密钥?
□ 那把公钥是否在该用户的 authorized_keys 中?
□ 两端的所有者和权限是否足够严格?
□ 配置展开后,`ssh -G alias` 显示什么?
□ 代理是否持有目标身份密钥?
□ 建立隧道时,究竟由哪台机器解析目标主机?
□ 是否只绑定到 loopback,并使用了 ExitOnForwardFailure?把各个阶段分开看。先连到端口,再确认服务器身份,然后验证用户身份,最后才打开通道或隧道。只要不再把所有问题都当成一种登录失败,SSH 的报错就很好用。