Ssh – How to parameterize the LocalForward option in ssh_config

port-forwardingsshssh-tunneling

I'm starting with this fully working ssh command:

$ ssh -fNL 3306:localhost:3306 coolio@database.example.com

This config gets me part of the simplification I am after:

Host tunnel
    HostName database.example.com
    IdentityFile ~/.ssh/coolio.example.key
    LocalForward 3306 localhost:3306
    User coolio

I can connect with this command, which is much better:

$ ssh -f -N tunnel

My present question is how can I parameterize it so I can do this (or similar):

$ ssh -f -N tunnel -p 3306

or:

$ ssh -f -N tunnel -p 5678

BTW, In my example up top I'm using the same port number in both places on purpose. That is what I need in this case. (But I would also like to know how to parameterize it if the ports are different.)

What I tried so far:

Host tunnel
    HostName database.example.com
    IdentityFile ~/.ssh/coolio.example.key
    LocalForward %p localhost:%p
    User coolio

This gives the error:

ssh/config Bad forwarding specification

Reference:
http://nerderati.com/2011/03/simplify-your-life-with-an-ssh-config-file/

Best Answer

Not the most elegant way, but here's how I would do it:

  • Replace the port specifications in your original file with a unique pattern. For example:

    LocalForward myport localhost:myport
    
  • Alias ssh from within your .bashrc:

    alias ssh='/path/to/ssh_wrapper.pl'
    
  • Write the file /path/to/ssh_wrapper.pl:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    my $cmdline = join ' ',@ARGV;
    if($cmdline =~ / -p\s*([0-9]+)\b/ and $cmdline=~ / -N/){
         my $port = $1;
         system("sed 's/myport/$port/g' /etc/ssh/ssh_config > /tmp/ssh_config");       
    }
    else{
         system("sed 's/LocalForward/#LocalForward/' /etc/ssh/ssh_config > /tmp/ssh_config");
    }
    $cmdline .= ' -F /tmp/ssh_config';
    my $pid = fork;
    $pid ? wait : exec("/usr/bin/ssh $cmdline");
    unlink '/tmp/ssh_config';
    

    Basically, this will parse your ssh command line and if it finds a -p option followed by a numeric port specification, it will create a new ssh_config in /tmp/ssh_config by substituting every occurrence of myport with the numeric port value you specify on the command line. After that, it fork-execs a system call to the real ssh with your command line. If it created its own configuration file, It adds a -F option to your command line to ensure ssh reads from the newly-created configuration file instead. Finally, the parent process waits for the ssh fork to finish, after which it removes the temporary ssh configuration file.

Disclaimer

This code is untested. Try at your own risk. Back up your configuration file first!

Edit

The code as previously written, will break with a "Bad port specification" error when you don't supply a numeric port via -p so I added code to comment the LocalForward part out in case it's not needed. I also made sure that the substitution in the config file won't occur unless there's also a -N option because you might want to specify a port via -p without requiring forwarding (e.g. to ssh into a box that uses a non-standard port).

Related Question