ssh conditional configuration

You have a ssh server in your private network.

You have set up a port forwarding on your router to access your ssh server from outside.

All is working, but you have to use different config to access your ssh server if you are connected at your private network or on the public internet.

This is not a great trouble for interactive ssh sessions, but it is for rsync jobs, unison configurations, scripts that use ssh, git over ssh, etc.

Luckily, playing with ssh's config, you can achieve a simple setup for your programs, moving the complexity to the ssh client.

All you need are 2 entries in your .ssh/config file and a script that checks where you are.

One of the 2 entries in the ssh config is used when you are in your private network, the other when you are in the public internet. Both call with Match exec a script with 3 parameters:

  • the location where the config entry is valid (public for the public internet, private for your private network
  • the hostname for which the configuration is valid (<HOSTNAME> in the config below, change with your real hostname).
  • the hostname you are calling, because otherwise the config is used even when you are connecting to other hosts

So your .ssh/config now looks like:

    Match exec "~/callfrom public <HOSTNAME> %n"
        Hostname <YOUR PUBLIC IP/NAME>
        Port <THE PORT FORWARDED TO THE SSH SERVER>

    Match exec "~/callfrom private <HOSTANME> %n"
        Hostname <THE SSH SERVER PRIVATE IP/NAME>
        Port 22

The bash script first check if you are on your private network or on the public internet by comparing your actual broadcast address with the one used by your private network. You have to change the script with your values.

Then it checks the location you are validating and compares the hostname you are connecting to with the hostname passed as 2nd parameter and returns the right value:

#!/bin/bash

from=$1
hostchecked=$2
rhost=$3

ip a s | grep "brd 10\.10\.10\.255\/8" # CHANGE WITH YOU PRIVATE NETOWRK BROADCAST ADDRESS
res=$?

case "$from" in
  private)
    if [ $res -eq 0 ] && [ "$rhost" == "$hostchecked" ] ; then
      exit 0
    else
      exit 1
    fi
    ;;
  public)
    if [ $res -ne 0 ] && [ "$rhost" == "$hostchecked" ] ; then
      exit 0
    else
      exit 1
    fi
    ;;
esac

Now when you call

ssh <HOSTNAME>

from your private network, the 1st Match returns 1 (FALSE), while the 2nd returns 0 (TRUE) (from=public, rhost=hostchecked, res!=0).

Conversely, when you are on the public internet,  the 1st Match returns 0 (TRUE), while the 2nd returns 1 (FALSE) (from=private, rhost=hostchecked, res==0).