Search code examples
sshssh-config

How to automatically switch ssh config based on local subnet?


I need to jump through an intermediate host to reach my destination when I'm on a certain network (subnet is 10.10.11.x) because of a destination port I can't change and limited ports on which I can exit the restricted network. I use a SSH config like the following with success:

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

Going through the jumpbox is a significant performance hit, so I need to avoid it for the majority of times that it is not needed. Switching the ssh/scp/rsync host nickname is fine for interactive use, but there are some automated/scripted tasks which it is very painful.

My shell stays open across network transitions, so startup (.zshrc) mechanisms don't help.

I've thought of running a script to poll for the restricted subnet and automating the switch by modifying the .ssh/config file, but I'm not even sure there would be a caching issue. Before I implement that, I thought I would ask if there is a better approach.

What's the best approach for swapping out SSH config based on origin host subnet detection?

In pseudo-config, something like:

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

Solution

  • Based on the answer by Fedor Dikarev, Mike created a bash script named onsubnet:

    #!/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 | grep -F 'inet ' | grep -Fv 127.0.0. | cut -d ' ' -f 2 | grep -Eq "$regexp"
    else
      hostname -I | tr -s " " "\012" | grep -Fv 127.0.0. | grep -Eq "$regexp"
    fi
    
    if [[ $? == 0 ]]; then 
      exit $on
    else
      exit $off
    fi
    

    Then in his .ssh/config file, he uses Match exec like Jakuje's answer:

    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