问题现象
通过 Cloudflare Tunnel 做 SSH 代理的环境下:
ssh ssh.example.com✅ 正常连接ssh racknerd❌ 连接失败(直接尝试 TCP 22 端口)
原始配置
Host racknerd
Hostname ssh.example.com
Host ssh.example.com
User root
IdentityFile ~/.ssh/vv
ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
根因分析
SSH config 的核心匹配规则:
- 按命令行输入的别名匹配,不会用
Hostname解析后的值做二次匹配 - 多个
Host块匹配时,首次出现的指令生效(first-match wins)
当执行 ssh racknerd 时:
| 步骤 | 匹配情况 | 结果 |
|---|---|---|
遍历 Host 行 | racknerd 匹配第一个块 | 只拿到 Hostname ssh.example.com |
| 继续匹配 | ssh.example.com 行不匹配别名 racknerd | ProxyCommand / User / IdentityFile 全部缺失 |
| 实际行为 | 无 ProxyCommand,直接 TCP 连接 | 连接 ssh.example.com:22 失败 |
关键误区
Hostname指令只改变最终连接的目标地址,不会触发其他 Host 块的匹配。%h展开为Hostname的值,但匹配逻辑在此之前已经完成。
修复方案
利用 SSH config 的多块合并特性:同一个别名可以同时匹配多个 Host 块,所有匹配块的指令会合并生效。
Host racknerd
Hostname ssh.example.com
Host racknerd ssh.example.com
User root
IdentityFile ~/.ssh/vv
ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
修复后 ssh racknerd 的解析结果
用 ssh -G racknerd 验证:
hostname ssh.example.com
user root
identityfile ~/.ssh/vv
proxycommand /usr/local/bin/cloudflared access ssh --hostname %h
- 第一个块提供:
Hostname ssh.example.com(%h展开为ssh.example.com) - 第二个块提供:
User、IdentityFile、ProxyCommand
排查工具:ssh -G
ssh -G 用于输出指定别名的完整解析配置,不会建立实际连接,是排查 SSH config 问题的首选工具。
功能说明
-G 选项会让 SSH 将经过所有 Host 块合并后的最终配置以 key value 形式输出到 stdout。可以清晰看到:
- 实际连接的
hostname(含Hostname指令的解析结果) - 使用的
user、identityfile、port proxycommand的完整命令(%h、%p等占位符已展开)- 以及所有其他生效的 SSH 选项
常用过滤
# 查看关键连接参数
ssh -G <alias> | grep -E '(hostname|user|identityfile|proxycommand|port)'
# 查看完整配置(输出较长)
ssh -G <alias>
本次排查中的应用
修复前,通过 ssh -G racknerd 发现 hostname 仍为 racknerd(非 ssh.example.com),且缺少 proxycommand,直接定位到配置合并不完整的问题。
# 修复前 — hostname 错误,缺少 proxycommand
$ ssh -G racknerd | grep -E '(hostname|proxycommand)'
hostname racknerd # ← 未解析到 ssh.example.com
# 修复后 — hostname 和 proxycommand 均正确
$ ssh -G racknerd | grep -E '(hostname|proxycommand)'
hostname ssh.example.com
proxycommand /usr/local/bin/cloudflared access ssh --hostname ssh.example.com
类似选项
ssh -T可测试连接并输出 debug 信息,ssh -v显示连接过程详细日志。-G的优势在于无需网络连接即可查看配置解析结果,适合纯配置排障。
验证连接
# 测试实际连接
ssh <alias> "echo connected"
相关场景
类似的 Hostname 别名场景都可以用这个模式处理:
Host my-alias
Hostname actual.host.com
Host my-alias actual.host.com other-alias
User root
IdentityFile ~/.ssh/key
ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
核心原则:把别名同时加入包含连接参数(ProxyCommand/User/IdentityFile)的 Host 行。