如何根据本地子网自动切换ssh配置?

时间:2016-11-22 16:03:49

标签: ssh

当我在某个网络(子网是10.10.11.x)时,我需要跳过一个中间主机才能到达我的目的地,因为我无法更改目标端口和我可以退出的有限端口限制网络。我使用类似下面的ssh配置成功:

Host web-direct web
    HostName web.example.com
    Port 1111

Host web-via-jump jweb
    HostName web.example.com
    Port 1111
    ForwardAgent yes
    ProxyCommand ssh -p 110 -q relay.example.com nc %h %p

通过跳转框是一个重要的性能打击,所以我需要避免它在大多数情况下不需要它。切换ssh / scp / rsync主机昵称可以用于交互式使用,但是有一些自动/脚本化的任务非常痛苦。

我的shell在网络转换中保持开放,因此启动(.zshrc)机制无济于事。

我曾经想过运行一个脚本来轮询受限子网并通过修改.ssh / config文件来自动化交换机,但我甚至不确定会出现缓存问题。在我实施之前,我想我会问是否有更好的方法。

根据源主机子网检测交换ssh配置的最佳方法是什么?

在伪配置中,例如:

if <any-active-local-interface> is on 10.10.11.x:
    Host web
        HostName web.example.com
        Port 1111
        ForwardAgent yes
        ProxyCommand ssh -p 110 -q relay.example.com nc %h %p
else:    
    Host web
        HostName web.example.com
        Port 1111
endif

6 个答案:

答案 0 :(得分:10)

有可能使用Match exec选项来执行特定命令,因此您可以编写如下内容:

Match exec "hostname -I | grep -F 10.10.11." host web
    ForwardAgent yes
    ProxyCommand ssh -p 110 -q relay.example.com nc %h %p
Host web
    HostName web.example.com
    Port 1111

答案 1 :(得分:1)

我正在使用以下功能:

function ssh() {
  network=`networksetup -getairportnetwork en0 | cut -d: -f2 | tr -d [:space:]`
  if [ -n "$network" -a -f $HOME/.ssh/config.$network ]; then
    /usr/bin/ssh -F $HOME/.ssh/config.$network "$@"
  else
    /usr/bin/ssh "$@"
  fi
}
export -f ssh

所以我需要为每个WiFi网络提供一个单独的配置文件,我想要一个自定义解决方案。它现在适合我,但它很难看。我只能作为一个想法推荐它,而不是最好的解决方案。

我很高兴知道任何更好的解决方案。

答案 2 :(得分:1)

您可以代替checking the subnet CIDR来检查DHCP给您的域名后缀。

  • 如果要在Intranet外部使用跳接盒,则此方法比检查reserved private分配中的IP范围(例如,您的家庭网络或远程工作位置使用与example.com的Intranet相同)。

  • 如果您的Intranet到处都使用相同的DNS后缀,并且您正尝试遍历Intranet内的子网,则此方法不是有用。如果是这种情况,请使用Jakuje's solution

Match Host web Exec "hostname -d | ! grep -q -E '^example\.com'"
    ForwardAgent yes
    ProxyCommand ssh -p 110 -q relay.example.com nc %h %p
Host web
    HostName web.example.com
    Port 1111

如果您没有hostname选项的最新-d版本(例如,在MacOS上),则可以直接查询resolve.conf

Match Host web Exec "! grep -q -E '^\s*search[ \t]+example\.com' /etc/resolv.conf"
    ...
    ...

答案 3 :(得分:1)

我对这个问题的解决方法如下:

Host myserver
    HostName [internal IP]
    ...
    
Match Host [internal IP] !Exec "nc -w1 -q0 %h %p < /dev/null"
    ProxyCommand ssh jumphost -W %h:%p

首先拥有Host myserver行很重要,这样SSH客户端将知道IP地址。

Match表达式中,

  • Host选项在该IP上匹配。 (它接受*,因此您也可以匹配/ 8,/ 16或/ 24子网。)
  • Exec选项将执行netcat,超时为1秒,以测试SSH端口是否打开。如果不是,则使用ProxyCommand

这是我发现实际测试是否需要Jumphost的最清晰方法。当然,如果网络落后,则可以设置更高的超时时间。有关更多详细信息,请参见man ssh_config

答案 4 :(得分:0)

基于Fedor Dikarev的the answerMike created一个名为onsubnet的bash脚本:

#!/usr/bin/env bash

if [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]  || [[ "$1" == "" ]] ; then
  printf "Usage:\n\tonsubnet [ --not ] partial-ip-address\n\n"
  printf "Example:\n\tonsubnet 10.10.\n\tonsubnet --not 192.168.0.\n\n"
  printf "Note:\n\tThe partial-ip-address must match starting at the first\n"
  printf "\tcharacter of the ip-address, therefore the first example\n"
  printf "\tabove will match 10.10.10.1 but not 110.10.10.1\n"
  exit 0
fi

on=0
off=1
if [[ "$1" == "--not" ]] ; then
  shift
  on=1
  off=0
fi

regexp="^$(sed 's/\./\\./g' <<<"$1")"

if [[ "$(uname)" == "Darwin" ]] ; then
  ifconfig | fgrep 'inet ' | fgrep -v 127.0.0. | cut -d ' ' -f 2 | egrep "$regexp" >/dev/null
else
  hostname -I | tr -s " " "\012" | fgrep -v 127.0.0. | egrep "$regexp" >/dev/null
fi

if [[ $? == 0 ]]; then 
  exit $on
else
  exit $off
fi

然后,在他的.ssh/config文件中,他像Jakuje's answer一样使用Match exec

Match exec "onsubnet 10.10.1." host my-server
    HostName web.example.com
    Port 1111
    ForwardAgent yes
    ProxyCommand ssh -p 110 -q relay.example.com nc %h %p

Match exec "onsubnet --not 10.10.1." host my-server
    HostName web.example.com
    Port 1111

答案 5 :(得分:0)

匹配 ip 的最简单方法是使用这样的正则表达式:

Match exec "[[ '%h' =~ ^10\.10\.11\. ]]"
   ... 

你可以扩展它:

Match exec "[[ '%h' =~ ^10\.10\.1(1|2)\. ]]"
   ...