| CI | 代码 | OpenSpec | 安全 |
|---|---|---|---|
|
|
|
|
|
网络感知的 SSH 路由器。它能检测你当前激活的网络或 VPN,并自动为每次 SSH 连接选择正确的主机、端口、身份文件和跳板机——无需修改 ~/.ssh/config。
为每个逻辑主机定义一次,包含一个 default 配置文件以及可选的按网络覆盖项。在每次连接时,sshroute 会检测你当前所处的网络(VPN、公司局域网、WireGuard 对端等),解析出正确的 SSH 参数,然后交给真实的 /usr/bin/ssh 执行。
ssh myserver
→ sshroute 检测到:corp-vpn 已激活
→ 解析出:10.100.0.50:2222 通过 bastion.corp.internal
→ exec /usr/bin/ssh -p 2222 -i ~/.ssh/corp_key -J bastion.corp.internal 10.100.0.50
你的实验室很可能至少存在两种现实状态:要么在家通过局域网连接,要么外出通过 WireGuard 或其他 VPN 接入。问题在于 ~/.ssh/config 不知道你当前在哪种状态下——所以你最终不得不为同一台主机创建不同的别名(server-lan、server-vpn),或者使用一个只在一半情况下有效的跳板机,又或者干脆记住 IP 地址。
sshroute 通过在每次连接前检测当前网络来解决这个问题。当 WireGuard 接口启动且对端路由存在时,它直接连接隧道 IP;当你在局域网时,它使用本地地址;当两者都不可达时,它回退到公网主机名。一个别名,三种现实,无需手动切换。
此外,它还能透明地拦截 SSH——一旦设置好影子模式,git push、rsync、scp 都会自动通过它处理。无需包装器、无需 shell 函数、无需思考。
企业网络更复杂。你有公网、可能有的站点到站点 VPN、可能有的个人 VPN 分流隧道,在这些内部,根据目标环境的不同——开发、测试、生产,每个都有自己的堡垒机和密钥——你还需要不同的跳板机。在 ~/.ssh/config 中保持这一切的清晰管理,要么是一个一旦基础设施变化就会出错的庞然大物,要么是团队中每个人都以不同方式维护的脚本。
sshroute 允许你声明性地定义路由逻辑,保存在版本化的 YAML 文件中,并在整个团队中共享。同一份配置文件对所有人都有效——根据每台机器上激活的接口或路由自动检测正确的网络。密钥、端口、用户和跳板机自动解析,用户无需思考。
| 特性 | ~/.ssh/config | 仅 WireGuard | Teleport / Boundary | sshroute |
|---|---|---|---|---|
| 检测当前网络 | ❌ | ❌ | ❌ | ✅ |
| 自动选择最佳路径 | ❌ | ❌ | ❌ | ✅ |
| 连接失败时回退 | ❌ | ❌ | ✅ | ✅ |
| 断开后自动重连并重新路由 | ❌ | ⚠️ 隧道漫游 | ⚠️ 通过固定代理 | ✅ |
| 每个主机一个命令,任何位置 | ❌ | ⚠️ VPN 必须启用 | ✅ | ✅ |
| 10 个主机 × 4 种路径的配置大小 | 📄 ~600 行 | 📄 ~600 行 + VPN 配置 | 📄 服务端配置 | 📄 ~60 行 |
| 移动设备漫游 | ⚠️ 手动别名 | ⚠️ 需要 VPN | ✅ | ✅ |
| 跳板机自动链式连接 | ⚠️ 手动 -J | ➖ 不适用 | ✅ | ✅ |
| 适用于 scp / rsync / git / Ansible | ✅ | ✅ | ⚠️ 部分 | ✅ |
| 目标端无服务端安装 | ✅ | ❌ | ❌ | ✅ |
| 无需认证服务器或守护进程 | ✅ | ❌ | ❌ | ✅ |
| 无客户端代理 | ✅ | ❌ | ❌ | ✅ |
| 开源,完全自托管 | ✅ | ✅ | ⚠️ 开放核心 | ✅ |
Teleport 和 Boundary 属于不同类别——它们在路由之上增加了访问控制、审计日志和基于证书的认证。如果你需要这些,请使用它们。sshroute 适用于你希望获得路由智能但不想承担运行中央认证服务器的运维开销的场景。
从 GitHub 发行版 下载最新版本。提供 Linux、macOS 和 Android 的 AMD64 和 ARM64 二进制文件。
go install github.com/thereisnotime/sshroute@latest
从 GitHub 发行版 下载 android_arm64 压缩包,解压后将二进制文件放入 ~/.local/bin:
mkdir -p ~/.local/bin
curl -Lo "$TMPDIR/sshroute.tar.gz" \
https://github.com/thereisnotime/sshroute/releases/latest/download/sshroute_android_arm64.tar.gz
tar -xzf "$TMPDIR/sshroute.tar.gz" -C ~/.local/bin sshroute
chmod +x ~/.local/bin/sshroute
如果尚未添加,在 ~/.bashrc 或 ~/.profile 中将 ~/.local/bin 加入 PATH:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
或者,使用 Termux 的 Go 从源码编译。由于官方 Go 工具链不提供 android/arm64 二进制文件,请设置 GOTOOLCHAIN=local 以使用 Termux 自带的版本:
GOTOOLCHAIN=local go install github.com/thereisnotime/sshroute@latest
安装后,由于 Termux 没有 /usr/bin/ssh,需要设置 SSH 二进制路径:
# ~/.config/sshroute/config.yaml
ssh_binary: /data/data/com.termux/files/usr/bin/ssh
或者通过环境变量:export SSHROUTE_SSH=$(which ssh)
docker run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
在启用了 SELinux 的系统(Fedora、RHEL 等)上,请在卷参数后添加 :Z:
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute:Z \
ghcr.io/thereisnotime/sshroute network
将 sshroute 作为 ssh 安装在 $PATH 中更靠前的位置。所有 SSH 调用——来自终端、git、rsync、scp——都会自动被拦截。配置文件中未定义的主机将原封不动地传递到 /usr/bin/ssh。
mkdir -p ~/.local/bin
ln -s $(which sshroute) ~/.local/bin/ssh
# 如果尚未添加,请添加到 ~/.bashrc 或 ~/.zshrc:
export PATH="$HOME/.local/bin:$PATH"
# 添加一个带有默认配置的主机
sshroute add myserver --host myserver.example.com --user alice --key ~/.ssh/id_ed25519
# 添加一个 VPN 特定的覆盖项
sshroute add myserver --network vpn --host 10.8.0.50 --port 2222 --jump bastion.vpn
# 连接——网络会自动检测
sshroute connect myserver
# 预览解析后的命令而不实际执行
sshroute connect myserver --dry-run
# 查看当前激活的网络
sshroute network
这些标志适用于所有命令:
| 标志 | 环境变量 | 默认值 | 描述 |
|---|---|---|---|
--config | SSHROUTE_CONFIG | ~/.config/sshroute/config.yaml | 配置文件路径 |
-o, --output | table | 输出格式:table、json、yaml | |
-v, --verbose | SSHROUTE_VERBOSE=1 | false | 将调试日志输出到 stderr |
--dry-run | false | 打印解析后的 SSH 命令而不执行 |
init创建一个带有注释示例的 starter 配置文件。如果文件已存在,则失败。
| 标志 | 默认值 | 描述 |
|---|---|---|
--force | false | 覆盖现有配置文件 |
connect <别名>检测激活的网络,为 <别名> 解析 SSH 参数,并执行真正的 SSH 二进制文件。别名后的任何额外参数都会原封不动地传递给 SSH。
| 标志 | 默认值 | 描述 |
|---|---|---|
--fallback | false | 按优先级顺序尝试每个配置文件,仅当连接失败(退出码 255)时才重试下一个 |
--reconnect | false | 监视连接,当断开时自动重连,每次重新检测激活的网络并重新解析路由 |
--reconnect-delay | 2s | 设置 --reconnect 时重连尝试之间的等待时间 |