Using SSH tunneling for RMI or HTTP communications

22.1. Overview

ProActive allows users to tunnel all of their RMI or HTTP communications over SSH: it is possible to specify in ProActive deployment descriptors which JVMs should export their RMI objects through a SSH tunnel.

This kind of feature is useful for two reasons:

  • it might be necessary to encrypt the RMI communications to improve the RMI security model.

  • the configuration of the network in which a given ProActive application is deployed might contain firewalls which reject or drop direct TCP connections to the machines which host RMI objects. If these machines are allowed to receive ssh conections over their port 22 (or another port number), it is possible to multiplex and demultiplex all RMI connections to that host through its ssh port.

To successfully use this feature with reasonable performance, it is mandatory to understand:

  • the configuration of the underlying network: location and configuration of the firewalls.

  • the communication patterns of the underlying ProActive runtime: which JVM makes requests to which JVMs.

  • the communication patterns of your ProActive objects: which object makes requests to which object. For example: A -> B, B -> C, A ->C

22.2. Configuration of the network

No two networks are alike. The only thing they share is the fact that they are all different. Usually, what you must look for is:

  • is A allowed to open a connection to B?

  • is B allowed to open a connection to A? (networks are rarely symetric)

If you use a TCP or a UDP-based communication protocol (ie: RMI is based on TCP), these questions can be translated into 'what ports on B is A allowed to open a connection to?'. Once you have answered this question for all the hosts used by your application, write down a small diagram which outlines what kind of connection is possible. For example:

  Firewall                         Firewall
     |   *                        *    |
     | ---->     Internet       <----  |
A    | <----                    ---->  |    B
     |   22                       22   |

This diagram summarizes the fact that host A is protected by a firewall which allows outgoing connections without control but allows only incoming connections on port 22. Host B is also protected by a similar firewall.

22.3. ProActive runtime communication patterns

To execute a ProActive application, you need to 'deploy' it. Deployment is performed by the ProActive runtime and is configured by the ProActive deployment descriptor of the initial host. During deployment, each newly- created ProActive runtime performs a request to the initial ProActive runtime. The initial runtime also performs at least one request on each of these distant runtime.

This 2-way communication handshake makes it necessary to correctly configure the network to make sure that the filtering described above does not interfere with the normal operation of the ProActive runtimes.

22.4. ProActive application communication patterns.

Once an application is properly deployed, the application objects deployed by the ProActive runtime start making requests to each other. It is important to properly identify what object connects to what object to identify the influence of the network configuration on these communication patterms.

22.5. ProActive communication protocols

Whenever a request is made to a non-local ProActive object, this request is performed with the communication protocol specified by the destination JVM. Namely, each JVM is characterized by a a unique property named proactive.communication.protocol which is set to one of:

  • rmi

  • http

  • rmissh

  • ibis

  • jini

This property uniquely identifies the protocol which is used by each client of the JVM to send data to this JVM. To use different protocols for different JVMs, two solutions exist:

  • one is to edit the ProActive deployment descriptors and to pass the property as a command-line option to the JVM:

  •    <jvmProcess class='org.objectweb.proactive.core.process.JVMNodeProcess'/>
    ....
    <jvmParameters>
        <parameter value='-Dproactive.communication.protocol=rmissh'/>
    </jvmParameters>
    ...
    </jvmProcess>
  • the other one is to set in the ProActive Configuration file(introduced in a previous chapter) on the remote host the property proactive.communication.protocol to the desired protocol

    <prop key='proactive.communication.protocol' value='rmissh'/> 

Finally, if you want to set this property on the initial deployment JVM (the JVM that starts the application), you will need to specify the -Dproactive.communication.protocol=rmissh argument yourself on the JVM command line.

22.6. The rmissh communication protocol.

This protocol is a bit special because it keeps a lot of compatibility with the rmi protocol and a lot of options are available to 'optimize' it.

This protocol can be used to automatically tunnel all RMI communications through SSH tunnels. Whenever a client wishes to access a distant rmissh server, rather than connecting directly to the distant server, it first creates a SSH tunnel (so-called port-forwarding) from a random port locally to the distant server on the distant host/port. Then, all it has to do to connect to this server is to pretend this server is listening on the local random port choosen by the ssh tunnel. The ssh daemon running on the server host receives the data for this tunnel, decapsulates it and forwards it to the real server.

Thus, whenever you request that a JVM be accessed only through rmissh (namely, whenever you set its proactive.communication.protocol to rmissh), you need to make sure that an ssh daemon is running on its host. ProActive uses the ganymed client ssh library to connect to this daemon.

The properties you can set to configure the behavior of the ssh tunneling code are listed below. All these properties are client-side properties:

  • proactive.ssh.port: the port number on which all the ssh daemons to which this JVM must connect to are expected to listen. If this property is not set, the default is 22.

  • proactive.ssh.username: Two possible syntaxes: username alone .e.g. proactive.ssh.username=jsmith, it represents the username which will be used during authentication with all the ssh daemons to which this JVM will need to connect to.

    Or you can use the form proactive.ssh.username=username1@machine1;username2@machine2;...;usernameN@machineN. Note that several usernames without machine's names is not allowed and won't be parsed properly.

    If this property is not set, the default is the user.name java property.

  • proactive.ssh.known_hosts: a filename which identifies the file which contains the traditional ssh known_hosts list. This list of hosts is used during authentication with each ssh daemon to which this JVM will need to connect to. If the host key does not match the one stored in this file, the authentication will fail. If this property is not set, the default is System.getProperty ('user.home') + '/.ssh/known_hosts'

  • proactive.ssh.key_directory: a directory which is expected to contain the pairs of public/private keys used during authentication. the private keys must not be encrypted. The public keys filenames must match '*.pub'. Private keys are ignored if their associated public key is not present. If this property is not set, the default is System.getProperty ('user.home') + '/.ssh/'

  • proactive.tunneling.try_normal_first: if this property is set to 'yes', the tunneling code always attempts to make a direct rmi connection to the remote object before tunneling. If this property is not set, the default is not to make these direct-connection attempts. This property is especially useful if you want to deploy a number of objects on a LAN where only one of the hosts needs to run with the rmissh protocol to allow hosts outside the LAN to connect to this frontend host. The other hosts located on the LAN can use the try_normal_first property to avoid using tunneling to make requests to the LAN frontend.

  • proactive.tunneling.connect_timeout: this property specifies how long the tunneling code will wait while trying to establish a connection to a remote host before declaring that the connection failed. If this property is not set, the default value is 2000ms.

  • proactive.tunneling.use_gc: if this property is set to 'yes', the client JVM does not destroy the ssh tunnels as soon as they are not used anymore. They are queued into a list of unused tunnels which can be reused. If this property is not set or is set to another value, the tunnels are destroyed as soon as they are not needed anymore by the JVM.

  • proactive.tunneling.gc_period: this property specifies how long the tunnel garbage collector will wait before destroying a unused tunnel. If a tunnel is older than this value, it is automatically destroyed. If this property is not set, the default value is 10000ms.

Note that the use of SSH tunneling over RMI still allows dynamic classloading through HTTP. For the dynamic classloading our protocol creates an SSH tunnel over HTTP, in order to get missing classes. It is also important to notice that all you have to do in order to use SSH tunneling is to set the proactive.communication.protocol property to rmissh and to use the related properties if needed(in major cases default behavior is sufficient), ProActive takes care of everything else.