Drb, firewall, ssh tunneling, and yield

I’ve noticed that I can run a drb server on a firewalled host, and talk
to it from outside the firewall, if the client uses ssh tunneling to
forward the port. At least, this works for simple method calls without
blocks. The problem I’m seeing is that if a method on the server side
yields to a block on the client side, the connection hangs. There is no
problem with the same code within the firewall. I’m guessing that drb is
trying to open another port, which hasn’t been forwarded thru the tunnel.

Is there an easy solution to this problem? Other than avoid using yield,
that is…

==== server.rb ====
require ‘drb’

class Test
def each
4.times do |i|
yield i
end
end

def to_a
[0,1,2,3]
end
end

DRb.start_service(‘druby://your.host.here:9876’, Test.new)

puts '[interrupt] to exit.'
sleep

···

===================

==== client.rb ====
require ‘drb’

DRb.start_service

x = DRbObject.new(nil, ‘druby://localhost:9876’)

p x.to_a # works fine

x.each do |i| # hangs here
p i
end

==== forwarding ====
$ ssh -N -L 9876:your.host.here:9876 your.host.here

I’ve noticed that I can run a drb server on a firewalled host, and
talk to it from outside the firewall, if the client uses ssh tunneling
to forward the port. At least, this works for simple method calls
without blocks. The problem I’m seeing is that if a method on the
server side yields to a block on the client side, the connection
hangs. There is no problem with the same code within the firewall. I’m
guessing that drb is trying to open another port, which hasn’t been
forwarded thru the tunnel.

Is the client side firewalled also? Basically callbacks work by running
a DRb server on the client, which the server then connects back to. If
your client is firewalled, the server won’t be able to connect to your
client.

Is there an easy solution to this problem? Other than avoid using
yield, that is…

DRbFire might interest you:

http://rubyforge.org/projects/drbfire/

It’s still very much in development, but it allows bi-directional
communication through a firewall. It’s more oriented towards
circumventing client-side firewalls, but in your case I think it would
work fine if you just forwarded the additional port it needs (your
normal DRb port + 1).

I’d love some outside feedback on how it works (or doesn’t work) for
you. Documentation is minimal at this point, consisting only of a
sample client and server (and tests), but it’s pretty simple to use,
and you’re welcome to drop me an email with any questions you have.

HTH,

Nathaniel

<:((><

···

On Jan 15, 2004, at 19:10, Joel VanderWerf wrote:

Nathaniel Talbott wrote:

I’ve noticed that I can run a drb server on a firewalled host, and
talk to it from outside the firewall, if the client uses ssh tunneling
to forward the port. At least, this works for simple method calls
without blocks. The problem I’m seeing is that if a method on the
server side yields to a block on the client side, the connection
hangs. There is no problem with the same code within the firewall. I’m
guessing that drb is trying to open another port, which hasn’t been
forwarded thru the tunnel.

Is the client side firewalled also? Basically callbacks work by running
a DRb server on the client, which the server then connects back to. If
your client is firewalled, the server won’t be able to connect to your
client.

Yes, that’s probably it. The client is behind a NAT.

Is there an easy solution to this problem? Other than avoid using
yield, that is…

DRbFire might interest you:

http://rubyforge.org/projects/drbfire/

It’s still very much in development, but it allows bi-directional
communication through a firewall. It’s more oriented towards
circumventing client-side firewalls, but in your case I think it would
work fine if you just forwarded the additional port it needs (your
normal DRb port + 1).

That makes sense. But I haven’t got the incantation right. On the client
side, I do:

$ ssh -N -L 9876:remote.host:9876 -R 9877:localhost:9877 remote.host

But I still have the same behavior: it hangs on the callback.

I’d love some outside feedback on how it works (or doesn’t work) for
you. Documentation is minimal at this point, consisting only of a sample
client and server (and tests), but it’s pretty simple to use, and you’re
welcome to drop me an email with any questions you have.

DRbFire sounds great… I’ll check it out!

···

On Jan 15, 2004, at 19:10, Joel VanderWerf wrote:

Ok, I’m not too bright on things like this, but it sounds cool.
What would be the value of such a thing? Can you provide some
example uses that are subversive, legal, sly or whatever? :slight_smile:

···

On Friday, 16 January 2004 at 9:29:59 +0900, Nathaniel Talbott wrote:

DRbFire might interest you:

http://rubyforge.org/projects/drbfire/

It’s still very much in development, but it allows bi-directional
communication through a firewall. It’s more oriented towards
circumventing client-side firewalls, but in your case I think it would
work fine if you just forwarded the additional port it needs (your
normal DRb port + 1).


Jim Freeze

If God had intended Man to Watch TV, He would have given him Rabbit
Ears.

Nathaniel Talbott wrote:

Is the client side firewalled also? Basically callbacks work by
running a DRb server on the client, which the server then connects
back to. If your client is firewalled, the server won’t be able to
connect to your client.

Yes, that’s probably it. The client is behind a NAT.

That’d do it.

DRbFire might interest you:
http://rubyforge.org/projects/drbfire/
It’s still very much in development, but it allows bi-directional
communication through a firewall. It’s more oriented towards
circumventing client-side firewalls, but in your case I think it
would work fine if you just forwarded the additional port it needs
(your normal DRb port + 1).

That makes sense. But I haven’t got the incantation right. On the
client side, I do:

Oh, I wasn’t very clear. I meant that’s what you’d need to do if you
were using DRbFire. If you’re not, you’d need to specify the url to use
on the client side when you do your DRb.start_service:

 DRb.start_service("druby://localhost:9877")

Otherwise DRb picks any available port, and specifies your actual
hostname (instead of localhost). Once you do that, this incantation…

$ ssh -N -L 9876:remote.host:9876 -R 9877:localhost:9877 remote.host

…should work.

DRbFire sounds great… I’ll check it out!

I’m not sure how much of an advantage it would be in your case, since
you’re already using SSH tunneling. I wrote it to avoid having to do
that myself. It (obviously) can’t handle a firewalled server on its
own; it would still need the ssh tunnel for that.

HTH,

Nathaniel

<:((><

···

On Jan 15, 2004, at 19:49, Joel VanderWerf wrote:

It’s not really subversive… basically, it just allows the server to
call back to clients that are behind firewalls. So the client can pass
the server a proxied (DRbUndumped) object, and at some later time, the
server can call a method on that object and have that method call
relayed back to the client. Normally this is done via the server
initiating the connection, but if the client is behind a firewall (or
NAT), that doesn’t work. So we have the client initiate connections to
the server that the server then uses for calling back to the client.

Does that make more sense?

Nathaniel

<:((><

···

On Jan 15, 2004, at 20:16, Jim Freeze wrote:

On Friday, 16 January 2004 at 9:29:59 +0900, Nathaniel Talbott wrote:

DRbFire might interest you:

http://rubyforge.org/projects/drbfire/

It’s still very much in development, but it allows bi-directional
communication through a firewall. It’s more oriented towards
circumventing client-side firewalls, but in your case I think it would
work fine if you just forwarded the additional port it needs (your
normal DRb port + 1).

Ok, I’m not too bright on things like this, but it sounds cool.
What would be the value of such a thing? Can you provide some
example uses that are subversive, legal, sly or whatever? :slight_smile:

If you extend the normal drb tcp protocol a little bit, you could let it
poke holes in NATs for you. When the client is doing a callback (ie, the
server is sending data in to the client on a new port), just have the
client open a blind tcp connection on that port to the server, that way
the connection tracking will get setup and and the server can connect
back to the client. Or, since the client is doing a connection to the
server anyway to poke the hole, just do the new connection on that
channel. It boils down to only the client create new connections.

Evan

···

On Thu, 2004-01-15 at 17:07, Nathaniel Talbott wrote:

On Jan 15, 2004, at 19:49, Joel VanderWerf wrote:

Nathaniel Talbott wrote:

Is the client side firewalled also? Basically callbacks work by
running a DRb server on the client, which the server then connects
back to. If your client is firewalled, the server won’t be able to
connect to your client.

Yes, that’s probably it. The client is behind a NAT.

That’d do it.

DRbFire might interest you:
http://rubyforge.org/projects/drbfire/
It’s still very much in development, but it allows bi-directional
communication through a firewall. It’s more oriented towards
circumventing client-side firewalls, but in your case I think it
would work fine if you just forwarded the additional port it needs
(your normal DRb port + 1).

That makes sense. But I haven’t got the incantation right. On the
client side, I do:

Oh, I wasn’t very clear. I meant that’s what you’d need to do if you
were using DRbFire. If you’re not, you’d need to specify the url to use
on the client side when you do your DRb.start_service:

 DRb.start_service("druby://localhost:9877")

Otherwise DRb picks any available port, and specifies your actual
hostname (instead of localhost). Once you do that, this incantation…

$ ssh -N -L 9876:remote.host:9876 -R 9877:localhost:9877 remote.host

…should work.

DRbFire sounds great… I’ll check it out!

I’m not sure how much of an advantage it would be in your case, since
you’re already using SSH tunneling. I wrote it to avoid having to do
that myself. It (obviously) can’t handle a firewalled server on its
own; it would still need the ssh tunnel for that.

HTH,

Nathaniel

<:((><

Nathaniel Talbott wrote:

Oh, I wasn’t very clear. I meant that’s what you’d need to do if you
were using DRbFire. If you’re not, you’d need to specify the url to use
on the client side when you do your DRb.start_service:

DRb.start_service("druby://localhost:9877")

That works perfectly, and it all makes sense now… Thanks!

It’s not really subversive… basically, it just allows the server to
call back to clients that are behind firewalls. So the client can pass
the server a proxied (DRbUndumped) object, and at some later time, the
server can call a method on that object and have that method call
relayed back to the client. Normally this is done via the server
initiating the connection, but if the client is behind a firewall (or
NAT), that doesn’t work. So we have the client initiate connections to
the server that the server then uses for calling back to the client.

Does that make more sense?

I think so. How about this example:

    >

[A] | [B]
work | home
firewall

Let’s say I have two machines, one work machine
behind a firewall and a home machine. I cannot
connect to A from B because of the firewall, but
I can connect from A to B. So, while at work, I
make a connection to B. Then when I get home, I
assume there is a way that I can call back into
A with that open connection. Is this correct?
If so, what type of calls can I make back to A.
I am limited to ruby code through drb?
Could I ever get an ssh open from B to A?

···

On Friday, 16 January 2004 at 11:21:40 +0900, Nathaniel Talbott wrote:

On Jan 15, 2004, at 20:16, Jim Freeze wrote:


Jim Freeze

Langsam’s Laws:
(1) Everything depends.
(2) Nothing is always.
(3) Everything is sometimes.

You have just described almost exactly what DRbFire does… want to
write docs? :wink:

Nathaniel

<:((><

···

On Jan 15, 2004, at 20:16, Evan Webb wrote:

If you extend the normal drb tcp protocol a little bit, you could let
it
poke holes in NATs for you. When the client is doing a callback (ie,
the
server is sending data in to the client on a new port), just have the
client open a blind tcp connection on that port to the server, that way
the connection tracking will get setup and and the server can connect
back to the client. Or, since the client is doing a connection to the
server anyway to poke the hole, just do the new connection on that
channel. It boils down to only the client create new connections.

Received: Fri, 16 Jan 2004 12:48:55 +0900
And lo Jim wrote:

Let’s say I have two machines, one work machine
behind a firewall and a home machine. I cannot
connect to A from B because of the firewall, but
I can connect from A to B. So, while at work, I
make a connection to B. Then when I get home, I
assume there is a way that I can call back into
A with that open connection.
(I assume one of those is B to A)

SSH can create port forwarding tunnels in either direction.

Suppose you can’t log into your home network from work, but you can log into your work network from home. All via SSH

On your home server:

ssh -R 9877:localhost:1234 yourusername@yourcomputer.at.work.com

This will both log you into your work computer from home, AND open port 1234 on yourcomputer.at.work.com - if you connect to yourcomputer.at.work.com:1234, it is essentially the same as connecting to home.server.net:9877. You can use 9877 local and remote, I just used 1234 so you know which number is which. You can also use this with port 22 as the local port to allow SSH connections in that normally can’t.

To do the reverse:

Suppose only your port 22 outgoing from home to work is allowed out by your firewall. Simply switch -R to -L

ssh -L 1234:localhost:9877 yourusername@yourcomputer.at.work.com

Then if you connect to home.server.net:1234, you get whatever service is listening on yourcomputer.at.work.com:9877

So to let a script at work connect to a druby server at your home, run the -R version. To let a script on your home box connect to a druby server at work, run the -L version. In both cases, run them from your home server.

Also, you might want to consider appending “-f sleep 86400” to either command, if you don’t want a login prompt. Instead, it will background SSH, and it’ll listen for 86400 seconds (1 day) So …

ssh -R 9877:localhost:9877 yourusername@yourcomputer.at.work.com -f sleep 86400

That’ll make the port forwarder active for 1 day. Tweak at will.

Hope this helped, and wasn’t overly in depth.

  • Greg

[detailed description snipped]

Thanks. I’ll try it out.

···

On Friday, 16 January 2004 at 13:15:30 +0900, Gregory Millam wrote:

Received: Fri, 16 Jan 2004 12:48:55 +0900


Jim Freeze

Job Placement, n.:
Telling your boss what he can do with your job.

Ok, I did this then ssh’d into the work computer, but how do I now
get contact back to the home computer?

···

On Friday, 16 January 2004 at 13:15:30 +0900, Gregory Millam wrote:

Received: Fri, 16 Jan 2004 12:48:55 +0900
And lo Jim wrote:

SSH can create port forwarding tunnels in either direction.

Suppose you can’t log into your home network from work, but you can log into your work network from home. All via SSH

On your home server:

ssh -R 9877:localhost:1234 yourusername@yourcomputer.at.work.com


Jim Freeze

Things will be bright in P.M. A cop will shine a light in your face.

Jim Freeze wrote:

Ok, I did this then ssh’d into the work computer, but how do I now
get contact back to the home computer?

This is what worked for me:

home.host$ ssh -R 9022:localhost:22 me@remote.host
remote.host$ ssh -p 9022 localhost

The 9022 can be anything, of course. Also, if your username is different
on the two hosts, the second line might need to be (untested):

remote.host$ ssh -p 9022 username@localhost