在SSH中每个主机使用两个不同的IP地址


23

我有一个名为的服务器,该服务器gamma不断启动并在工作中运行。有时我从家里连接到它,在这种情况下,我使用公共IP地址55.22.33.99。有时候,我会在工作时连接到它,而不是通过不必要的方式将数据包弹回,而是通过本地IP地址进行连接192.168.1.100

目前,我将它们分为两个不同的条目 ~/.ssh/conf

Host gamma-local
        HostName 192.168.1.100
        Port 22
        User andreas

Host gamma-remote
        HostName 55.22.33.99
        Port 12345
        User andreas

因此,如果我在工作,我只需要输入就可以ssh gamma-local了。如果我在家(或世界其他任何地方),我会跑步ssh gamma-remote

连接到服务器时,我宁愿不必根据自己所在的位置键入其他名称,而是宁愿自动完成该部分。例如,在某些情况下,我会使用自动脚本来连接不知道我在哪里的人。

一个问题可以通过使用Bash脚本先“尝试”连接到本地脚本解决此问题,如果未连接,请尝试连接到远程IP地址。这很好,但是(1)似乎效率很低(特别是因为有时您必须“等待”连接超时,因为它们并不总是立即发回错误),并且(2)需要Bash并拖拉脚本。

有没有另一种方法可以实现此目标,而该方法不依赖于使用Bash脚本,也不依赖于“测试”以查看连接是否首先起作用?


我试图找出的是,是否可以/etc/hosts通过SSH配置文件来解决这个问题?还是某种“检测”您当前连接到哪个LAN的方式?
IQAndreas 2014年

您是否有办公室网络使用的另一套名称服务器?
2014年

@Sree Ah,我知道你要去哪里了;聪明!我们目前不在运行名称服务器,但我可以肯定地将其中一台计算机转换为一台计算机。
IQAndreas 2014年

@Sree随时详细说明,并将其添加为“如果您的办公室网络上有域名服务器...”
IQAndreas 2014年

做过某事。随意投票:)
2014年

Answers:


28

如果您有办法识别您在哪个网络上,则可以使用Match关键字in ~/.ssh/config做您想做的事情。这要求OpenSSH≥6.5。

我用类似的东西

Match originalhost gamma exec "[ x$(/sbin/iwgetid --scheme) != xMyHomeESSID ]"
  HostName 192.168.1.100
  Port 22

Host gamma
  User andreas
  Port 12345
  HostName 55.22.33.99

因此,我使用的是所用wifi网络的标识符来确定我是否在家中进行SSH连接,但也可以使用分配给您计算机的IP地址或区分这两个网络的其他任何方法。


+1聪明的使用Match,谢谢!将检测结果包装到退出状态为零或非零退出的脚本中可能是一个好主意,因为这将使其可重复使用。
彼得2014年

@peterph它当然可以抽象到一个外部脚本,但是直到现在我只需要在一个地方,所以三个规则还没有开始。
米哈尔Politowski

如果您在工作但实际上要连接到其他计算机,将会发生什么?exec语句不匹配,因为您不使用HomeESSID进入WiFi,因此将主机名设置为192.168.1.100?
消失

@我不明白这个问题。比赛也在目标主机上。
米哈尔Politowski

1
据我所知,@typelogic不容易。您可以尝试使用ProxyCommand nc $address ssh,但是例如。所有设备都具有相同的主机密钥吗?如果您仍然需要设置环境变量,那么仅仅将地址直接连接作为参数,ssh是否会更容易?
米哈尔Politowski

5

如果您恰巧有一个私人名称服务器在工作,并且您在办公室和家里使用同一台笔记本电脑,则可以利用它来实现此目的:

  1. 修改您nsswitch.conf的计算机以先检查DNS
  2. gamma在办公室的专用DNS中创建一个DNS条目以解析为192.168.1.100。
  3. 在计算机的/ etc / hosts文件中创建一个条目,以解析gamma为55.22.33.99。

这样,当您ssh gamma离开办公室时,它将从办公室DNS解析为192.168.1.100;当您从家里进行连接时,它将从主机文件解析为55.22.33.99。

PS:此答案假设您不希望gamma具有公共DNS条目。另外,如果您要从Windows机器SSH连接到服务器,我认为应该有一些与nssswitch.conf文件等效的位置来覆盖hosts文件条目。


有-这是主机文件。这正是我的操作方式-在家时,我的公共域解析为我的本地IP。很好的答案-这也是我想到的第一件事。好吧...今天,就是几年前我进行设置时,我不得不在Google上搜索很多以找到解决方案。
mikeserv

2

我不知道是否有可能这样做,~/.ssh/config但是另一种方法是根据您的外部IP地址连接到一个或另一个。由于大概在工作时,您的IP为55.22.33.NNN,因此您可以运行以下命令:

