Search code examples
javaipclocalhostrmifirewall

Localhost-only RMI?


I have a project which starts a second JVM. Currently I'm using RMI to communicate between the two. Works well on my own machines.

I need to be able to deploy this project on Windows 7 machines where I do not have privileges to change firewall rules.

The registry (launched from within the first JVM on a high arbitrary port) is being blocked from opening a server socket on these machines.

Is there a way to restrict RMI to only listen to local connections; such that Windows firewall will be cool with it?

Alternatively, is there a good alternative IPC approach that will require little functional changes?

Cheers.


Solution

  • This is trickier than it appears to be at first. What is wanted is to restrict the IP addresses that the registry is listening on, but not the port. The problem is that the RMI Registry implementation program (rmiregistry) does not in fact provide any mechanism to restrict what IP addresses it listens on; it always listens on every network interface available. There are only two practical mechanisms for restricting this:

    1. Use a firewall rule. This is the normal technique to be honest, but it requires configuration during deployment.
    2. Use a special version of the RMI Registry that applies the restriction to just the right IP address (namely localhost, 127.0.0.1) because Windows doesn't complain about sockets that can only be reached from the local machine.

    This second option is what I'll sketch out. It's actually quite simple to do, since you can delegate most of the complexity to existing classes.

    import java.io.IOException;
    import java.net.*;
    import java.rmi.server.*;
    import java.rmi.registry.LocateRegistry;
    
    public class RestrictedRMIRegistry implements RMIServerSocketFactory {
        public static void main(String... args) throws IOException {
            int port = (args.length == 0 ? 1099 : Integer.parseInt(args[0], 10));
            RMIClientSocketFactory csf = RMISocketFactory.getDefaultSocketFactory();
            RMIServerSocketFactory ssf = new RestrictedRMIRegistry();
    
            LocateRegistry.createRegistry(port, csf, ssf);
        }
    
        public ServerSocket createServerSocket(int port) throws IOException {
            // Tricky bit; make a server socket with bound address
            return new ServerSocket(port, 0, InetAddress.getLocalHost());
        }
    }
    

    The only difference between this and the normal rmiregistry implementation is that that uses the defaults for both the client and server socket factories.