Jan Staal
Jan Staal

Reputation: 41

how tunnel all RMI traffic over SSH

I'm working on tunnelling the cajo rmi traffic through a SSH tunnel.

For that I have a server running an SSH deamon (apache Mina) and a client running an SSH client (Trilead SSH).

The shh connection between these machines can be established and by applying local and remote port forwarding I can tunnel rmi traffic, however this works only in the outging (to server) direction.

The setup:

Active SSH connection (port 22)
client: forwarding local port 4000, to remote host port 1198 (this traffic actually goes trhough the tunnel) server: forwarding server port 4000, to client port 1198 (this part of the tunnel is not being used by cajo)

The server exports an object using:

Remote.config(null, 1198, null, 0); 
ItemServer.bind(new SomeObject(), "someobject");

The client does an object lookup using:

ObjectType someObject =  (ObjectType)TransparentItemProxy.getItem(
                          "//localhost:4000/someobject", 
                          new Class[] { ObjectType.class });
logger.info(someObject.getName());

Port forwarding is invoked using the trilead ssh library on the client side:

conn.createLocalPortForwarder(4000, "Server-IP", 1198);
conn.requestRemotePortForwarding("localhost" 4000, "Client-IP", 1198);

When analysing the ip traffic between the two machines with WireShark, I see that the lookup is being redirected throug the tunnel, but the response is not. The respons is ordinarily send to port 1198 of the client.

How can I force the server to send the response of a remote invocation to a local port, in order to get it tunneled back to the client?

UPDATE: The problem here was that the ports for RMI objects are different then the registry port and therefore also need to be forwarded.

In short, client 10.0.0.1 makes lookup to //10.0.0.1:4000 which is forwarded to the RMI port on the server (through the tunnel). Subsequently the server responds to 10.0.0.1:1198 where I would like the server to send its traffic to its local port 4000 instead, in order to use the tunnel.

I have tried to fiddle with the cajo Remote.config(ServerAddress, ServerPort, ClientAddress, ClientPort) settings, however when I set the clientaddress to 10.0.0.1 or 127.0.0.1 for this method, I'm unable to get response back and I don't see any responding traffic at all...

Upvotes: 1

Views: 4590

Answers (1)

Jan Staal
Jan Staal

Reputation: 41

I did find a solution to this problem, in which I omitted the cajo framework from the setup and use pure java rmi. This makes things more transparent.

On both client and server I placed a security policy file: C:\server.policy

grant {
permission java.security.AllPermission;
};

Then on the server, set security permissions and start registry on desired port:

System.setProperty("java.rmi.server.hostname", "127.0.0.1");            
System.setProperty("java.security.policy","C:\\server.policy");
System.setSecurityManager(new RMISecurityManager());
new SocketPermission("*:1024-", "accept,connect,listen");      
createRMIRegistry(Property.getProperty("rmi.registry.port"));

Notice the hostname 127.0.0.1, this makes sure we are always pointing to localost,

  • this tricks the client in thinking the object got from the remote registry is local and then connects to its local forwarded ports.

On the client I give the same permissions as above, I don't start the register, but bind an extra socket factory to use for the registry lookup.

RMISocketFactory.setSocketFactory(new LocalHostSocketFactory());

This socket factory creates a SSHClientSocket to the localhost ssh port (to the remote registry).

The remote objects are exported with a custom ClientSocketFactory, which is therefore implemented on the clientside. (On the serverside it needs to be disabled, otherwise you will ssh to your own machine :$)

It then creates a ssh socket and port forwarder on the fly.

public class SSHClientSocketFactory implements RMIClientSocketFactory, Serializable {

public Socket createSocket(String host, int port) throws IOException {
    try {    
    Connection conn = new Connection(hostname, 22);
    conn.connect();
    boolean isAuthenticated = conn.authenticateWithPassword(username, password);
    LocalPortForwarder lpf1 = conn.createLocalPortForwarder(port, serverAddress, port);
        return new Socket(host, port);
    catch (Exception e) {System.out.println("Unable to connect")};
}

}

This automatic port formwarding ensures that whatever port is being used to bind an RMI object, it goes through the SSH tunnel and points to localhost for that.

Remote port forwarding is not needed for this setup.

Upvotes: 2

Related Questions