0

I have a reasonably simple requirement: I need to open a SSH connection to a Remote server from my Java server using JSCH. I don't have direct access, so I need to do it via a Jump server.

First I tested it out using basic SSH port forwarding commands. I logged into the Jump server and ran the following command: (port 34567 has no significance, just a random port)

raddaya@jumpserver> ssh -L *:34567:RemoteServer.com:22 [user]@JumpServer.com

After this, I logged into my Java server and saw if it worked:

raddaya@javaserver> ssh [user]@JumpServer.com -p 34567

As expected, it logged me into the Remote Server! So far, so good.

I wrote some Java code that looked roughly like this: [can't directly copy/paste the code, so please excuse typos]

Session jumpSession = jSch.getSession(jumpServerUsername,jumpServerAddress,22);
jumpSession.setPassword(jumpServerpw);
jumpSession.setConfig(STRICTHOSTKEYCHECKING,"no");
jumpSession.connect();

//now forward the port
int forwardedPort = jumpSession.setPortForwardingL("*",0,remoteServerAddress,22);
System.out.println("Port forwarding rules: " + Arrays.toString(jumpSession.getPortForwardingL()));

Session remoteSession = jSch.getSession(jumpServerUsername,jumpServerAddress,forwardedPort);
remoteSession.setPassword(jumpServerpw);
remoteSession.setConfig(STRICTHOSTKEYCHECKING,"no");
remoteSession.connect();

The port forwarding rule was properly printed here, it would pick a random port between ~30,000 to ~50,000 and forward the port. But to my surprise, I got a "Connection Refused" error when the remote session tried to connect to whatever port was forwarded. I was stuck here for quite some time, because this was pretty much exactly the same code to replicate the SSH commands that worked perfectly. Eventually, a colleague helped me out by showing me the code that worked:

Session jumpSession = jSch.getSession(jumpServerUsername,jumpServerAddress,22);
jumpSession.setPassword(jumpServerpw);
jumpSession.setConfig(STRICTHOSTKEYCHECKING,"no");
jumpSession.connect();

//if you don't give the first argument, it defaults to only allowing loopback forwarding
//i.e you can only connect via localhost!
int forwardedPort = jumpSession.setPortForwardingL(0,remoteServerAddress,22);
System.out.println("Port forwarding rules: " + Arrays.toString(jumpSession.getPortForwardingL()));

//Now we connect to localhost??
Session remoteSession = jSch.getSession(jumpServerUsername,"127.0.0.1",forwardedPort);
remoteSession.setPassword(jumpServerpw);
remoteSession.setConfig(STRICTHOSTKEYCHECKING,"no");
remoteSession.connect();

This does work and successfully connects. But I am completely baffled at the logic of this and even my colleague admits he doesn't know why this works. Is the second getSession somehow running from the jump server instead of the java server? But even in that case, why would I get a Connection Refused the first time? I really don't want to just use this code without understanding it, so I hope someone can explain.

Raddaya
  • 23
  • 4
  • `ssh -L *:34567:RemoteServer.com:22 [user]@JumpServer.com` – This forwards *local* port `34567` via `JumpServer.com` to `RemoteServer.com:22`. But in the next step you are not connecting to local port `34567`, you are connecting to `JumpServer.com:34567`. If that works, it works only because the a port `34567` on the `JumpServer.com` is forwarded. And that imo has nothing to do with your previous port-forwarding. You do not even seem to be using the forwarding you have setup earlier. You seem to be using some pre-existing forwarding that is setup on the jump server. – Martin Prikryl Jun 22 '23 at 04:51
  • And that would explain why your Java code does not work. Because it forwards random local port, but the it try to use that random port on the `JumpServer.com`. But the `JumpServer.com` seems to have `34567` explicitly forwarded. So you obviously cannot use the random port. – Martin Prikryl Jun 22 '23 at 04:52
  • Hi Martin, you may have misread my post, I'm running the ssh -L command while logged into JumpServer.com, not logged into JavaServer.com. It's that command itself which is forwarding the port on JumpServer. The two ssh commands I've mentioned are on different servers - the first one on the JumpServer, the second one on my Java Server (i.e where my Java app is running.) [If the [user]@JumpServer.com part is unnecessary in the first command, I apologise - it just so happened that it worked when I did it like that, so I continued.] – Raddaya Jun 22 '23 at 05:29
  • And in my java code, I try to connect to the Jump Server first and forward a port, and then use it. Am I misunderstanding how JSCH port forwarding works at all? – Raddaya Jun 22 '23 at 05:31
  • Also just to clarify, I am quite sure the jumpserver does not have any port explicitly forwarded - not only do I check every time to see if forwarding was successful using `lsof -i -n | egrep 'ssh'` but if I try to connect from the java server without forwarding a port, I always get connection refused, and only if I forward any given port (not just 34567, tried with a few others to be safe), it sends me through to the remote server. – Raddaya Jun 22 '23 at 05:51
  • Ok, I've indeed missed that. But it's quite strange way to setup the forwarding, imo. Anyway, your Java code does not do that. It forwards *local* port. – Martin Prikryl Jun 22 '23 at 06:46
  • I am inexperienced with ssh forwarding so I could be wrong; I thought this was the only way to set up the forwarding I want, since `ssh -R` would only be useful if I am already connected to the remote server . In the java code, I thought that since I first connect to the jump server and then set the port forwarding rule, it would set up the rule on the jump server, not the java server. Is that not how it works? If not, is there any way to set it up instead of using this weird localhost hack? Thanks a ton for your help! – Raddaya Jun 22 '23 at 07:25
  • It's not a weird localhost hack. That's how it is commonly done. Even with `ssh`. Commonly you would do `ssh -L *:34567:RemoteServer.com:22 [user]@JumpServer.com` **from the local machine**. And then you will connect to `localhost:34567`. – Martin Prikryl Jun 22 '23 at 10:10
  • Because this way, 1) The connection is encrypted all the way from the local machine to the remote end (while with your approach, it's not encrypted between the local machine and the jump server - well in your case it is, because you are using SSH for both steps. But the port-forwarding/ssh-tunneling is used even with protocols that do not encrypt on their own). 2) You do not need to have the 34567 port opened on the jump server. – Martin Prikryl Jun 22 '23 at 10:13
  • 1
    Actually, you do not really need to use the physical local port for `ssh`, if you tool/library allows using the underlying SSH channel directly as a transport for another SSH connection. With `ssh`, you can do `ssh -J [user]@JumpServer.com RemoteServer.com` – it internally uses "SSH port forwarding API" without forwarding any actual *port*. The same can be possibly be implemented with JSch too, but I do not know the right API. But you can see this approach for example here in Python: https://stackoverflow.com/q/35304525/850848 – Note the `sock=vmchannel` and my comment below the answer. – Martin Prikryl Jun 22 '23 at 10:16
  • Thank you very much for all your help. I will do more research on how exactly ssh port forwarding works using the command you showed me instead of my approach, that should help me understand what is going on here. I did indeed come across the -J option for ssh, but I decided against using it because the JSch documentation, at least for the very old version my project is forced to work with, does not have anything about it and may not support it. Thank you again! – Raddaya Jun 22 '23 at 10:54

0 Answers0