问题现象

通过 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 的核心匹配规则:

  1. 按命令行输入的别名匹配,不会用 Hostname 解析后的值做二次匹配
  2. 多个 Host 块匹配时,首次出现的指令生效(first-match wins)

当执行 ssh racknerd 时:

步骤匹配情况结果
遍历 Host 行racknerd 匹配第一个块只拿到 Hostname ssh.example.com
继续匹配ssh.example.com 行不匹配别名 racknerdProxyCommand / 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 行。