Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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/

share|improve this question
add comment

1 Answer

up vote 2 down vote accepted

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).

share|improve this answer
    
Wow! I have great respect for both you and Linux... –  MountainX Jul 12 '13 at 6:21
    
@MountainX See the updated answer, please. –  Joseph R. Jul 12 '13 at 13:49
    
thanks for the update. I don't know perl, but with your explanation and reading through your code, I feel comfortable with it now. And your update helps a lot, so I plan to use this solution. (It's a very interesting approach.) –  MountainX Jul 12 '13 at 19:47
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.