Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Port Forwarding with Net::SSH::Expect

by jrsimmon (Hermit)
on Dec 21, 2007 at 17:06 UTC ( [id://658481]=perlquestion: print w/replies, xml ) Need Help??

jrsimmon has asked for the wisdom of the Perl Monks concerning the following question:

Venerable Monks-

I have two networks totally isolated from each other, except by a "jump-box" that will pass through ssh connections. In order to accomplish a bit more, I've used the port forwarding feature of ssh to create ports on my local machine that will connect me directly to various other ports on the other network. This all works fine when I take the time to enter all the commands manually.

That said, I do this enough that I'd like to automate it (and learn a little in the process). Net::SSH::Expect seems to be the perfect utility and, in fact, it connects through the jump box to the remote box with no issues. However, the local port that should allow other applications to tunnel through just doesn't get created. I am using cygwin and perl 5.8.8 with the latest versions of Net::SSH, Expect, and Net::SSH::Expect. Here's the gist of what I'm doing, trimmed down a bit for brevity:
use strict; use warnings; use Net::SSH::Expect; use Data::Dumper; my $user = "user"; my $pwd = "password"; my $host = "host1"; my $local_port = 4100; my $remote_host = "host2"; my $remote_port = 22; my $raw_pty = 1; my $timeout = 3; my $rc = undef; my $ssh_option = "-L $local_port:$remote_host:$remote_port"; my $ssh1 = Net::SSH::Expect->new( host => $host, password => "$pwd", user => "$user", raw_pty => $raw_pty, timeout => $timeout, ssh_option => "$ssh_option" ); eval{ $rc = $ssh1->login(); }; die $@ unless defined $rc; print $rc;

Any suggestions will be much appreciated! Read on for a more in depth explanation...
Ok, so it's a little more complicated than shown above, but this code does show the problem. The "jump box" is actually a double jump--so what is actually required is the following:
ssh to box1
login
box1 automatically initiates a ssh connection to box2
login to box2
box2 automatically initiates a ssh connection to box3
login to box3.

And, as I said, I am able to step through all of this with Net::SSH::Expect, no issues. However, the local port I set up to forward traffic through localhost to box3 and beyond just doesn't seem to get created. When doing this manually, these are the commands I enter:
ssh -L 4100:box2:22 user@box1
ssh -p 4100 -L 4101:box3:22 user@localhost
ssh -p 4101 -L some_port:box_with_that_service:the_same_port user@localhost

So the first ssh connection requires 3 logins (box 1, 2, and 3). The second requires 2 (boxes 2 and 3) and the third only requires 1 (box 3). At that point whichever port I've forwarded with the last ssh should be available for tunneling.

The localhost stuff is required so the applications I'm tunneling can do the ip validations that they want to do...I just have that set up in my hosts file--it looks funny, but it works just fine. And then I can tunnel through to the remote box to my heart's desire. Why doesn't this work with Net::SSH::Expect?

Replies are listed 'Best First'.
Re: Port Forwarding with Net::SSH::Expect
by naChoZ (Curate) on Dec 21, 2007 at 21:22 UTC
    Although I often use ssh tunnels in curious ways such as this, I've never actually played with Net::SSH::Expect until now. I got this working just fine. I just rigged it up so that I can ssh from my workstation to box3 via localhost port 4101, I didn't bother trying other ports.

    I switched the $ssh1 object method to run_ssh() because the lab machines I used to test this already have ssh keys set up already.

    One thing that was very important, the sleep statements. It would not work until I put both of them in there. I'm assuming it must be some sort of race condition, but I'm not sure what the proper way to handle this.

    And the $ssh_params I added are just arguments that I normally use to stuff ssh into the background when making tunnels.

    #!/usr/bin/perl use strict; use warnings; use Net::SSH::Expect; use Data::Dumper; my $user = "user"; my $pwd = undef; my $localhost = 'localhost'; my $first_host = 'box1'; my $second_host = 'box2'; my $third_host = 'box3'; my $local_port_one = 4100; my $local_port_two = 4101; my $ssh_port = 22; my $raw_pty = 1; my $timeout = 3; my $ssh_params = '-P -N -f '; # send ssh to the background my $rc; my $rc2; my $ssh1 = Net::SSH::Expect->new( host => $first_host, password => $pwd, user => $user, raw_pty => $raw_pty, timeout => $timeout, ssh_option => "${ssh_params} -L ${local_port_one}:${second_host}" . ":${ssh_port}" ); $Data::Dumper::Varname = 'rc_died_'; $rc = $ssh1->run_ssh() or die Dumper( @! ); sleep 1; my $ssh2 = Net::SSH::Expect->new( host => $localhost, #password => "$pwd", user => $user, raw_pty => $raw_pty, timeout => $timeout, ssh_option => "${ssh_params} -p ${local_port_one} -L " . "${local_port_two}:${third_host}:${ssh_port} ", ); $Data::Dumper::Varname = 'rc2_died_'; $rc2 = $ssh2->run_ssh() or die Dumper( @! ); sleep 1;

    --
    naChoZ

    Therapy is expensive. Popping bubble wrap is cheap. You choose.

      Ok, so at first this post confused me, seems at first glance to be functionally equivalent...but then i realized the last thing I did before going to be last night was to "clean up" my code and put each session into its own subroutine.

      I pulled subs and created on long script like you have here, and it works like a charm. I don't quite understand why that would make a difference...maybe something with the scope of the various $ssh vars?

      Thanks much for the help! ++

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://658481]
Approved by Joost
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2024-04-24 09:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found