SSH tunneling via JSch
a SOCKS
proxy setting on jsch allows you to connect to a running proxy server on the remote side. An sshd
on the remote side would not be considered a SOCKS
proxy. What you will have to do is establish a local port forward to the ssh port on the machine you're tunneling to, then establish a secondary ssh connection to this system using the api.
I've taken your example and slightly rewritten it to accomplish this:
import com.jcraft.jsch.*;
import java.io.*;
public class JschExecutor2 {
public static void main(String[] args){
JschExecutor2 t=new JschExecutor2();
try{
t.go();
} catch(Exception ex){
ex.printStackTrace();
}
}
public void go() throws Exception{
StringBuilder outputBuffer = new StringBuilder();
String host="firstsystem"; // First level target
String user="username";
String password="firstlevelpassword";
String tunnelRemoteHost="secondlevelhost"; // The host of the second target
String secondPassword="targetsystempassword";
int port=22;
JSch jsch=new JSch();
Session session=jsch.getSession(user, host, port);
session.setPassword(password);
localUserInfo lui=new localUserInfo();
session.setUserInfo(lui);
session.setConfig("StrictHostKeyChecking", "no");
// create port from 2233 on local system to port 22 on tunnelRemoteHost
session.setPortForwardingL(2233, tunnelRemoteHost, 22);
session.connect();
session.openChannel("direct-tcpip");
// create a session connected to port 2233 on the local host.
Session secondSession = jsch.getSession(user, "localhost", 2233);
secondSession.setPassword(secondPassword);
secondSession.setUserInfo(lui);
secondSession.setConfig("StrictHostKeyChecking", "no");
secondSession.connect(); // now we're connected to the secondary system
Channel channel=secondSession.openChannel("exec");
((ChannelExec)channel).setCommand("hostname");
channel.setInputStream(null);
InputStream stdout=channel.getInputStream();
channel.connect();
while (true) {
byte[] tmpArray=new byte[1024];
while(stdout.available() > 0){
int i=stdout.read(tmpArray, 0, 1024);
if(i<0)break;
outputBuffer.append(new String(tmpArray, 0, i));
}
if(channel.isClosed()){
System.out.println("exit-status: "+channel.getExitStatus());
break;
}
}
stdout.close();
channel.disconnect();
secondSession.disconnect();
session.disconnect();
System.out.print(outputBuffer.toString());
}
class localUserInfo implements UserInfo{
String passwd;
public String getPassword(){ return passwd; }
public boolean promptYesNo(String str){return true;}
public String getPassphrase(){ return null; }
public boolean promptPassphrase(String message){return true; }
public boolean promptPassword(String message){return true;}
public void showMessage(String message){}
}
}
What this code does is create a local port forwarding to the ssh port on the target system, then connects through it. The running of the hostname command illustrates that it is, indeed, running on the forwarded-to system.
Comments
-
Unni Kris almost 2 years
My aim is to connect to a server (host) which is behind a firewall. I am able to access this server by connecting to another server (tunnel) in the network and then SSH to this server. However I am not able to implement the same scenario via JSch.
I am not able to have the below code work, which I have written for this purpose. Please let me know if I am doing anything silly here.
public class JschExecutor { public static void main(String[] args){ JschExecutor t=new JschExecutor(); try{ t.go(); } catch(Exception ex){ ex.printStackTrace(); } } public void go() throws Exception{ StringBuilder outputBuffer = new StringBuilder(); String host="xxx.xxx.xxx.xxx"; // The host to be connected finally String user="user"; String password="passwrd"; int port=22; String tunnelRemoteHost="xx.xx.xx.xx"; // The host from where the tunnel is created JSch jsch=new JSch(); Session session=jsch.getSession(user, host, port); session.setPassword(password); localUserInfo lui=new localUserInfo(); session.setUserInfo(lui); session.setConfig("StrictHostKeyChecking", "no"); ProxySOCKS5 proxyTunnel = new ProxySOCKS5(tunnelRemoteHost, 22); proxyTunnel.setUserPasswd(user, password); session.setProxy(proxyTunnel); session.connect(30000); Channel channel=session.openChannel("exec"); ((ChannelExec)channel).setCommand("hostname"); channel.setInputStream(null); ((ChannelExec)channel).setErrStream(System.err); InputStream in=channel.getInputStream(); BufferedReader ebr = new BufferedReader(new InputStreamReader(in)); channel.connect(); while (true) { byte[] tmpArray=new byte[1024]; while(in.available()>0){ int i=in.read(tmpArray, 0, 1024); if(i<0)break; outputBuffer.append(new String(tmpArray, 0, i)).append("\n"); } if(channel.isClosed()){ System.out.println("exit-status: "+channel.getExitStatus()); break; } } ebr.close(); channel.disconnect(); session.disconnect(); System.out.println(outputBuffer.toString()); } class localUserInfo implements UserInfo{ String passwd; public String getPassword(){ return passwd; } public boolean promptYesNo(String str){return true;} public String getPassphrase(){ return null; } public boolean promptPassphrase(String message){return true; } public boolean promptPassword(String message){return true;} public void showMessage(String message){} } }
The above code gives the below exception on the
session.connect(30000);
line.com.jcraft.jsch.JSchException: ProxySOCKS5: com.jcraft.jsch.JSchException: fail in SOCKS5 proxy at com.jcraft.jsch.ProxySOCKS5.connect(ProxySOCKS5.java:317) at com.jcraft.jsch.Session.connect(Session.java:231) at com.ukris.main.JschExecutor.go(JschExecutor.java:50) at com.ukris.main.JschExecutor.main(JschExecutor.java:19) Caused by: com.jcraft.jsch.JSchException: fail in SOCKS5 proxy at com.jcraft.jsch.ProxySOCKS5.connect(ProxySOCKS5.java:200) ... 3 more
-
Unni Kris about 9 yearsThanks Petesh.. This works like a charm. And thanks for clarifying on the
SOCKS
part. One query though,session.openChannel("direct-tcpip")
, do u have the list of all available channels for a JSch session? -
Anya Shenanigans about 9 yearsThese are listed in the openChannel documentation.
-
Arya over 6 years@Petesh if I understand correctly this pretty much does the thing thing the tunneling setting in Putty does? While it's running I can point Firefox's SOCKS proxy to port 2233 and it will use it as a SOCKS proxy?
-
Anya Shenanigans over 6 yearsYes, this does the same as the 'tunneling' setting in putty. In this case it produces a local port which mirrors a port on the remote end, allowing you to connect to it. If the remote port you are connecting to is a SOCKS proxy server's port, then you can point firefox's settings to this local port as a SOCKS proxy. It does not 'SOCKSify' the port in any way, it simply adds a local mirror to the remote port.
-
Anya Shenanigans almost 6 years@FedericoTaschin I don't know where you get the exception to try to debug this. The first connection establishes the tunnel from a local port to the remote system. If this 'went away' as soon as connected to, then this would cause the closed connection from the foreign host exception on the second call; but I'm just positing an approach. You could test this from the command line using command line ssh
ssh -L 2233:tunnelRemoteHost:22 host
, and thenssh -p 2233 localhost
and see where it falls over. -
Dwarrior about 5 years@Petesh I am able to understand your code after referring these links:1 and 2. 2 things to clarify: (1) 2nd URL says "though the agent uses a socket, it's a UNIX domain socket, which is accessible only from the local filesystem. The agents do not listen on any TCP/IP-based socket." How it relates to "direct-tcpip"that you have used in open channel.(2) any security implications of using this code?
-
Anya Shenanigans about 5 years@Dwarrior agent forwarding is something completely different and unrelated to the feature of tcp port forwarding, which is what this question is asking about. Security implications of this code? It uses embedded passwords, it creates port forwardings which can be taken advantage of on the relay box because TCP ports have no inherent access security. This code is simple, sample code and should not be used in a production environment.
-
Dwarrior about 5 years@Petesh - Thank you for clarifying. That's exactly what I was trying to understand. Assume your passwords are stored securely but are decrypted to actual password while getting session and one wants to use port forwarding in production, how should that be handled then?