[[ $(wget -qO - http://wtfismyip.com/text) =~ ^'55.22.33.' ]] && 
    ssh 192.168.1.100 ||
    ssh -p 12345 55.22.33.99

一个更简单的方法是使用内部IP。我不知道您的两个网络是如何建立的,但是如果可以通过IP轻松确定您是否在工作(例如,如果您有工作中的特定IP,例如192.168.1.12),则可以执行(更改eth0为您的网卡名称):

[[ $(ip address show dev eth0 | grep -Po 'inet \K[\d.]+') = '192.168.1.12' ]] && 
    ssh 192.168.1.100 ||
    ssh -p 12345 55.22.33.99

无论您决定使用哪种格式,都可以将其作为别名添加到您的外壳程序中(~/.bashrc如果使用,请将该行添加到外壳程序的初始化文件中bash):

alias gamma="[[ $(wget -qO - http://wtfismyip.com/text) =~ ^'55.22.33.' ]] && ssh 192.168.1.100 || ssh -p 12345 55.22.33.99

如果希望其他脚本可以访问它,也可以将其设置.bashrc为脚本(运行脚本时不读取别名)。


2

~/.ssh/config使用IP地址作为主机名时,您无法实现此目的。由于不仅连接到不同的IP地址而且连接到不同的端口,因此引入了更多的复杂性,因为这几乎排除了DNS解析器的任何调整。

我的立场是正确的-您可以使用Match originalhost ... exec ...组合菜单~/.ssh/config请参阅@MichałPolitowski的答案。但是,尽管它对于OpenSSH可以很好地工作,但是您不一定在其他SSH客户端中可以找到类似的功能。

您可以使用一个简单的包装器(如果需要从各种外壳程序使用外壳程序函数或脚本,则可以解决该问题)ssh,该程序将检查您所在的网络并使用适当的Host条目。最大的问题是,如何可靠地检测您所在的网络。我会想到本地IP地址,但它并不可靠,因为您可能还需要从使用与工作网络相同的子网的LAN连接。

如果本地和远程网络都可以使用相同的端口,则可以/etc/resolv.conf根据所使用的网络进行编辑-显然必须自动完成(很可能是从DHCP客户端的挂钩脚本中完成)。或者-更好-运行本地名称服务器(例如dnsmasq),并为其提供适当的配置。但是,这超出了这个问题的范围。

如果只需要交互式连接,则另一个选择是使用将扫描的命令完成~/.ssh/config。这将节省您一些键入操作(特别是如果您有足够多的Host条目)。这样的东西(用于bash初始化):

# complete session names for ssh
declare -g _ssh_complete_hostlist 2> /dev/null
function _ssh_complete_init () {
    _ssh_complete_hostlist=$( \
        sed -nr '/^\s*Host\s*=/{s/^[^=]+= *//;s/ /\n/g;p}' ~/.ssh/config \
        | sort )
}
_ssh_complete_init

function _ssh_complete () {
    local match=${COMP_WORDS[${COMP_CWORD}]}
    local hosts=
    local default=
    for h in $_ssh_complete_hostlist; do
        if [[ $h =~ ^$match ]]; then
            hosts="$hosts $h"
        fi
    done
    if ! (( ${COMP_CWORD} == ${#COMP_WORDS[@]}-1 )); then
        default=$( compgen -f ${COMP_WORDS[${COMP_CWORD}]} )
    fi
    COMPREPLY=($hosts $default)
}
complete -F _ssh_complete ssh

第一个函数创建一个列表,列出主机的完成情况(通常在每个shell中运行一次就足够了),第二个函数执行实际的完成,尽管有点笨拙-仅在主机名完成时才完成命令行上的最后一个标记。

综上所述,解决此问题的正确方法是通过VPN连接到工作网络,从而可以像访问办公室一样访问本地工作IP地址。然后,您可以硬线的地址转换成任何你喜欢的任何层:~/.ssh/config/etc/resolv.conf或(恕我直言最好的选择)办公室名称服务器。


这些端口不是一成不变的。如果可以简化gamma事情,我绝对可以更改的本地SSH端口以匹配远程端口。
IQAndreas 2014年

2

几年前,我为此目的编写了一个程序。它可能适合您的需求。使用该程序,ssh配置可能如下所示:

Host gamma
    ProxyCommand ssh-multipath-proxy 192.168.1.100:22 55.22.33.99:12345
    User andreas

1

另一个解决方案是对SSH使用两个不同的配置文件。您可能会认为这比将所有内容都包含在一个配置文件中略逊一筹,但它更易于维护。

您选择要用于的配置文件-F <configfile>


谢谢。我喜欢这个-F选择。
typelogic

0

部分答案:

上面的许多答案都以“如果您可以检测到您所在的网络”开头。为此,我使用了一个脚本,该脚本在连接接口以启动各种东西(通常是VPN)时运行,当我连接到一个常用网络之一时。该技术是使用ARP获取网关的MAC地址:

function identifyConnection {
    gatewayIP=$(route -n | grep -e '^0\.0\.0\.0' | tr -s ' ' | cut -d ' ' -f 2)

    if [[ ! -z "$gatewayIP" ]]
    then
        # Identify the gateway by its MAC (uniqueness...)
        log "Gateway IP address=$gatewayIP"
        log "Obtaining corresponding gateway MAC address"
        gatewayData=($(arp -n $gatewayIP | grep -e $gatewayIP | tr -s ' '))
        if [[ "${gatewayData[1]}" == "(incomplete)" ]]
        then
            log "Status of gateway $gatewayIP "incomplete""
            echo ""
        elif [[ "${gatewayData[2]}" == "--" ]]
        then 
            log "No MAC address found for $gatewayIP"
            echo ""
        else
            log "Gateway MAC address=[${gatewayData[2]}]"
            echo "${gatewayData[2]}"
        fi
    fi
}

然后,我有一个查找表,将网络与网关MAC关联。当然,该表可以为同一网络容纳多个MAC(典型情况是大型公司站点上的各种Wifi热点)。另一个查找表用于确定要为该网络运行的脚本。

就我而言,该脚本是使用Plasma桌面通知运行的,因此所有内容都在用户区中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.