0 votes
by (420 points)
edited by

I have the following requirements:

  • A proxy server behind a firewall. Only outgoing connections are possible from this server outside the firewall.
  • The proxy server has permissions to connect to any machine behind the firewall, using any protocol (ssh/telnet).
  • An application outside the firewall.
  • The application needs to open ssh/telnet connections to machines behind the firewall with the help of the proxy server.
  • Note: the communication between the application and proxy needs to be two-way.

Could you help me figure out how can I use Rebex to achieve such a behavior? Thank you in advance.

by (144k points)
Before we propose a solution, I need to make sure that our assumptions are correct:
- I assume that any solutions that require any configuration changes at the 'firewall' are not acceptable. Is that correct?
- On the other hand, I assume it is possible to install and run a custom .NET application (possibly based on Rebex libraries) on the 'proxy server'. Please confirm whether this is the case.
- If both of the assumptions are correct, any solution would involve setting up a different kind of proxy server (possibly an SSH server) outside the 'firewall' to facilitate the communication. Is that acceptable as well?
by (420 points)
1. No changes to the firewall.
2. Both the proxy and the application will include the Rebex components.
3. Outside of the firewall we do not have any constraints, meaning that we are in control.

1 Answer

+1 vote
by (144k points)
selected by
 
Best answer

Consider the following approach:

  1. Set up an SSH server outside the firewall that is accessible both by the proxy server and by the application.
  2. Configure that external SSH server to allow incoming tunnels (reverse port forwarding) from non-local endpoints.
  3. At the proxy server, run an application that uses Rebex Terminal Emulation's port forwarding API to establish an incoming tunnel (or a set of tunnels) from an address/port at the external SSH server to a machine behind the firewall.

Details:

(1) Any SSH server that supports reverse port forwarding is suitable. An OpenSSH server on a Linux VPS would be fine. (Our Buru SFTP Server is intended for file transfer and doesn't support reverse port forwarding yet.)

(2) In OpenSSH, this can be enabled by setting the GatewayPorts configuration option to yes.

(3) At the proxy server, an application based on Rebex.SshShell assembly would establish tunnels from the SSH server to local machines:

// connect and log in to an SSH server
var ssh = new Rebex.Net.Ssh();
ssh.Connect(hostname);
ssh.Login(username, password);

// create port forwarding rules
var tunnel1 = ssh.StartIncomingTunnel(
    "sshserver.example.org", 10022,  // server-side source address/port
    "192.168.1.1", 22); // client-side target address/port

// create port forwarding rules
var tunnel2 = ssh.StartIncomingTunnel(
    "sshserver.example.org", 11022,  // server-side source address/port
    "192.168.1.2", 22); // client-side target address/port

...

With this setup, an application outside the firewall would be able to connect to (for example) sshserver.example.org:11022 and the connection would get tunneled to 192.168.1.2:22.

With this approach, all of the possible tunnels would have to be pre-configured. Or, in other words, the application outside the firewall could not connect to any IP/port behind the firewall. This could be both a drawback or a benefit, depending on your scenario. But if you need the ability for the application to connect to any IP/port behind the firewall, this could be achieved by first connecting to an SSH server behind the firewall and using forward port forwarding to reach the other machines.

by (420 points)
So, I come up with some news. After a lot of prototyping, I managed to implement the desired behavior using the FileServer, but only to the point that I can establish a "duplex" communication channel between the SSH server and the Proxy server. How I did it?

1. The Proxy server initiates the connection by creating two SSH sessions (Ssh component): one for the input queue and the other one for the output queue.

2. The SSH Server receives the commands in the ShellCommand event from the Proxy. The session of the Proxy's input queue becomes the session of the SSH Server's output queue (ServerSession component) and the session of the Proxy's output queue becomes the session of the SSH Server's input queue (also ServerSession component).

Side note: at this point it would be interesting to see how can I use a sigle session for the duplex communication (currently, when I use a single session for the input and output queues, a Send would crash if a ReadLine is active).

The communication protocol works like this:
A. SSH Server sends a request.
  1. SSH Server out: ServerSession.SendMessage(string_encoded_message).
  2. Proxy in: Scripting.ReadLine until message is completely received.
  3. Proxy processes the message.
  4. Proxy out: Scripting.SendCommand(string_encoded_response).
  5. SSH Server in: ShellCommand handler receives response and matches the request.

B. Proxy sends a request.
  1. Proxy out: Scripting.SendCommand(string_encoded_message).
  2. SSH Server in: ShellCommand handler receives message.
  3. SSH Server processes the request.
  4. SSH Server out: ServerSession.SendMessage(string_encoded_response).
  5. Proxy in: Scripting.ReadLine until response is completely received and matches the request.

The communication works fine so far. The problem arises when I try to start a tunnel from the SSH Server to a target. So here is the flow, assuming that the duplex connection is up and running:

1. Identify a free port on the SSH Server.
2. Send to the proxy the free port number and the target host/port.
3. The proxy uses its out queue's SSH session to start an incoming tunnel from (127.0.0.1, free-port) to (target-host, target-port). BANG! This is the moment when the method crashes with the cryptical "The request has failed" exception.
   (from log: ID:3|area:SSH|Rebex.Net.SshException: The request has failed.
                at Rebex.Net.SshSession.ExecuteRequest(ConnectGlobalRequest request, GlobalRequestKind kind)
                at Rebex.Net.SshSession.StartTcpIpForward(String address, Int32 port))

This is the region which generates the crash:
            object[] result = WaitFor<object[], SshGlobalRequest>(ExecuteRequestFinishCheck, r);
            if (result == null)
                throw new SshException(SshExceptionStatus.OperationFailure, SshStrings.RequestFailed);

Trying to build a tunnel using putty indicates an error: "Remote port forwarding from 127.0.0.1:4800 refused." (a connection was previously established to the server, and the tunnel is created in this connection.)

Side note: there is no notification in both Ssh and ServerSession when the underlying connection is closed/dead (such as a Closed event). I implemented a timer that checks periodically (each minute) if the connection is alive.

Any additional help would be appreciated.
by (144k points)
It looks like you choose to implement this in a different way using a lower level API, but at quick glance it looks like it should work as well. However, as I already mentioned on May 6th, Rebex File Server does not support incoming SSH tunneling yet, and that means it will reject all SshSession.StartTcpIpForward requests.

This is actually one of the features we would like to add soon, but we are currently busy with other features and this is not in our near-term plans yet. At this point, our recommendation would be to use a different SSH server, but that approach is not compatible with your solution.

However, the list of customers who would like this feature keeps growing, so we will consider reprioritizing this. What is your project's schedule? Would a beta with incoming SSH tunnel support be sufficient for a month or two?
by (420 points)
Hi Lukas,

yes, thanks, it should suffice. When do you think it could be available?
by (420 points)
Are there any updates on the incoming SSH tunneling?
by (144k points)
We will make a decision on this next week. This is one of the most requested new features, so chances are we'll start implementing this when we are done with "2019 R4 Preview 1" release.
...