how tunnel all RMI traffic over SSH - java

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...

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.

Related

Java : How to connect to ServerSocket using router public IP?

I'm trying to connect to server socket in java using my router's public ip,
first, I tried by simply configuring server socket to localhost, like this,
server = new ServerSocket(5000);
It is working on localhost but not working on trying internal ip 192.168.1.6
then, I tried configuring server socket to the internal Ip (saw this solution), code is as follows,
int backlog = 5;
server = new ServerSocket(5000, backlog, InetAddress.getByName("192.168.1.6"));
and it is working as my devices are connected to same network, I can connect to this Ip 192.168.1.6 from a device with Ip 192.168.1.5 on the same network but when I use public ip of my router from client side, connection is getting timed out, I've done port forwarding,
What am I doing wrong here? any help is appreciated, thanks in advance.
EDIT :
I came to about NAT-loopback (saw this solution) as I was trying to connect using public ip while being on same network so I tried different network but still it is not connecting, connection is getting timed out.
Firstly replace your port number by 8080, which is the default port for HTTP when you are not root. Then you have to open the port 8080 on your router to allow the client to connect.
Your server must be connected to the Internet and have a public IP address. Then the client can connect to this public IP address.

Java Socket connection getHostName

I am testing on java.net.ServerSocket.
What I want is the following.
When connecting to aaa.com, you get aaa.com,
Getting bbb.com when connecting to bbb.com.
My etc/hosts file configuration is as follows.
127.0.0.1 aaa.com
127.0.0.1 bbb.com
I used the following java source.
ServerSocket server = new ServerSocket(port);
Socket request = server.accept();
request.getInetAddress().getHostName();
And when connecting to aaa.com, aaa.com is returned.
When connecting to bbb.com, aaa.com is returned.
How can I get bbb.com when connected to bbb.com?
This code is not connecting to anything. It is accepting connections from ... something.
So ... I presume that you have some client code (not shown) that is connecting to port port using hostnames "aaa.com" and "bbb.com" respectively. And you want this server side to know which hostname that the client side used.
It is not possible.
The client resolves the hostnames to an IP address and then makes the connection using the IP address (and only the IP address). Since the IP address is the same in both cases, the server side cannot distinguish the two cases.
It follows that if the application level of the server needs to know the hostname that the client used to make the connection, then the application protocol must pass this information from the client to the server. (That is what protocols like HTTP, FTP and so on do.)

Java RMI cannot connect to server via WAN while it works in LAN

I'm working on a project using Java RMI and with a client-server implementation.
On the server side, this is how I initialize my server:
final String url = "localhost";
final int port = 8090;
LocateRegistry.createRegistry(port);
IServerGame serverGame = new ServerGame();
Naming.rebind("//"+url+":"+port+"/undercover", serverGame);
System.out.println("Server running at //" + url + ":" + port + "/undercover")
Undercover is the name of my application. We chose to use the port 8090
arbitrarily.
Now, here is how I initialize the connection client-side:
try {
server = (IServerGame) Naming.lookup("//"+address+":8090/undercover");
System.out.println("Connected to server " + address + ".");
} catch (Exception e) {
System.out.println("Connection failed.");
e.printStackTrace();
}
address is a string in the parameter of the method which initializes the connection and is the IP of the server. We ask the client to enter this IP to allow us to connect to different servers if we want.
When I run my application in local, whether I use localhost or my private IP 192.168.x.x as address, everything works fine. The client connects to the server and I can use the application. The problem is that when I want to use the application via WAN, sending the client to a friend and starting the server on my local machine, it doesn't work anymore. I get this error:
java.rmi.ConnectException: Connection refused to host: (private IP of host machine); nested exception is:
java.net.ConnectException: Connection timed out: connect
I've already checked a lot of posts in StackOverflow with the exact same problem and the usual answer is to either set the SystemProperty(java.rmi.server.hostname,"192.168.x.x") or do it via the -Djava.rmi.server.hostname in command line. I'm using Gradle to compile. I run the application via the run task. The client is a basic fx application too. None of this works unfortunately. I've also tried to open the port 8090 in my firewall but it doesn't solve the problem either.
Now maybe I'm doing this wrong. I've also tried to replace my private IP 192.168.x.x, which is IPv4 that I found via ipconfig in the command shell, with my public IP 79.95.x.x. But, again, it doesn't work.
I tried adding the SystemProperty(java.rmi.server.hostname,"192.168.x.x") like the first line of code that appears in the server code I showed you above.
I'm connected to internet via 4G. I don't have a box connection, so I can't really go to the box settings to allow certain ports, if that's ever a thing that could fix the issue.
EDIT :
So i've tried to switch from naming implementation in server-side to Registry implementation as it was suggested bellow but it didn't make any difference. As i thought it could be just a connection problem, i asked a friend to ping the server with telnet on the port 8090 and in fact it didn't work. He ran telnet [domain name or ip] [port] and the error was :
Unable to connect to remote host: Connection timed out
So the problem is that the external clients cannot reach my server or connect to the port. As i'm using 4G as internet connection as i mentioned above, any idea on how i could make the port reachable ?
Try using class java.rmi.registry.Registry rather than class java.rmi.Naming.
On the server side:
final int port = 8090;
Registry registry = LocateRegistry.createRegistry(port);
ServerGame serverGame = new ServerGame();
IServerGame stub = (IServerGame) UnicastRemoteObject.exportObject(serverGame, 0);
registry.bind("undercover", stub);
On the client side:
Registry registry = LocateRegistry.getRegistry("server host name or IP", 8090);
IServerGame serverGame = (IServerGame) registry.lookup("undercover");

How to connect an ActiveMQ Client that's behind a NAT to a Server that isn't?

I've looked online, and everything I find shows how to make a separate server to connect to the main server if it's behind a nat or firewall.
But in my case the client is behind the NAT, and the server is on the local network.
So it's set up kinda like below:
Client Actual:10.0.0.1 -> Client NAT:100.0.0.2:1111 <--> Server 10.0.0.0:1099
The Java code I use to connect to the server is as below:
String serviceUrl = "service:jmx:rmi:///jndi/rmi://10.0.0.0:1099/jmxrmi";
String[] credentials = new String[] {"username", "password"};
String objectName = "org.apache.activemq:type=Broker,brokerName=test";
JMXServiceURL url = new JMXServiceURL(serviceUrl);
Map<String, String[]> env = new HashMap<String, String[]>();
env.put(JMXConnector.CREDENTIALS, credentials);
JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
conn = jmxc.getMBeanServerConnection();
broker = MBeanServerInvocationHandler.newProxyInstance(conn, new ObjectName(objectName), BrokerViewMBean.class, true);
And the error it throws is:
java.rmi.ConnectException: Connection refused to host: 10.0.0.0; nested exception is:
java.net.ConnectException: Connection timed out: connect
So my question is, how do I make this client behind NAT connection work?
First of all: there is nothing special in with regard with network configuration for ActiveMQ to work. ActiveMQ's protocol is single port, and can be easily routed just like most other TCP/IP protocol.
Therefore, given that the server is properly listening on its TCP port and that a client can successfully connect to it locally, then this problem can be analyzed as if it was any other network-related problem.
Can the client machine ping the server machine? It is difficult from the IP address scheme that you present to properly understand your network, but as it is presented right now, the client machine will simply assume that the server is on the local network and therefore send an ARP request asking for the MAC address of "10.0.0.0" (which will timeout because there is no such machine to answer the request) rather than forward the request to its NAT gateway. If that is indeed the problem you have, then there are three possible solutions: a) modify the network layout (have the client use a different IP scheme), b) setup a static route for the server's IP on the client machine to force its routing through the gateway, or c) add a port redirect on the gateway and have the client connect to the IP address of the gateway instead. Now solution a is not very practical, unless your setup is barely a lab configuration. Solution b is a possibility, but a really bad one. Solution C, that is setting up port redirection on the gateway, is the most common solution to this kind of problem.
Use hostnames on both sides, by setting the same -Djava.rmi.server.hostname=XXX. Be sure that hostname is resolvable on both sides. You can have a look at http://docs.oracle.com/javase/8/docs/technotes/guides/rmi/faq.html#nethostname

Java sockets over internet

I have written a client and server using java sockets. The two machines are on the same network. When I connect using the local IP addresses there are no issues. However if I use the public IP address the connection times out and the client throws "SocketException: connection reset", and the server throws "SocketException: connection reset by peer: socket write error". This happens most of the time, but once in a blue moon the connection actually succeeds. I can successfully ping the servers local address and the router from the client machine.
I have gone into my router, assigned the server machine to a permanent IP address, and forwarded all traffic on the relevant port to that IP address. Unfortunately that was the only thing that I thought could have solved the issue and it did not. I have also turned off windows firewall on the server, still no luck.
The client: (ip taken from whatismyip.com)
Socket s = new Socket("xx.xxx.xxx.xx", 27499);
to connect.
The server:
InetAddress ip = InetAddress.getLocalHost();
ServerSocket ss = new ServerSocket(27499, 0, ip);
Socket cs = ss.accept();
Any help would be appreciated. I can provide more code if necessary.
"SocketException: connection reset by peer: socket write error"
This means the server is actually rejecting the connection, could be a firewall issue, the server rejecting the connection, the server actually being down or overloaded.

Categories